(83 lines)
1 -- This file contains the parser, which converts text into abstract syntax trees2 lpeg = require 'lpeg'3 re = require 're'4 lpeg.setmaxstack 200005 {:P,:R,:S,:C,:Cmt,:Carg,:Cc} = lpeg7 -- foldr {A{a1,a2,...},B{b1,b2,...},C{c1,c2,...}} -> C{B{A{a1,a2,...},b1,b2...},c1,c2...}8 foldr = (...)->9 inner = select(1,...)10 for i=2,select('#',...) do11 assert inner.type12 outer = select(i,...)13 table.insert(outer, 1, inner)14 outer.source.start = inner.source.start15 inner = outer16 assert inner.type17 return inner19 DEFS = with {}20 -- Newline supports either windows-style CR+LF or unix-style LF21 .nl = P("\r")^-1 * P("\n")22 .tab = P("\t")23 .at_break = lpeg.B(lpeg.S(";,. \r\n\t({[")) + -lpeg.B(1)24 .tonumber = tonumber25 .tochar = string.char26 .unpack = unpack or table.unpack27 .nil = Cc(nil)28 .userdata = Carg(1)29 -- Always match and capture the indentation (spaces only) of the current line30 -- i.e. the leading space of the chunk of non-newline characters leading up to s[i]31 .indentation = lpeg.Cmt P(0),32 (s, i)->33 sub = string.sub34 while i > 1 and sub(s,i-1,i-1) != '\n' do i -= 135 return true, (s\match("^ *", i))36 .utf8_char = (37 R("\194\223")*R("\128\191") +38 R("\224\239")*R("\128\191")*R("\128\191") +39 R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))40 .operator_char = S("#'`~@^&*+=<>?/%!|\\-") + (P("\xE2") * (R("\x88\x8B")+R("\xA8\xAB")) * R("\128\191"))41 .Tree = (t, userdata)-> userdata.make_tree(t, userdata)42 .foldr = foldr44 setmetatable(DEFS, {__index:(key)=>45 if i = key\match("^ascii_(%d+)$")46 c = string.char(tonumber(i))47 self[key] = c48 return c49 elseif i = key\match("^number_(%d+)$")50 p = Cc(tonumber(i))51 self[key] = p52 return p53 })55 -- Just for cleanliness, I put the language spec in its own file using a slightly56 -- extended version of the lpeg.re syntax.57 peg_tidier = re.compile [[58 file <- %nl* {~ (captured_def/line) (%nl+ (captured_def/line))* %nl* ~}59 ident <- [a-zA-Z_][a-zA-Z0-9_]*60 line <- [^%nl]*61 captured_def <-62 ({ident} (" "*) "(" {ident} ")" (" "*) "<-" {[^%nl]* (%nl+ " "+ [^%nl]*)*}) ->63 "%1 <- ({| {:type:''->'%2':} {:start:{}:}64 (%3)65 {:stop:{}:} |} %%userdata) -> Tree"66 ]]68 make_parser = (peg, make_tree=nil)->69 peg = assert(peg_tidier\match(peg))70 peg = assert(re.compile(peg, DEFS))71 return (input, filename='???')->72 input = tostring(input)73 tree_mt = {__index: {source:input, filename:filename}}74 userdata = {75 make_tree: make_tree or ((t)->setmetatable(t, tree_mt))76 :filename, file:input77 }78 tree = peg\match(input, nil, userdata)79 if not tree or type(tree) == 'number'80 error "File #{filename} failed to parse:\n#{input}"81 return tree83 return make_parser