nomsu/parser.moon
Bruce Hill b6d3cbd61c Misc changes, including text indented interpolations are now indented
relative to the text, not the opening '("', code objects can now remove
all free vars, the REPL uses global vars. Error API is changing a bit.
2019-01-01 15:07:10 -08:00

82 lines
2.6 KiB
Plaintext

-- This file contains the parser, which converts text into abstract syntax trees
lpeg = require 'lpeg'
re = require 're'
lpeg.setmaxstack 20000
{:P,:R,:S,:C,:Cmt,:Carg,:Cc} = lpeg
-- foldr {A{a1,a2,...},B{b1,b2,...},C{c1,c2,...}} -> C{B{A{a1,a2,...},b1,b2...},c1,c2...}
foldr = (...)->
inner = select(1,...)
for i=2,select('#',...) do
assert inner.type
outer = select(i,...)
table.insert(outer, 1, inner)
inner.start = outer.start
inner = outer
assert inner.type
return inner
DEFS = with {}
-- Newline supports either windows-style CR+LF or unix-style LF
.nl = P("\r")^-1 * P("\n")
.tab = P("\t")
.tonumber = tonumber
.tochar = string.char
.unpack = unpack or table.unpack
.nil = Cc(nil)
.userdata = Carg(1)
-- Always match and capture the indentation (spaces only) of the current line
-- i.e. the leading space of the chunk of non-newline characters leading up to s[i]
.indentation = lpeg.Cmt P(0),
(s, i)->
sub = string.sub
while i > 1 and sub(s,i-1,i-1) != '\n' do i -= 1
return true, (s\match("^ *", i))
.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"))
.Tree = (t, userdata)-> userdata.make_tree(t, userdata)
.foldr = foldr
setmetatable(DEFS, {__index:(key)=>
if i = key\match("^ascii_(%d+)$")
c = string.char(tonumber(i))
self[key] = c
return c
elseif i = key\match("^number_(%d+)$")
p = Cc(tonumber(i))
self[key] = p
return p
})
-- Just for cleanliness, I put the language spec in its own file using a slightly
-- extended version of the lpeg.re syntax.
peg_tidier = re.compile [[
file <- %nl* {~ (captured_def/line) (%nl+ (captured_def/line))* %nl* ~}
ident <- [a-zA-Z_][a-zA-Z0-9_]*
line <- [^%nl]*
captured_def <-
({ident} (" "*) "(" {ident} ")" (" "*) "<-" {[^%nl]* (%nl+ " "+ [^%nl]*)*}) ->
"%1 <- ({| {:type:''->'%2':} {:start:{}:}
(%3)
{:stop:{}:} |} %%userdata) -> Tree"
]]
make_parser = (peg, make_tree=nil)->
peg = assert(peg_tidier\match(peg))
peg = assert(re.compile(peg, DEFS))
return (input, filename='???')->
input = tostring(input)
tree_mt = {__index: {source:input, filename:filename}}
userdata = {
make_tree: make_tree or ((t)->setmetatable(t, tree_mt))
:filename, source:input
}
tree = peg\match(input, nil, userdata)
if not tree
error "File #{filename} failed to parse:\n#{input}"
return tree
return make_parser