1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# -*- coding: utf-8 -*- #
# frozen_string_literal: true
module Rouge
module Lexers
load_lexer 'python.rb'
class Cython < Python
title "Cython"
desc "Cython and Pyrex source code (cython.org)"
tag 'cython'
aliases 'pyx', 'pyrex'
filenames '*.pyx', '*.pxd', '*.pxi'
mimetypes 'text/x-cython', 'application/x-cython'
def initialize(opts = {})
super opts
@indentation = nil
end
def self.keywords
@keywords ||= super + %w(
by except? fused gil nogil
)
end
def self.c_keywords
@ckeywords ||= %w(
public readonly extern api inline enum union
)
end
identifier = /[a-z_]\w*/i
dotted_identifier = /[a-z_.][\w.]*/i
prepend :root do
rule %r/cp?def|ctypedef/ do
token Keyword
push :c_definitions
push :c_start
end
rule %r/(from)((?:\\\s|\s)+)(#{dotted_identifier})((?:\\\s|\s)+)(cimport)/ do
groups Keyword::Namespace,
Text,
Name::Namespace,
Text,
Keyword::Namespace
end
rule %r/(cimport)(\s+)(#{dotted_identifier})/ do
groups Keyword::Namespace, Text, Name::Namespace
end
rule %r/(struct)((?:\\\s|\s)+)/ do
groups Keyword, Text
push :classname
end
mixin :func_call_fix
rule %r/[(,]/, Punctuation, :c_start
end
prepend :classname do
rule %r/(?:\\\s|\s)+/, Text
end
prepend :funcname do
rule %r/(?:\\\s|\s)+/, Text
end
# This is a fix for the way that function calls are lexed in the Python
# lexer. This should be moved to the Python lexer once confirmed that it
# does not cause any regressions.
state :func_call_fix do
rule %r/#{identifier}(?=\()/ do |m|
if self.class.keywords.include? m[0]
token Keyword
elsif self.class.exceptions.include? m[0]
token Name::Builtin
elsif self.class.builtins.include? m[0]
token Name::Builtin
elsif self.class.builtins_pseudo.include? m[0]
token Name::Builtin::Pseudo
else
token Name::Function
end
end
end
# The Cython lexer adds three states to those already in the Python lexer.
# Calls to `cdef`, `cpdef` and `ctypedef` move the lexer into the :c_start
# state. The primary purpose of this state is to highlight datatypes. Once
# this has been done, the lexer moves to the :c_definitions state where
# the majority of text in a definition is lexed. Finally, newlines cause
# the lexer to move to :c_indent. This state is used to check whether we
# have moved out of a C block.
state :c_start do
rule %r/[^\S\n]+/, Text
rule %r/cp?def|ctypedef/, Keyword
rule %r/(?:un)?signed/, Keyword::Type
# This rule matches identifiers that could be type declarations. The
# lookahead matches (1) pointers, (2) arrays and (3) variable names.
rule %r/#{identifier}(?=(?:\*+)|(?:[ \t]*\[)|(?:[ \t]+\w))/ do |m|
if self.class.keywords.include? m[0]
token Keyword
pop!
elsif %w(def).include? m[0]
token Keyword
goto :funcname
elsif %w(struct class).include? m[0]
token Keyword::Reserved
goto :classname
elsif self.class.c_keywords.include? m[0]
token Keyword::Reserved
else
token Keyword::Type
pop!
end
end
rule(//) { pop! }
end
state :c_definitions do
rule %r/\n/, Text, :c_indent
mixin :root
end
state :c_indent do
rule %r/[ \t]+/ do |m|
token Text
goto :c_start
if @indentation.nil?
@indentation = m[0]
elsif @indentation.length > m[0].length
@indentation = nil
pop! 2 # Pop :c_start and :c_definitions
end
end
rule(//) { @indentation = nil; reset_stack }
end
end
end
end
|