1
|
module CodeRay
|
2
|
module Scanners
|
3
|
class CSharp < Scanner
|
4
|
|
5
|
register_for :csharp
|
6
|
|
7
|
RESERVED_WORDS = %w(abstract as base break case catch checked class
|
8
|
const continue default delegate do else enum event explicit extern
|
9
|
finally fixed for foreach goto if implicit in interface internal is
|
10
|
lock namespace new operator out override params private protected
|
11
|
public readonly ref return sealed sizeof stackalloc static struct
|
12
|
switch this throw try typeof unchecked unsafe using virtual volatile
|
13
|
void while
|
14
|
add dynamic from get global group into join let orderby partial
|
15
|
remove select set value var where yield)
|
16
|
|
17
|
PREDEFINED_TYPES = %w(bool byte char double float int long short
|
18
|
decimal uint ulong ushort object sbyte string)
|
19
|
|
20
|
PREDEFINED_CONSTANTS = %w(true false null)
|
21
|
|
22
|
IDENT_KIND = WordList.new(:ident).
|
23
|
add(RESERVED_WORDS, :reserved).
|
24
|
add(PREDEFINED_TYPES, :pre_type).
|
25
|
add(PREDEFINED_CONSTANTS, :pre_constant)
|
26
|
|
27
|
ESCAPE = / [rbfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
|
28
|
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
|
29
|
|
30
|
def scan_tokens tokens, options
|
31
|
state = :initial
|
32
|
|
33
|
until eos?
|
34
|
kind = nil
|
35
|
match = nil
|
36
|
|
37
|
case state
|
38
|
when :initial
|
39
|
|
40
|
if scan(/ \s+ | \\\n /x)
|
41
|
kind = :space
|
42
|
|
43
|
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
|
44
|
kind = :comment
|
45
|
|
46
|
elsif match = scan(/ \# \s* if \s* 0 /x)
|
47
|
match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
|
48
|
kind = :comment
|
49
|
|
50
|
elsif scan(/ [-+*\/=<>?:;,!&^|()\[\]{}~%]+ | \.(?!\d) /x)
|
51
|
kind = :operator
|
52
|
|
53
|
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
|
54
|
kind = IDENT_KIND[match]
|
55
|
if kind == :ident and check(/:(?!:)/)
|
56
|
match << scan(/:/)
|
57
|
kind = :label
|
58
|
end
|
59
|
|
60
|
elsif match = scan(/"/)
|
61
|
tokens << [:open, :string]
|
62
|
state = :string
|
63
|
kind = :delimiter
|
64
|
|
65
|
elsif match = scan(/@"/)
|
66
|
tokens << [:open, :string]
|
67
|
state = :stringat
|
68
|
kind = :delimiter
|
69
|
|
70
|
elsif scan(/#\s*(\w*)/)
|
71
|
kind = :preprocessor
|
72
|
|
73
|
elsif scan(/ ' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
|
74
|
kind = :char
|
75
|
|
76
|
elsif scan(/0[xX][0-9A-Fa-f]+/)
|
77
|
kind = :hex
|
78
|
|
79
|
elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
|
80
|
kind = :oct
|
81
|
|
82
|
elsif scan(/(?:\d+)(?![.eEfFdDmML])/)
|
83
|
kind = :integer
|
84
|
|
85
|
elsif scan(/\d[fFdDmM]?|\d*\.\d+(?:[eE][+-]?\d+)?[fFdDmM]?|\d+[eE][+-]?\d+[fFdDmM]?/)
|
86
|
kind = :float
|
87
|
|
88
|
else
|
89
|
getch
|
90
|
kind = :error
|
91
|
|
92
|
end
|
93
|
|
94
|
when :string
|
95
|
if scan(/[^\\\n"]+/)
|
96
|
kind = :content
|
97
|
elsif scan(/"/)
|
98
|
tokens << ['"', :delimiter]
|
99
|
tokens << [:close, :string]
|
100
|
state = :initial
|
101
|
next
|
102
|
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
|
103
|
kind = :char
|
104
|
elsif scan(/ \\ | $ /x)
|
105
|
tokens << [:close, :string]
|
106
|
kind = :error
|
107
|
state = :initial
|
108
|
else
|
109
|
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
|
110
|
end
|
111
|
|
112
|
when :stringat
|
113
|
if scan(/[^"]+/)
|
114
|
kind = :content
|
115
|
elsif scan(/""/)
|
116
|
kind = :char
|
117
|
elsif scan(/"/)
|
118
|
tokens << ['"', :delimiter]
|
119
|
tokens << [:close, :string]
|
120
|
state = :initial
|
121
|
next
|
122
|
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
|
123
|
kind = :char
|
124
|
elsif scan(/ $ /x)
|
125
|
tokens << [:close, :string]
|
126
|
kind = :error
|
127
|
state = :initial
|
128
|
else
|
129
|
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
|
130
|
end
|
131
|
|
132
|
else
|
133
|
raise_inspect 'Unknown state', tokens
|
134
|
|
135
|
end
|
136
|
|
137
|
match ||= matched
|
138
|
if $DEBUG and not kind
|
139
|
raise_inspect 'Error token %p in line %d' %
|
140
|
[[match, kind], line], tokens
|
141
|
end
|
142
|
raise_inspect 'Empty token', tokens unless match
|
143
|
|
144
|
tokens << [match, kind]
|
145
|
|
146
|
end
|
147
|
|
148
|
if state == :string
|
149
|
tokens << [:close, :string]
|
150
|
end
|
151
|
|
152
|
tokens
|
153
|
end
|
154
|
end
|
155
|
end
|
156
|
end
|