The goal of stage 06 is to try parse zig synax in lua. I pulled in lpeglable 1.2.0 and parser-gen off github to get started. All of this needs to be cleaned up rather soon. Lua boostraps using tcc and musl from the previous stage. Since musl 0.6.0 doesn't support dynamic linking this build of lua doesn't support shared libraries. I couldn't easily patch musl with dlopen and friends so instead I link statically and call deps with c api.
260 lines
No EOL
4.5 KiB
Lua
260 lines
No EOL
4.5 KiB
Lua
local peg = require "peg-parser"
|
|
local f = peg.pegToAST
|
|
|
|
local eq = require "equals"
|
|
local equals = eq.equals
|
|
|
|
|
|
-- self-description of peg-parser:
|
|
|
|
--assert(f(peg.gram))
|
|
|
|
-- ( p ) grouping
|
|
e = f("('a')")
|
|
res = {t="a"}
|
|
|
|
assert(equals(e,res))
|
|
|
|
-- 'string' literal string
|
|
|
|
e = f("'string'")
|
|
res = {t="string"}
|
|
assert(equals(e,res))
|
|
|
|
-- "string" literal string
|
|
e = f('"string"')
|
|
res = {t="string"}
|
|
|
|
assert(equals(e,res))
|
|
--[class] character class
|
|
e = f("[^a-zA-Z01]")
|
|
res = {
|
|
action = "invert",
|
|
op1 = {
|
|
action = "or",
|
|
op1 = {
|
|
action = "or",
|
|
op1 = {
|
|
action = "or",
|
|
op1 = {
|
|
action = "range",
|
|
op1 = {
|
|
s = "az"
|
|
}
|
|
},
|
|
op2 = {
|
|
action = "range",
|
|
op1 = {
|
|
s = "AZ"
|
|
}
|
|
}
|
|
},
|
|
op2 = {
|
|
t = "0"
|
|
}
|
|
},
|
|
op2 = {
|
|
t = "1"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
assert(equals(e,res))
|
|
|
|
--. any character
|
|
e = f(".")
|
|
res = {action="anychar"}
|
|
|
|
assert(equals(e,res))
|
|
--%name pattern defs[name] or a pre-defined pattern
|
|
e = f("%name")
|
|
res = {action="%", op1={s="name"}}
|
|
|
|
assert(equals(e,res))
|
|
--name non terminal
|
|
e = f("name")
|
|
res = {nt="name"}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--<name> non terminal
|
|
e = f("<name>")
|
|
res = {nt="name"}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--{} position capture
|
|
e = f("{}")
|
|
|
|
res = {action="poscap"}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--{ p } simple capture
|
|
e = f("{name}")
|
|
res = {action="scap", op1= {nt="name"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--{: p :} anonymous group capture
|
|
e = f("{:name:}")
|
|
res = {action="gcap", op1= {nt="name"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--{:name: p :} named group capture
|
|
e = f("{:g: name:}")
|
|
res = {action="gcap", op1= {nt="name"} , op2={s="g"}}
|
|
|
|
assert(equals(e,res))
|
|
--{~ p ~} substitution capture
|
|
e = f("{~ name ~}")
|
|
|
|
res = {action="subcap", op1= {nt="name"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--{| p |} table capture
|
|
e = f("{| name |}")
|
|
res = {action="tcap", op1= {nt="name"}}
|
|
assert(equals(e,res))
|
|
|
|
--=name back reference
|
|
e = f("=name")
|
|
res = {action="bref", op1= {s="name"}}
|
|
assert(equals(e,res))
|
|
|
|
--p ? optional match
|
|
e = f("name?")
|
|
res = {action="?", op1= {nt="name"}}
|
|
assert(equals(e,res))
|
|
|
|
--p * zero or more repetitions
|
|
e = f("name*")
|
|
res = {action="*", op1= {nt="name"}}
|
|
assert(equals(e,res))
|
|
|
|
--p + one or more repetitions
|
|
e = f("name+")
|
|
res = {action="+", op1= {nt="name"}}
|
|
assert(equals(e,res))
|
|
|
|
--p^num exactly n repetitions
|
|
e = f("name^3")
|
|
res = {action="^", op1= {nt="name"}, op2 = {num="3"}}
|
|
assert(equals(e,res))
|
|
|
|
--p^+num at least n repetitions
|
|
e = f("name^+3")
|
|
res = {action="^", op1= {nt="name"}, op2 = {num="+3"}}
|
|
assert(equals(e,res))
|
|
|
|
--p^-num at most n repetitions
|
|
e = f("name^-3")
|
|
res = {action="^", op1= {nt="name"}, op2 = {num="-3"}}
|
|
assert(equals(e,res))
|
|
|
|
--p^LABEL error label
|
|
e = f("name^err")
|
|
res = {action = "^LABEL", op1= {nt="name"}, op2 = {s="err"}}
|
|
assert(equals(e,res))
|
|
|
|
--p -> 'string' string capture
|
|
e = f("name -> 'a'")
|
|
res = {action="->", op1= {nt="name"}, op2 = {s="a"}}
|
|
assert(equals(e,res))
|
|
|
|
--p -> "string" string capture
|
|
e = f('name -> "a"')
|
|
res = {action="->", op1= {nt="name"}, op2 = {s="a"}}
|
|
assert(equals(e,res))
|
|
|
|
--p -> num numbered capture
|
|
|
|
e = f('name -> 3')
|
|
|
|
res = {action="->", op1= {nt="name"}, op2 = {num="3"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--p -> name function/query/string capture equivalent to p / defs[name]
|
|
|
|
e = f('name -> func')
|
|
res = {action="->", op1= {nt="name"}, op2 = {func="func"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
|
|
|
|
--p => name match-time capture equivalent to lpeg.Cmt(p, defs[name])
|
|
|
|
e = f('name => func')
|
|
res = {action="=>", op1= {nt="name"}, op2 = {func="func"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
|
|
--& p and predicate
|
|
|
|
e = f('&name')
|
|
res = {action="&", op1= {nt="name"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
|
|
--! p not predicate
|
|
|
|
|
|
e = f('!name')
|
|
res = {action="!", op1= {nt="name"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
|
|
--p1 p2 p3 concatenation with left association
|
|
|
|
e = f('name name2 name3')
|
|
res = {action="and", op1= {action = "and", op1={nt="name"}, op2={nt="name2"}}, op2={nt="name3"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
--p1 / p2 / p3 ordered choice with left association
|
|
|
|
e = f('name / name2 / name3')
|
|
res = {action="or", op1= {action = "or", op1={nt="name"}, op2={nt="name2"}}, op2={nt="name3"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
|
|
--(name <- p)+ grammar
|
|
|
|
e = f('a <- b b <- c')
|
|
res = {
|
|
{rulename = "a", rule = {nt="b"}},
|
|
{rulename = "b", rule = {nt="c"}}
|
|
}
|
|
|
|
assert(equals(e,res))
|
|
|
|
-- error labels
|
|
-- %{errName}
|
|
|
|
--peg.setlabels({errName=1})
|
|
e = f('%{errName}')
|
|
|
|
res = {action="label", op1={s="errName"}}
|
|
|
|
assert(equals(e,res))
|
|
|
|
-- a //{errName,errName2} b
|
|
|
|
--peg.setlabels({errName=1, errName2=2})
|
|
e = f('a //{errName,errName2} b')
|
|
|
|
res = {action="or", condition={{s="errName"},{s="errName2"}}, op1={nt="a"}, op2={nt="b"}}
|
|
|
|
|
|
assert(equals(e,res))
|
|
|
|
print("all tests succesful") |