@@ -11,6 +11,9 @@ local PARSE = {}
11
11
local LEX = require ' metalua.compiler.parser.lexer'
12
12
local LEXG = require ' metalua.grammar.lexer'
13
13
14
+ local function warn (message , position )
15
+ io.stderr :write (' WARNING: ' , tostring (position ), ' : ' , message , ' \n ' )
16
+ end
14
17
15
18
--[[
16
19
Loose parser.
@@ -28,8 +31,8 @@ local LEXG = require 'metalua.grammar.lexer'
28
31
inside following block. Used for control variables in 'for' statements.
29
32
'Id', name, lineinfo - reference to variable.
30
33
'String', name - string or table field
31
- 'Scope' - beginning of scope block
32
- 'Endscope' - end of scope block
34
+ 'Scope' - beginning of scope block [TODO: pass lineinfo like Endscope does?]
35
+ 'Endscope', nil, lineinfo - end of scope block
33
36
--]]
34
37
function PARSE .parse_scope (lx , f )
35
38
local cprev = {tag = ' Eof' }
@@ -41,9 +44,9 @@ function PARSE.parse_scope(lx, f)
41
44
scopes [# scopes + 1 ] = {}
42
45
f (' Scope' )
43
46
end
44
- local function scope_end ()
47
+ local function scope_end (lineinfo )
45
48
table.remove (scopes )
46
- f (' Endscope' )
49
+ f (' Endscope' , nil , lineinfo )
47
50
end
48
51
49
52
local function parse_function_list (has_self )
@@ -81,7 +84,7 @@ function PARSE.parse_scope(lx, f)
81
84
cprev [1 ] == ' nil' ) or
82
85
cprev .tag == ' Number' or cprev .tag == ' String' )
83
86
then
84
- if scopes [# scopes ].inside_until then scope_end () end
87
+ if scopes [# scopes ].inside_until then scope_end (c . lineinfo ) end
85
88
f (' Statement' )
86
89
end
87
90
@@ -132,9 +135,9 @@ function PARSE.parse_scope(lx, f)
132
135
scope_begin ()
133
136
-- note: do/while/for statement scopes all begin at 'do'.
134
137
elseif c [1 ] == ' end' or c [1 ] == ' elseif' then
135
- scope_end ()
138
+ scope_end (c . lineinfo )
136
139
elseif c [1 ] == ' else' then
137
- scope_end ()
140
+ scope_end (c . lineinfo )
138
141
scope_begin ()
139
142
elseif c [1 ] == ' until' then
140
143
scopes [# scopes ].inside_until = true
@@ -195,7 +198,12 @@ function PARSE.parse_scope_resolve(lx, f)
195
198
elseif op == ' Scope' then
196
199
vars = newscope (vars )
197
200
elseif op == ' Endscope' then
198
- vars = getmetatable (vars ).__index
201
+ local mt = getmetatable (vars )
202
+ if mt == nil then
203
+ warn (" 'end' without opening block." , lineinfo .first )
204
+ else
205
+ vars = mt .__index
206
+ end
199
207
elseif op == ' Id' then
200
208
if vars [name ] then other = ' local' else other = ' global' end
201
209
elseif op == ' String' then
245
253
f(s) - call back function to send chunks of Lua code output to. Example: io.stdout.
246
254
--]]
247
255
function PARSE .replace_env (code , f )
256
+ if not f then return PARSE .accumulate (PARSE .replace_env , code ) end
248
257
PARSE .extract_vars (code , function (op , name , other )
249
258
if op == ' Id' then
250
259
f (other == ' global' and ' _ENV.' .. name or name )
@@ -254,4 +263,22 @@ function PARSE.replace_env(code, f)
254
263
end )
255
264
end
256
265
266
+ -- helper function. Can be passed as argument `f` to functions
267
+ -- like `replace_env` above to accumulate fragments into a single string.
268
+ function PARSE .accumulator ()
269
+ local ts = {}
270
+ local mt = {}
271
+ mt .__index = mt
272
+ function mt :__call (s ) ts [# ts + 1 ] = s end
273
+ function mt :result () return table.concat (ts ) end
274
+ return setmetatable ({}, mt )
275
+ end
276
+
277
+ -- helper function
278
+ function PARSE .accumulate (g , code )
279
+ local accum = PARSE .accumulator ()
280
+ g (code , accum )
281
+ return accum :result ()
282
+ end
283
+
257
284
return PARSE
0 commit comments