Skip to content

Commit 8b78006

Browse files
author
Damien Radtke
committed
Added initial Go scanner.
1 parent ab1bb26 commit 8b78006

File tree

1 file changed

+190
-0
lines changed
  • lib/coderay/scanners

1 file changed

+190
-0
lines changed

lib/coderay/scanners/go.rb

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
module CodeRay
2+
module Scanners
3+
4+
# Scanner for Go.
5+
# http://golang.org/ref/spec
6+
class Go < Scanner
7+
8+
register_for :go
9+
file_extension 'go'
10+
11+
KEYWORDS = [
12+
'break', 'case', 'chan', 'const', 'continue', 'default',
13+
'defer', 'else', 'fallthrough', 'for', 'func', 'go',
14+
'goto', 'if', 'import', 'interface', 'map', 'package',
15+
'range', 'return', 'select', 'struct', 'switch',
16+
'type', 'var'
17+
] # :nodoc:
18+
19+
PREDEFINED_TYPES = [
20+
'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16',
21+
'int32', 'int64', 'float32', 'float64', 'complex64',
22+
'complex128', 'byte', 'rune', 'uint', 'int', 'uintptr',
23+
'string'
24+
] # :nodoc:
25+
26+
PREDEFINED_CONSTANTS = [
27+
'nil', 'true', 'false'
28+
] # :nodoc:
29+
30+
DIRECTIVES = [
31+
# none in Go
32+
] # :nodoc:
33+
34+
IDENT_KIND = WordList.new(:ident).
35+
add(KEYWORDS, :keyword).
36+
add(PREDEFINED_TYPES, :predefined_type).
37+
add(DIRECTIVES, :directive).
38+
add(PREDEFINED_CONSTANTS, :predefined_constant) # :nodoc:
39+
40+
ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc:
41+
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc:
42+
43+
protected
44+
45+
def scan_tokens encoder, options
46+
47+
state = :initial
48+
label_expected = true
49+
case_expected = false
50+
label_expected_before_preproc_line = nil
51+
in_preproc_line = false
52+
53+
until eos?
54+
55+
case state
56+
57+
when :initial
58+
59+
if match = scan(/ \s+ | \\\n /x)
60+
if in_preproc_line && match != "\\\n" && match.index(?\n)
61+
in_preproc_line = false
62+
label_expected = label_expected_before_preproc_line
63+
end
64+
encoder.text_token match, :space
65+
66+
elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
67+
encoder.text_token match, :comment
68+
69+
elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x)
70+
label_expected = match =~ /[;\{\}]/
71+
if case_expected
72+
label_expected = true if match == ':'
73+
case_expected = false
74+
end
75+
encoder.text_token match, :operator
76+
77+
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
78+
kind = IDENT_KIND[match]
79+
if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/)
80+
kind = :label
81+
match << matched
82+
else
83+
label_expected = false
84+
if kind == :keyword
85+
case match
86+
when 'case', 'default'
87+
case_expected = true
88+
end
89+
end
90+
end
91+
encoder.text_token match, kind
92+
93+
elsif match = scan(/L?"/)
94+
encoder.begin_group :string
95+
if match[0] == ?L
96+
encoder.text_token 'L', :modifier
97+
match = '"'
98+
end
99+
encoder.text_token match, :delimiter
100+
state = :string
101+
102+
elsif match = scan(/ \# \s* if \s* 0 /x)
103+
match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
104+
encoder.text_token match, :comment
105+
106+
elsif match = scan(/#[ \t]*(\w*)/)
107+
encoder.text_token match, :preprocessor
108+
in_preproc_line = true
109+
label_expected_before_preproc_line = label_expected
110+
state = :include_expected if self[1] == 'include'
111+
112+
elsif match = scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
113+
label_expected = false
114+
encoder.text_token match, :char
115+
116+
elsif match = scan(/\$/)
117+
encoder.text_token match, :ident
118+
119+
elsif match = scan(/0[xX][0-9A-Fa-f]+/)
120+
label_expected = false
121+
encoder.text_token match, :hex
122+
123+
elsif match = scan(/(?:0[0-7]+)(?![89.eEfF])/)
124+
label_expected = false
125+
encoder.text_token match, :octal
126+
127+
elsif match = scan(/(?:\d+)(?![.eEfF])L?L?/)
128+
label_expected = false
129+
encoder.text_token match, :integer
130+
131+
elsif match = scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
132+
label_expected = false
133+
encoder.text_token match, :float
134+
135+
else
136+
encoder.text_token getch, :error
137+
138+
end
139+
140+
when :string
141+
if match = scan(/[^\\\n"]+/)
142+
encoder.text_token match, :content
143+
elsif match = scan(/"/)
144+
encoder.text_token match, :delimiter
145+
encoder.end_group :string
146+
state = :initial
147+
label_expected = false
148+
elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
149+
encoder.text_token match, :char
150+
elsif match = scan(/ \\ | $ /x)
151+
encoder.end_group :string
152+
encoder.text_token match, :error unless match.empty?
153+
state = :initial
154+
label_expected = false
155+
else
156+
raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
157+
end
158+
159+
when :include_expected
160+
if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
161+
encoder.text_token match, :include
162+
state = :initial
163+
164+
elsif match = scan(/\s+/)
165+
encoder.text_token match, :space
166+
state = :initial if match.index ?\n
167+
168+
else
169+
state = :initial
170+
171+
end
172+
173+
else
174+
raise_inspect 'Unknown state', encoder
175+
176+
end
177+
178+
end
179+
180+
if state == :string
181+
encoder.end_group :string
182+
end
183+
184+
encoder
185+
end
186+
187+
end
188+
189+
end
190+
end

0 commit comments

Comments
 (0)