|
1 |
module CodeRay
|
|
2 |
module Scanners
|
|
3 |
|
|
4 |
class Lua < Scanner
|
|
5 |
|
|
6 |
register_for :lua
|
|
7 |
|
|
8 |
include Streamable
|
|
9 |
|
|
10 |
RESERVED_WORDS = [
|
|
11 |
'if', 'elseif', 'else', 'then',
|
|
12 |
'end', 'do', 'while', 'true',
|
|
13 |
'false', 'in', 'for', 'and', 'or',
|
|
14 |
'function', 'local', 'not', 'repeat',
|
|
15 |
'return', 'until', 'break',
|
|
16 |
]
|
|
17 |
|
|
18 |
PREDEFINED_TYPES = [
|
|
19 |
'nil', 'boolean', 'number', 'string', 'table',
|
|
20 |
]
|
|
21 |
|
|
22 |
BUILTIN_LIBS = [
|
|
23 |
'package', 'table', 'math', 'string', 'io', 'os', 'debug',
|
|
24 |
]
|
|
25 |
|
|
26 |
BUILTIN_METHODS = [
|
|
27 |
'loadlib', 'path', 'cpath',
|
|
28 |
'loaded','preloaded','seeall',
|
|
29 |
'coroutine', 'create','resume','yield',
|
|
30 |
'status','running','wrap',
|
|
31 |
'insert','remove','maxn','sort',
|
|
32 |
'concat','abs','mod','floor','ceil',
|
|
33 |
'min','max','sqrt','math.pow','math.log',
|
|
34 |
'exp','log10','deg','math.pi','math.rad',
|
|
35 |
'sin','cos','tan','asin','acos',
|
|
36 |
'atan','atan2','frexp','ldexp','random',
|
|
37 |
'randomseed', 'len','sub','rep','upper',
|
|
38 |
'lower','byte','char','dump','format',
|
|
39 |
'find','gmatch','gsub','match','open',
|
|
40 |
'input','output','close','read','lines',
|
|
41 |
'write','flush','stdout','stdin','stderr',
|
|
42 |
'popen','type','tmpfile','execute','exit',
|
|
43 |
'getenv','setlocale','remove','rename','tmpname',
|
|
44 |
'clock','time','date','difftime','debug',
|
|
45 |
'getinfo','getlocal','getupvalue','traceback',
|
|
46 |
'setlocal','setupvalue','sethook','gethook',
|
|
47 |
]
|
|
48 |
|
|
49 |
BUILTIN_FUNCTIONS = [
|
|
50 |
'print', 'pairs','ipairs', 'error', 'load',
|
|
51 |
'require', 'getfenv', 'setfenv', 'dofile',
|
|
52 |
'loadfile', 'loadstring', 'pcall', 'xpcall',
|
|
53 |
'assert', 'type', 'tostring', 'tonumber',
|
|
54 |
'select', 'upack', 'next', 'collectgarbage',
|
|
55 |
'module',
|
|
56 |
]
|
|
57 |
|
|
58 |
IDENT_KIND = WordList.new(:ident).
|
|
59 |
add(RESERVED_WORDS, :reserved).
|
|
60 |
add(PREDEFINED_TYPES, :pre_type).
|
|
61 |
add(BUILTIN_LIBS, :predefined).
|
|
62 |
add(BUILTIN_METHODS, :pre_type).
|
|
63 |
add(BUILTIN_FUNCTIONS, :preprocessor)
|
|
64 |
|
|
65 |
ESCAPE = / [rbfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
|
|
66 |
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
|
|
67 |
|
|
68 |
def scan_tokens tokens, options
|
|
69 |
|
|
70 |
state = :initial
|
|
71 |
|
|
72 |
until eos?
|
|
73 |
|
|
74 |
kind = nil
|
|
75 |
match = nil
|
|
76 |
|
|
77 |
case state
|
|
78 |
|
|
79 |
when :initial
|
|
80 |
|
|
81 |
if scan(/ \s+ | \\\n /x)
|
|
82 |
kind = :space
|
|
83 |
|
|
84 |
elsif scan(%r! --[^\n\\]* (?: \\. [^\n\\]* )* | --\[\[ (?: .*? \]\] | .* ) !mx)
|
|
85 |
kind = :comment
|
|
86 |
|
|
87 |
elsif scan(/ [-+*\/=<>?:;,!&^|()\[\]{}~%]+ | \.(?!\d) /x)
|
|
88 |
kind = :operator
|
|
89 |
|
|
90 |
elsif match = scan(/ [#A-Za-z_][A-Za-z_0-9]* /x)
|
|
91 |
kind = IDENT_KIND[match]
|
|
92 |
if kind == :pre_type and check(/[^\.\:\(\']/)
|
|
93 |
kind = :ident
|
|
94 |
end
|
|
95 |
|
|
96 |
elsif match = scan(/L?"/)
|
|
97 |
tokens << [:open, :string]
|
|
98 |
if match[0] == ?L
|
|
99 |
tokens << ['L', :modifier]
|
|
100 |
match = '"'
|
|
101 |
end
|
|
102 |
state = :string
|
|
103 |
kind = :string
|
|
104 |
|
|
105 |
elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
|
|
106 |
kind = :char
|
|
107 |
|
|
108 |
elsif scan(/0[xX][0-9A-Fa-f]+/)
|
|
109 |
kind = :hex
|
|
110 |
|
|
111 |
elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
|
|
112 |
kind = :oct
|
|
113 |
|
|
114 |
elsif scan(/(?:\d+)(?![.eEfF])/)
|
|
115 |
kind = :integer
|
|
116 |
|
|
117 |
elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
|
|
118 |
kind = :float
|
|
119 |
|
|
120 |
else
|
|
121 |
getch
|
|
122 |
kind = :error
|
|
123 |
|
|
124 |
end
|
|
125 |
|
|
126 |
when :string
|
|
127 |
if scan(/[^\\\n"]+/)
|
|
128 |
kind = :content
|
|
129 |
elsif scan(/"/)
|
|
130 |
tokens << ['"', :string]
|
|
131 |
tokens << [:close, :string]
|
|
132 |
state = :initial
|
|
133 |
next
|
|
134 |
elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
|
|
135 |
kind = :char
|
|
136 |
elsif scan(/ \\ | $ /x)
|
|
137 |
tokens << [:close, :string]
|
|
138 |
kind = :error
|
|
139 |
state = :initial
|
|
140 |
else
|
|
141 |
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
|
|
142 |
end
|
|
143 |
|
|
144 |
when :include_expected
|
|
145 |
if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
|
|
146 |
kind = :include
|
|
147 |
state = :initial
|
|
148 |
|
|
149 |
elsif match = scan(/\s+/)
|
|
150 |
kind = :space
|
|
151 |
state = :initial if match.index ?\n
|
|
152 |
|
|
153 |
else
|
|
154 |
getch
|
|
155 |
kind = :error
|
|
156 |
|
|
157 |
end
|
|
158 |
|
|
159 |
else
|
|
160 |
raise_inspect 'Unknown state', tokens
|
|
161 |
|
|
162 |
end
|
|
163 |
|
|
164 |
match ||= matched
|
|
165 |
if $DEBUG and not kind
|
|
166 |
raise_inspect 'Error token %p in line %d' %
|
|
167 |
[[match, kind], line], tokens
|
|
168 |
end
|
|
169 |
raise_inspect 'Empty token', tokens unless match
|
|
170 |
|
|
171 |
tokens << [match, kind]
|
|
172 |
|
|
173 |
end
|
|
174 |
|
|
175 |
if state == :string
|
|
176 |
tokens << [:close, :string]
|
|
177 |
end
|
|
178 |
|
|
179 |
tokens
|
|
180 |
end
|
|
181 |
|
|
182 |
end
|
|
183 |
|
|
184 |
end
|
|
185 |
end
|