diff options
Diffstat (limited to 'parser.lua')
| -rw-r--r-- | parser.lua | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/parser.lua b/parser.lua new file mode 100644 index 0000000..d48c435 --- /dev/null +++ b/parser.lua @@ -0,0 +1,164 @@ +local lpeg = require('lpeg') +local re = require('re') +lpeg.setmaxstack(10000) +local P, R, V, S, Cg, C, Cp, B, Cmt, Carg +P, R, V, S, Cg, C, Cp, B, Cmt, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt, lpeg.Carg +local utils = require('utils') +local match, sub, rep, gsub, format, byte, find +do + local _obj_0 = string + match, sub, rep, gsub, format, byte, match, find = _obj_0.match, _obj_0.sub, _obj_0.rep, _obj_0.gsub, _obj_0.format, _obj_0.byte, _obj_0.match, _obj_0.find +end +local AST = require("nomsu_tree") +local NOMSU_DEFS +do + local _with_0 = { } + _with_0.nl = P("\r") ^ -1 * P("\n") + _with_0.ws = S(" \t") + _with_0.tonumber = tonumber + local string_escapes = { + n = "\n", + t = "\t", + b = "\b", + a = "\a", + v = "\v", + f = "\f", + r = "\r" + } + local digit, hex = R('09'), R('09', 'af', 'AF') + _with_0.escaped_char = (P("\\") * S("xX") * C(hex * hex)) / function(self) + return string.char(tonumber(self, 16)) + end + _with_0.escaped_char = _with_0.escaped_char + ((P("\\") * C(digit * (digit ^ -2))) / function(self) + return string.char(tonumber(self)) + end) + _with_0.escaped_char = _with_0.escaped_char + ((P("\\") * C(S("ntbavfr"))) / string_escapes) + _with_0.operator_char = S("'`~!@$^&*-+=|<>?/") + _with_0.utf8_char = (R("\194\223") * R("\128\191") + R("\224\239") * R("\128\191") * R("\128\191") + R("\240\244") * R("\128\191") * R("\128\191") * R("\128\191")) + _with_0.ident_char = R("az", "AZ", "09") + P("_") + _with_0.utf8_char + _with_0.indent = Cmt(Carg(1), function(self, start, userdata) + local indented = userdata.indent .. ' ' + if sub(self, start, start + #indented - 1) == indented then + userdata.indent = indented + return start + #indented + end + end) + _with_0.dedent = Cmt(Carg(1), function(self, start, userdata) + local dedented = sub(userdata.indent, 1, -5) + if #match(self, "^[ ]*", start) <= #dedented then + userdata.indent = dedented + return start + end + end) + _with_0.nodent = Cmt(Carg(1), function(self, start, userdata) + if sub(self, start, start + #userdata.indent - 1) == userdata.indent then + return start + #userdata.indent + end + end) + _with_0.userdata = Carg(1) + _with_0.error = function(src, end_pos, start_pos, err_msg, userdata) + local seen_errors = userdata.errors + if seen_errors[start_pos] then + return true + end + if utils.size(seen_errors) >= 10 then + seen_errors[start_pos + 1] = colored.bright(colored.yellow(colored.onred("Too many errors, canceling parsing..."))) + return #src + 1 + end + local err_pos = start_pos + local line_no = pos_to_line(src, err_pos) + src = FILE_CACHE[userdata.source.filename] + local line_starts = LINE_STARTS[src] + local prev_line = line_no == 1 and "" or src:sub(line_starts[line_no - 1] or 1, line_starts[line_no] - 2) + local err_line = src:sub(line_starts[line_no], (line_starts[line_no + 1] or 0) - 2) + local next_line = src:sub(line_starts[line_no + 1] or -1, (line_starts[line_no + 2] or 0) - 2) + local i = err_pos - line_starts[line_no] + local pointer = ("-"):rep(i) .. "^" + err_msg = colored.bright(colored.yellow(colored.onred((err_msg or "Parse error") .. " at " .. tostring(userdata.source.filename) .. ":" .. tostring(line_no) .. ":"))) + if #prev_line > 0 then + err_msg = err_msg .. ("\n" .. colored.dim(prev_line)) + end + err_line = colored.white(err_line:sub(1, i)) .. colored.bright(colored.red(err_line:sub(i + 1, i + 1))) .. colored.dim(err_line:sub(i + 2, -1)) + err_msg = err_msg .. "\n" .. tostring(err_line) .. "\n" .. tostring(colored.red(pointer)) + if #next_line > 0 then + err_msg = err_msg .. ("\n" .. colored.dim(next_line)) + end + seen_errors[start_pos] = err_msg + return true + end + NOMSU_DEFS = _with_0 +end +setmetatable(NOMSU_DEFS, { + __index = function(self, key) + local make_node + make_node = function(start, value, stop, userdata) + if userdata.source then + do + local _with_0 = userdata.source + value.source = Source(_with_0.filename, _with_0.start + start - 1, _with_0.start + stop - 1) + end + end + setmetatable(value, AST[key]) + if value.__init then + value:__init() + end + for i = 1, #value do + assert(value[i]) + end + return value + end + self[key] = make_node + return make_node + end +}) +local NOMSU_PATTERN +do + local peg_tidier = re.compile([[ file <- {~ %nl* (def/comment) (%nl+ (def/comment))* %nl* ~} + def <- anon_def / captured_def + anon_def <- ({ident} (" "*) ":" + {((%nl " "+ [^%nl]*)+) / ([^%nl]*)}) -> "%1 <- %2" + captured_def <- ({ident} (" "*) "(" {ident} ")" (" "*) ":" + {((%nl " "+ [^%nl]*)+) / ([^%nl]*)}) -> "%1 <- (({} %3 {} %%userdata) -> %2)" + ident <- [a-zA-Z_][a-zA-Z0-9_]* + comment <- "--" [^%nl]* + ]]) + local nomsu_peg = peg_tidier:match(FILE_CACHE["nomsu.peg"]) + NOMSU_PATTERN = re.compile(nomsu_peg, NOMSU_DEFS) +end +local parse +parse = function(nomsu_code, source) + if source == nil then + source = nil + end + nomsu_code = tostring(nomsu_code) + local userdata = { + indent = "", + errors = { }, + source = source + } + local tree = NOMSU_PATTERN:match(nomsu_code, nil, userdata) + if not (tree) then + error("In file " .. tostring(colored.blue(tostring(source or "<unknown>"))) .. " failed to parse:\n" .. tostring(colored.onyellow(colored.black(nomsu_code)))) + end + if type(tree) == 'number' then + tree = nil + end + if next(userdata.errors) then + local keys = utils.keys(userdata.errors) + table.sort(keys) + local errors + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #keys do + local k = keys[_index_0] + _accum_0[_len_0] = userdata.errors[k] + _len_0 = _len_0 + 1 + end + errors = _accum_0 + end + error(table.concat(errors, "\n\n"), 0) + end + return tree +end +return parse |
