code / nomsu

Lines6.6K Lua5.1K PEG1.3K make117
2 others 83
Markdown60 Bourne Again Shell23
(83 lines)
1 -- This file contains the parser, which converts text into abstract syntax trees
2 lpeg = require 'lpeg'
3 re = require 're'
4 lpeg.setmaxstack 20000
5 {:P,:R,:S,:C,:Cmt,:Carg,:Cc} = lpeg
7 -- 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('#',...) do
11 assert inner.type
12 outer = select(i,...)
13 table.insert(outer, 1, inner)
14 outer.source.start = inner.source.start
15 inner = outer
16 assert inner.type
17 return inner
19 DEFS = with {}
20 -- Newline supports either windows-style CR+LF or unix-style LF
21 .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 = tonumber
25 .tochar = string.char
26 .unpack = unpack or table.unpack
27 .nil = Cc(nil)
28 .userdata = Carg(1)
29 -- Always match and capture the indentation (spaces only) of the current line
30 -- 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.sub
34 while i > 1 and sub(s,i-1,i-1) != '\n' do i -= 1
35 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 = foldr
44 setmetatable(DEFS, {__index:(key)=>
45 if i = key\match("^ascii_(%d+)$")
46 c = string.char(tonumber(i))
47 self[key] = c
48 return c
49 elseif i = key\match("^number_(%d+)$")
50 p = Cc(tonumber(i))
51 self[key] = p
52 return p
53 })
55 -- Just for cleanliness, I put the language spec in its own file using a slightly
56 -- 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:input
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 tree
83 return make_parser