Skip to content

Commit a544d89

Browse files
committed
parser: warnings and accumulator helper function
1 parent 4cce76a commit a544d89

File tree

1 file changed

+35
-8
lines changed

1 file changed

+35
-8
lines changed

lua_parser_loose.lua

+35-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ local PARSE = {}
1111
local LEX = require 'metalua.compiler.parser.lexer'
1212
local LEXG = require 'metalua.grammar.lexer'
1313

14+
local function warn(message, position)
15+
io.stderr:write('WARNING: ', tostring(position), ': ', message, '\n')
16+
end
1417

1518
--[[
1619
Loose parser.
@@ -28,8 +31,8 @@ local LEXG = require 'metalua.grammar.lexer'
2831
inside following block. Used for control variables in 'for' statements.
2932
'Id', name, lineinfo - reference to variable.
3033
'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
3336
--]]
3437
function PARSE.parse_scope(lx, f)
3538
local cprev = {tag='Eof'}
@@ -41,9 +44,9 @@ function PARSE.parse_scope(lx, f)
4144
scopes[#scopes+1] = {}
4245
f('Scope')
4346
end
44-
local function scope_end()
47+
local function scope_end(lineinfo)
4548
table.remove(scopes)
46-
f('Endscope')
49+
f('Endscope', nil, lineinfo)
4750
end
4851

4952
local function parse_function_list(has_self)
@@ -81,7 +84,7 @@ function PARSE.parse_scope(lx, f)
8184
cprev[1] == 'nil') or
8285
cprev.tag == 'Number' or cprev.tag == 'String')
8386
then
84-
if scopes[#scopes].inside_until then scope_end() end
87+
if scopes[#scopes].inside_until then scope_end(c.lineinfo) end
8588
f('Statement')
8689
end
8790

@@ -132,9 +135,9 @@ function PARSE.parse_scope(lx, f)
132135
scope_begin()
133136
-- note: do/while/for statement scopes all begin at 'do'.
134137
elseif c[1] == 'end' or c[1] == 'elseif' then
135-
scope_end()
138+
scope_end(c.lineinfo)
136139
elseif c[1] == 'else' then
137-
scope_end()
140+
scope_end(c.lineinfo)
138141
scope_begin()
139142
elseif c[1] == 'until' then
140143
scopes[#scopes].inside_until = true
@@ -195,7 +198,12 @@ function PARSE.parse_scope_resolve(lx, f)
195198
elseif op == 'Scope' then
196199
vars = newscope(vars)
197200
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
199207
elseif op == 'Id' then
200208
if vars[name] then other = 'local' else other = 'global' end
201209
elseif op == 'String' then
@@ -245,6 +253,7 @@ end
245253
f(s) - call back function to send chunks of Lua code output to. Example: io.stdout.
246254
--]]
247255
function PARSE.replace_env(code, f)
256+
if not f then return PARSE.accumulate(PARSE.replace_env, code) end
248257
PARSE.extract_vars(code, function(op, name, other)
249258
if op == 'Id' then
250259
f(other == 'global' and '_ENV.' .. name or name)
@@ -254,4 +263,22 @@ function PARSE.replace_env(code, f)
254263
end)
255264
end
256265

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+
257284
return PARSE

0 commit comments

Comments
 (0)