Add stage 06: Lua bootstrap
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.
This commit is contained in:
parent
2ae045cf8a
commit
e6b88d5a0f
170 changed files with 72518 additions and 2 deletions
128
06/lpeglabel/examples/expRec.lua
Normal file
128
06/lpeglabel/examples/expRec.lua
Normal file
|
@ -0,0 +1,128 @@
|
|||
local m = require"lpeglabel"
|
||||
local re = require"relabel"
|
||||
|
||||
local labels = {
|
||||
{"ExpTermFirst", "expected an expression"},
|
||||
{"ExpTermOp", "expected a term after the operator"},
|
||||
{"MisClose", "missing a closing ')' after the expression"},
|
||||
}
|
||||
|
||||
local function labelindex(labname)
|
||||
for i, elem in ipairs(labels) do
|
||||
if elem[1] == labname then
|
||||
return i
|
||||
end
|
||||
end
|
||||
error("could not find label: " .. labname)
|
||||
end
|
||||
|
||||
local errors, subject
|
||||
|
||||
local function expect(patt, labname)
|
||||
local i = labelindex(labname)
|
||||
return patt + m.T(i)
|
||||
end
|
||||
|
||||
|
||||
local num = m.R("09")^1 / tonumber
|
||||
local op = m.S("+-")
|
||||
|
||||
local function compute(tokens)
|
||||
local result = tokens[1]
|
||||
for i = 2, #tokens, 2 do
|
||||
if tokens[i] == '+' then
|
||||
result = result + tokens[i+1]
|
||||
elseif tokens[i] == '-' then
|
||||
result = result - tokens[i+1]
|
||||
else
|
||||
error('unknown operation: ' .. tokens[i])
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local g = m.P {
|
||||
"Exp",
|
||||
Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute,
|
||||
OperandFirst = expect(m.V"Term", "ExpTermFirst"),
|
||||
Operand = expect(m.V"Term", "ExpTermOp"),
|
||||
Term = num + m.V"Group",
|
||||
Group = "(" * m.V"Exp" * expect(")", "MisClose"),
|
||||
}
|
||||
|
||||
function recorderror(pos, lab)
|
||||
local line, col = re.calcline(subject, pos)
|
||||
table.insert(errors, { line = line, col = col, msg = labels[lab][2] })
|
||||
end
|
||||
|
||||
function record (labname)
|
||||
return (m.Cp() * m.Cc(labelindex(labname))) / recorderror
|
||||
end
|
||||
|
||||
function sync (p)
|
||||
return (-p * m.P(1))^0
|
||||
end
|
||||
|
||||
function defaultValue (p)
|
||||
return p or m.Cc(1000)
|
||||
end
|
||||
|
||||
local grec = m.P {
|
||||
"S",
|
||||
S = m.Rec(m.V"A", m.V"ErrExpTermFirst", labelindex("ExpTermFirst")), -- default value is 0
|
||||
A = m.Rec(m.V"Sg", m.V"ErrExpTermOp", labelindex("ExpTermOp")),
|
||||
Sg = m.Rec(g, m.V"ErrMisClose", labelindex("MisClose")),
|
||||
ErrExpTermFirst = record("ExpTermFirst") * sync(op + ")") * defaultValue(),
|
||||
ErrExpTermOp = record("ExpTermOp") * sync(op + ")") * defaultValue(),
|
||||
ErrMisClose = record("MisClose") * sync(m.P")") * defaultValue(m.P""),
|
||||
}
|
||||
|
||||
local function eval(input)
|
||||
errors = {}
|
||||
io.write("Input: ", input, "\n")
|
||||
subject = input
|
||||
local result, label, suffix = grec:match(input)
|
||||
io.write("Syntactic errors found: " .. #errors, "\n")
|
||||
if #errors > 0 then
|
||||
local out = {}
|
||||
for i, err in ipairs(errors) do
|
||||
local pos = err.col
|
||||
local msg = err.msg
|
||||
table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
|
||||
end
|
||||
print(table.concat(out, "\n"))
|
||||
end
|
||||
io.write("Result = ")
|
||||
return result
|
||||
end
|
||||
|
||||
print(eval "90-70-(5)+3")
|
||||
--> 20
|
||||
|
||||
print(eval "15+")
|
||||
--> 2 + 0
|
||||
|
||||
print(eval "-2")
|
||||
--> 0 - 2
|
||||
|
||||
print(eval "1+3+-9")
|
||||
--> 1 + 3 + [0] - 9
|
||||
|
||||
print(eval "1+()3+")
|
||||
--> 1 + ([0]) [3 +] [0]
|
||||
|
||||
print(eval "8-(2+)-5")
|
||||
--> 8 - (2 + [0]) - 5
|
||||
|
||||
print(eval "()")
|
||||
|
||||
print(eval "")
|
||||
|
||||
print(eval "1+()+")
|
||||
|
||||
print(eval "1+(")
|
||||
|
||||
print(eval "3)")
|
||||
|
||||
print(eval "11+())3")
|
||||
|
122
06/lpeglabel/examples/expRecAut.lua
Normal file
122
06/lpeglabel/examples/expRecAut.lua
Normal file
|
@ -0,0 +1,122 @@
|
|||
local m = require"lpeglabel"
|
||||
local re = require"relabel"
|
||||
|
||||
local num = m.R("09")^1 / tonumber
|
||||
local op = m.S("+-")
|
||||
|
||||
local labels = {}
|
||||
local nlabels = 0
|
||||
|
||||
local function newError(lab, msg, psync, pcap)
|
||||
nlabels = nlabels + 1
|
||||
psync = psync or m.P(-1)
|
||||
pcap = pcap or m.P""
|
||||
labels[lab] = { id = nlabels, msg = msg, psync = psync, pcap = pcap }
|
||||
end
|
||||
|
||||
newError("ExpTermFirst", "expected an expression", op + ")", m.Cc(1000))
|
||||
newError("ExpTermOp", "expected a term after the operator", op + ")", m.Cc(1000))
|
||||
newError("MisClose", "missing a closing ')' after the expression", m.P")")
|
||||
newError("Extra", "extra characters found after the expression")
|
||||
|
||||
local errors, subject
|
||||
|
||||
local function expect(patt, labname)
|
||||
local i = labels[labname].id
|
||||
return patt + m.T(i)
|
||||
end
|
||||
|
||||
local function compute(tokens)
|
||||
local result = tokens[1]
|
||||
for i = 2, #tokens, 2 do
|
||||
if tokens[i] == '+' then
|
||||
result = result + tokens[i+1]
|
||||
elseif tokens[i] == '-' then
|
||||
result = result - tokens[i+1]
|
||||
else
|
||||
error('unknown operation: ' .. tokens[i])
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local g = m.P {
|
||||
"Exp",
|
||||
Exp = m.Ct(m.V"OperandFirst" * (m.C(op) * m.V"Operand")^0) / compute,
|
||||
OperandFirst = expect(m.V"Term", "ExpTermFirst"),
|
||||
Operand = expect(m.V"Term", "ExpTermOp"),
|
||||
Term = num + m.V"Group",
|
||||
Group = "(" * m.V"Exp" * expect(")", "MisClose"),
|
||||
}
|
||||
|
||||
function recorderror(pos, lab)
|
||||
local line, col = re.calcline(subject, pos)
|
||||
table.insert(errors, { line = line, col = col, msg = labels[lab].msg })
|
||||
end
|
||||
|
||||
function record (labname)
|
||||
return (m.Cp() * m.Cc(labname)) / recorderror
|
||||
end
|
||||
|
||||
function sync (p)
|
||||
return (-p * m.P(1))^0
|
||||
end
|
||||
|
||||
function defaultValue (p)
|
||||
return p or m.Cc(1000)
|
||||
end
|
||||
|
||||
local grec = g * expect(m.P(-1), "Extra")
|
||||
for k, v in pairs(labels) do
|
||||
grec = m.Rec(grec, record(k) * sync(v.psync) * v.pcap, v.id)
|
||||
end
|
||||
|
||||
local function eval(input)
|
||||
errors = {}
|
||||
io.write("Input: ", input, "\n")
|
||||
subject = input
|
||||
local result, label, suffix = grec:match(input)
|
||||
io.write("Syntactic errors found: " .. #errors, "\n")
|
||||
if #errors > 0 then
|
||||
local out = {}
|
||||
for i, err in ipairs(errors) do
|
||||
local pos = err.col
|
||||
local msg = err.msg
|
||||
table.insert(out, "syntax error: " .. msg .. " (at index " .. pos .. ")")
|
||||
end
|
||||
print(table.concat(out, "\n"))
|
||||
end
|
||||
io.write("Result = ")
|
||||
return result
|
||||
end
|
||||
|
||||
print(eval "90-70-(5)+3")
|
||||
--> 18
|
||||
|
||||
print(eval "15+")
|
||||
--> 2 + 0
|
||||
|
||||
print(eval "-2")
|
||||
--> 0 - 2
|
||||
|
||||
print(eval "1+3+-9")
|
||||
--> 1 + 3 + [0] - 9
|
||||
|
||||
print(eval "1+()3+")
|
||||
--> 1 + ([0]) [+] 3 + [0]
|
||||
|
||||
print(eval "8-(2+)-5")
|
||||
--> 8 - (2 + [0]) - 5
|
||||
|
||||
print(eval "()")
|
||||
|
||||
print(eval "")
|
||||
|
||||
print(eval "1+()+")
|
||||
|
||||
print(eval "1+(")
|
||||
|
||||
print(eval "3)")
|
||||
|
||||
print(eval "11+()3")
|
||||
--> 1 + ([0]) [+] 3 + [0]
|
14
06/lpeglabel/examples/farthest.lua
Normal file
14
06/lpeglabel/examples/farthest.lua
Normal file
|
@ -0,0 +1,14 @@
|
|||
local m = require'lpeglabel'
|
||||
|
||||
function matchPrint(p, s)
|
||||
local r, lab, sfail = p:match(s)
|
||||
print("r: ", r, "lab: ", lab, "sfail: ", sfail)
|
||||
end
|
||||
|
||||
local p = m.P"a"^0 * m.P"b" + m.P"c"
|
||||
matchPrint(p, "abc") --> r: 3 lab: nil sfail: nil
|
||||
matchPrint(p, "c") --> r: 2 lab: nil sfail: nil
|
||||
matchPrint(p, "aac") --> r: nil lab: 0 sfail: c
|
||||
matchPrint(p, "xxc") --> r: nil lab: 0 sfail: xxc
|
||||
|
||||
|
33
06/lpeglabel/examples/listId1.lua
Normal file
33
06/lpeglabel/examples/listId1.lua
Normal file
|
@ -0,0 +1,33 @@
|
|||
local m = require'lpeglabel'
|
||||
local re = require'relabel'
|
||||
|
||||
local id = m.R'az'^1
|
||||
|
||||
local g = m.P{
|
||||
"S",
|
||||
S = m.V"Id" * m.V"List",
|
||||
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
|
||||
Id = m.V"Sp" * id + m.T(1),
|
||||
Comma = m.V"Sp" * "," + m.T(2),
|
||||
Sp = m.S" \n\t"^0,
|
||||
}
|
||||
|
||||
function mymatch (g, s)
|
||||
local r, e, sfail = g:match(s)
|
||||
if not r then
|
||||
local line, col = re.calcline(s, #s - #sfail)
|
||||
local msg = "Error at line " .. line .. " (col " .. col .. ")"
|
||||
if e == 1 then
|
||||
return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
|
||||
elseif e == 2 then
|
||||
return r, msg .. ": expecting ',' before '" .. sfail .. "'"
|
||||
else
|
||||
return r, msg
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
print(mymatch(g, "one,two"))
|
||||
print(mymatch(g, "one two"))
|
||||
print(mymatch(g, "one,\n two,\nthree,"))
|
39
06/lpeglabel/examples/listId2.lua
Normal file
39
06/lpeglabel/examples/listId2.lua
Normal file
|
@ -0,0 +1,39 @@
|
|||
local m = require'lpeglabel'
|
||||
local re = require'relabel'
|
||||
|
||||
local terror = {}
|
||||
|
||||
local function newError(s)
|
||||
table.insert(terror, s)
|
||||
return #terror
|
||||
end
|
||||
|
||||
local errUndef = newError("undefined")
|
||||
local errId = newError("expecting an identifier")
|
||||
local errComma = newError("expecting ','")
|
||||
|
||||
local id = m.R'az'^1
|
||||
|
||||
local g = m.P{
|
||||
"S",
|
||||
S = m.V"Id" * m.V"List",
|
||||
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
|
||||
Id = m.V"Sp" * id + m.T(errId),
|
||||
Comma = m.V"Sp" * "," + m.T(errComma),
|
||||
Sp = m.S" \n\t"^0,
|
||||
}
|
||||
|
||||
|
||||
function mymatch (g, s)
|
||||
local r, e, sfail = g:match(s)
|
||||
if not r then
|
||||
local line, col = re.calcline(s, #s - #sfail)
|
||||
local msg = "Error at line " .. line .. " (col " .. col .. "): "
|
||||
return r, msg .. terror[e] .. " before '" .. sfail .. "'"
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
print(mymatch(g, "one,two"))
|
||||
print(mymatch(g, "one two"))
|
||||
print(mymatch(g, "one,\n two,\nthree,"))
|
67
06/lpeglabel/examples/listId2Rec2.lua
Normal file
67
06/lpeglabel/examples/listId2Rec2.lua
Normal file
|
@ -0,0 +1,67 @@
|
|||
local m = require'lpeglabel'
|
||||
local re = require'relabel'
|
||||
|
||||
local terror = {}
|
||||
|
||||
local function newError(s)
|
||||
table.insert(terror, s)
|
||||
return #terror
|
||||
end
|
||||
|
||||
local errUndef = newError("undefined")
|
||||
local errId = newError("expecting an identifier")
|
||||
local errComma = newError("expecting ','")
|
||||
|
||||
local id = m.R'az'^1
|
||||
|
||||
local g = m.P{
|
||||
"S",
|
||||
S = m.V"Id" * m.V"List",
|
||||
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
|
||||
Id = m.V"Sp" * id + m.T(errId),
|
||||
Comma = m.V"Sp" * "," + m.T(errComma),
|
||||
Sp = m.S" \n\t"^0,
|
||||
}
|
||||
|
||||
local subject, errors
|
||||
|
||||
function recorderror(pos, lab)
|
||||
local line, col = re.calcline(subject, pos)
|
||||
table.insert(errors, { line = line, col = col, msg = terror[lab] })
|
||||
end
|
||||
|
||||
function record (lab)
|
||||
return (m.Cp() * m.Cc(lab)) / recorderror
|
||||
end
|
||||
|
||||
function sync (p)
|
||||
return (-p * m.P(1))^0
|
||||
end
|
||||
|
||||
local grec = m.P{
|
||||
"S",
|
||||
S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
|
||||
ErrComma = record(errComma) * sync(id),
|
||||
ErrId = record(errId) * sync(m.P",")
|
||||
}
|
||||
|
||||
|
||||
function mymatch (g, s)
|
||||
errors = {}
|
||||
subject = s
|
||||
local r, e, sfail = g:match(s)
|
||||
if #errors > 0 then
|
||||
local out = {}
|
||||
for i, err in ipairs(errors) do
|
||||
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
|
||||
table.insert(out, msg)
|
||||
end
|
||||
return nil, table.concat(out, "\n") .. "\n"
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
print(mymatch(grec, "one,two"))
|
||||
print(mymatch(grec, "one two three"))
|
||||
print(mymatch(grec, "1,\n two, \n3,"))
|
||||
print(mymatch(grec, "one\n two123, \nthree,"))
|
79
06/lpeglabel/examples/listId2Rec2Cap.lua
Normal file
79
06/lpeglabel/examples/listId2Rec2Cap.lua
Normal file
|
@ -0,0 +1,79 @@
|
|||
local m = require'lpeglabel'
|
||||
local re = require'relabel'
|
||||
|
||||
local terror = {}
|
||||
|
||||
local function newError(s)
|
||||
table.insert(terror, s)
|
||||
return #terror
|
||||
end
|
||||
|
||||
local errUndef = newError("undefined")
|
||||
local errId = newError("expecting an identifier")
|
||||
local errComma = newError("expecting ','")
|
||||
|
||||
local id = m.R'az'^1
|
||||
|
||||
local g = m.P{
|
||||
"S",
|
||||
S = m.V"Id" * m.V"List",
|
||||
List = -m.P(1) + m.V"Comma" * m.V"Id" * m.V"List",
|
||||
Id = m.V"Sp" * m.C(id) + m.T(errId),
|
||||
Comma = m.V"Sp" * "," + m.T(errComma),
|
||||
Sp = m.S" \n\t"^0,
|
||||
}
|
||||
|
||||
local subject, errors
|
||||
|
||||
function recorderror(pos, lab)
|
||||
local line, col = re.calcline(subject, pos)
|
||||
table.insert(errors, { line = line, col = col, msg = terror[lab] })
|
||||
end
|
||||
|
||||
function record (lab)
|
||||
return (m.Cp() * m.Cc(lab)) / recorderror
|
||||
end
|
||||
|
||||
function sync (p)
|
||||
return (-p * m.P(1))^0
|
||||
end
|
||||
|
||||
function defaultValue ()
|
||||
return m.Cc"NONE"
|
||||
end
|
||||
|
||||
local grec = m.P{
|
||||
"S",
|
||||
S = m.Rec(m.Rec(g, m.V"ErrComma", errComma), m.V"ErrId", errId),
|
||||
ErrComma = record(errComma) * sync(id),
|
||||
ErrId = record(errId) * sync(m.P",") * defaultValue(),
|
||||
}
|
||||
|
||||
|
||||
function mymatch (g, s)
|
||||
errors = {}
|
||||
subject = s
|
||||
io.write("Input: ", s, "\n")
|
||||
local r = { g:match(s) }
|
||||
io.write("Captures (separated by ';'): ")
|
||||
for k, v in pairs(r) do
|
||||
io.write(v .. "; ")
|
||||
end
|
||||
io.write("\nSyntactic errors found: " .. #errors)
|
||||
if #errors > 0 then
|
||||
io.write("\n")
|
||||
local out = {}
|
||||
for i, err in ipairs(errors) do
|
||||
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
|
||||
table.insert(out, msg)
|
||||
end
|
||||
io.write(table.concat(out, "\n"))
|
||||
end
|
||||
print("\n")
|
||||
return r
|
||||
end
|
||||
|
||||
mymatch(grec, "one,two")
|
||||
mymatch(grec, "one two three")
|
||||
mymatch(grec, "1,\n two, \n3,")
|
||||
mymatch(grec, "one\n two123, \nthree,")
|
30
06/lpeglabel/examples/listIdRe1.lua
Normal file
30
06/lpeglabel/examples/listIdRe1.lua
Normal file
|
@ -0,0 +1,30 @@
|
|||
local re = require 'relabel'
|
||||
|
||||
local g = re.compile[[
|
||||
S <- Id List
|
||||
List <- !. / Comma Id List
|
||||
Id <- Sp [a-z]+ / %{2}
|
||||
Comma <- Sp ',' / %{3}
|
||||
Sp <- %s*
|
||||
]]
|
||||
|
||||
function mymatch (g, s)
|
||||
local r, e, sfail = g:match(s)
|
||||
if not r then
|
||||
local line, col = re.calcline(s, #s - #sfail)
|
||||
local msg = "Error at line " .. line .. " (col " .. col .. ")"
|
||||
if e == 1 then
|
||||
return r, msg .. ": expecting an identifier before '" .. sfail .. "'"
|
||||
elseif e == 2 then
|
||||
return r, msg .. ": expecting ',' before '" .. sfail .. "'"
|
||||
else
|
||||
return r, msg
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
print(mymatch(g, "one,two"))
|
||||
print(mymatch(g, "one two"))
|
||||
print(mymatch(g, "one,\n two,\nthree,"))
|
||||
|
71
06/lpeglabel/examples/listIdRe2.lua
Normal file
71
06/lpeglabel/examples/listIdRe2.lua
Normal file
|
@ -0,0 +1,71 @@
|
|||
local re = require 'relabel'
|
||||
|
||||
local errinfo = {
|
||||
{"errUndef", "undefined"},
|
||||
{"errId", "expecting an identifier"},
|
||||
{"errComma", "expecting ','"},
|
||||
}
|
||||
|
||||
local errmsgs = {}
|
||||
local labels = {}
|
||||
|
||||
for i, err in ipairs(errinfo) do
|
||||
errmsgs[i] = err[2]
|
||||
labels[err[1]] = i
|
||||
end
|
||||
|
||||
re.setlabels(labels)
|
||||
|
||||
local g = re.compile[[
|
||||
S <- Id List
|
||||
List <- !. / Comma Id List
|
||||
Id <- Sp {[a-z]+} / %{errId}
|
||||
Comma <- Sp ',' / %{errComma}
|
||||
Sp <- %s*
|
||||
]]
|
||||
|
||||
local errors
|
||||
|
||||
function recorderror (subject, pos, label)
|
||||
local line, col = re.calcline(subject, pos)
|
||||
table.insert(errors, { line = line, col = col, msg = errmsgs[labels[label]] })
|
||||
return true
|
||||
end
|
||||
|
||||
function sync (p)
|
||||
return '( !(' .. p .. ') .)*'
|
||||
end
|
||||
|
||||
local grec = re.compile(
|
||||
"S <- %g //{errComma} ErrComma //{errId} ErrId" .. "\n" ..
|
||||
"ErrComma <- ('' -> 'errComma' => recorderror) " .. sync('[a-z]+') .. "\n" ..
|
||||
"ErrId <- ('' -> 'errId' => recorderror) " .. sync('","') .. "-> default"
|
||||
, {g = g, recorderror = recorderror, default = "NONE"})
|
||||
|
||||
function mymatch (g, s)
|
||||
errors = {}
|
||||
subject = s
|
||||
io.write("Input: ", s, "\n")
|
||||
local r = { g:match(s) }
|
||||
io.write("Captures (separated by ';'): ")
|
||||
for k, v in pairs(r) do
|
||||
io.write(v .. "; ")
|
||||
end
|
||||
io.write("\nSyntactic errors found: " .. #errors)
|
||||
if #errors > 0 then
|
||||
io.write("\n")
|
||||
local out = {}
|
||||
for i, err in ipairs(errors) do
|
||||
local msg = "Error at line " .. err.line .. " (col " .. err.col .. "): " .. err.msg
|
||||
table.insert(out, msg)
|
||||
end
|
||||
io.write(table.concat(out, "\n"))
|
||||
end
|
||||
print("\n")
|
||||
return r
|
||||
end
|
||||
|
||||
mymatch(grec, "one,two")
|
||||
mymatch(grec, "one two three")
|
||||
mymatch(grec, "1,\n two, \n3,")
|
||||
mymatch(grec, "one\n two123, \nthree,")
|
151
06/lpeglabel/examples/tiny.lua
Normal file
151
06/lpeglabel/examples/tiny.lua
Normal file
|
@ -0,0 +1,151 @@
|
|||
local re = require 'relabel'
|
||||
|
||||
local terror = {}
|
||||
|
||||
local function newError(l, msg)
|
||||
table.insert(terror, { l = l, msg = msg} )
|
||||
end
|
||||
|
||||
newError("errSemi", "Error: missing ';'")
|
||||
newError("errExpIf", "Error: expected expression after 'if'")
|
||||
newError("errThen", "Error: expected 'then' keyword")
|
||||
newError("errCmdSeq1", "Error: expected at least a command after 'then'")
|
||||
newError("errCmdSeq2", "Error: expected at least a command after 'else'")
|
||||
newError("errEnd", "Error: expected 'end' keyword")
|
||||
newError("errCmdSeqRep", "Error: expected at least a command after 'repeat'")
|
||||
newError("errUntil", "Error: expected 'until' keyword")
|
||||
newError("errExpRep", "Error: expected expression after 'until'")
|
||||
newError("errAssignOp", "Error: expected ':=' in assigment")
|
||||
newError("errExpAssign", "Error: expected expression after ':='")
|
||||
newError("errReadName", "Error: expected an identifier after 'read'")
|
||||
newError("errWriteExp", "Error: expected expression after 'write'")
|
||||
newError("errSimpExp", "Error: expected '(', ID, or number after '<' or '='")
|
||||
newError("errTerm", "Error: expected '(', ID, or number after '+' or '-'")
|
||||
newError("errFactor", "Error: expected '(', ID, or number after '*' or '/'")
|
||||
newError("errExpFac", "Error: expected expression after '('")
|
||||
newError("errClosePar", "Error: expected ')' after expression")
|
||||
|
||||
|
||||
local labelCode = {}
|
||||
for k, v in ipairs(terror) do
|
||||
labelCode[v.l] = k
|
||||
end
|
||||
|
||||
re.setlabels(labelCode)
|
||||
|
||||
local g = re.compile[[
|
||||
Tiny <- CmdSeq
|
||||
CmdSeq <- (Cmd (SEMICOLON / ErrSemi)) (Cmd (SEMICOLON / ErrSemi))*
|
||||
Cmd <- IfCmd / RepeatCmd / ReadCmd / WriteCmd / AssignCmd
|
||||
IfCmd <- IF (Exp / ErrExpIf) (THEN / ErrThen) (CmdSeq / ErrCmdSeq1) (ELSE (CmdSeq / ErrCmdSeq2) / '') (END / ErrEnd)
|
||||
RepeatCmd <- REPEAT (CmdSeq / ErrCmdSeqRep) (UNTIL / ErrUntil) (Exp / ErrExpRep)
|
||||
AssignCmd <- NAME (ASSIGNMENT / ErrAssignOp) (Exp / ErrExpAssign)
|
||||
ReadCmd <- READ (NAME / ErrReadName)
|
||||
WriteCmd <- WRITE (Exp / ErrWriteExp)
|
||||
Exp <- SimpleExp ((LESS / EQUAL) (SimpleExp / ErrSimpExp) / '')
|
||||
SimpleExp <- Term ((ADD / SUB) (Term / ErrTerm))*
|
||||
Term <- Factor ((MUL / DIV) (Factor / ErrFactor))*
|
||||
Factor <- OPENPAR (Exp / ErrExpFac) (CLOSEPAR / ErrClosePar) / NUMBER / NAME
|
||||
ErrSemi <- %{errSemi}
|
||||
ErrExpIf <- %{errExpIf}
|
||||
ErrThen <- %{errThen}
|
||||
ErrCmdSeq1 <- %{errCmdSeq1}
|
||||
ErrCmdSeq2 <- %{errCmdSeq2}
|
||||
ErrEnd <- %{errEnd}
|
||||
ErrCmdSeqRep <- %{errCmdSeqRep}
|
||||
ErrUntil <- %{errUntil}
|
||||
ErrExpRep <- %{errExpRep}
|
||||
ErrAssignOp <- %{errAssignOp}
|
||||
ErrExpAssign <- %{errExpAssign}
|
||||
ErrReadName <- %{errReadName}
|
||||
ErrWriteExp <- %{errWriteExp}
|
||||
ErrSimpExp <- %{errSimpExp}
|
||||
ErrTerm <- %{errTerm}
|
||||
ErrFactor <- %{errFactor}
|
||||
ErrExpFac <- %{errExpFac}
|
||||
ErrClosePar <- %{errClosePar}
|
||||
ADD <- Sp '+'
|
||||
ASSIGNMENT <- Sp ':='
|
||||
CLOSEPAR <- Sp ')'
|
||||
DIV <- Sp '/'
|
||||
IF <- Sp 'if'
|
||||
ELSE <- Sp 'else'
|
||||
END <- Sp 'end'
|
||||
EQUAL <- Sp '='
|
||||
LESS <- Sp '<'
|
||||
MUL <- Sp '*'
|
||||
NAME <- Sp !RESERVED [a-z]+
|
||||
NUMBER <- Sp [0-9]+
|
||||
OPENPAR <- Sp '('
|
||||
READ <- Sp 'read'
|
||||
REPEAT <- Sp 'repeat'
|
||||
SEMICOLON <- Sp ';'
|
||||
SUB <- Sp '-'
|
||||
THEN <- Sp 'then'
|
||||
UNTIL <- Sp 'until'
|
||||
WRITE <- Sp 'write'
|
||||
RESERVED <- (IF / ELSE / END / READ / REPEAT / THEN / UNTIL / WRITE) ![a-z]+
|
||||
Sp <- %s*
|
||||
]]
|
||||
|
||||
|
||||
local function mymatch(g, s)
|
||||
local r, e, sfail = g:match(s)
|
||||
if not r then
|
||||
local line, col = re.calcline(s, #s - #sfail)
|
||||
local msg = "Error at line " .. line .. " (col " .. col .. "): "
|
||||
return r, msg .. terror[e].msg
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
local s = [[
|
||||
n := 5;
|
||||
f := 1;
|
||||
repeat
|
||||
f := f + n;
|
||||
n := n - 1
|
||||
until (n < 1);
|
||||
write f;]]
|
||||
print(mymatch(g, s))
|
||||
|
||||
s = [[
|
||||
n := 5;
|
||||
f := 1;
|
||||
repeat
|
||||
f := f + n;
|
||||
n := n - 1;
|
||||
until (n < 1);
|
||||
read ;]]
|
||||
print(mymatch(g, s))
|
||||
|
||||
s = [[
|
||||
if a < 1 then
|
||||
b := 2;
|
||||
else
|
||||
b := 3;]]
|
||||
print(mymatch(g, s))
|
||||
|
||||
s = [[
|
||||
n := 5;
|
||||
f := 1;
|
||||
repeat
|
||||
f := f + n;
|
||||
n := n - 1;
|
||||
untill (n < 1);
|
||||
]]
|
||||
print(mymatch(g, s))
|
||||
|
||||
s = [[
|
||||
n := 5;
|
||||
f := 1;
|
||||
repeat
|
||||
f := f + n;
|
||||
n := n - 1;
|
||||
3 (n < 1);
|
||||
]]
|
||||
print(mymatch(g, s))
|
||||
|
||||
print(mymatch(g, "a : 2"))
|
||||
print(mymatch(g, "a := (2"))
|
||||
|
2572
06/lpeglabel/examples/typedlua/test.lua
Executable file
2572
06/lpeglabel/examples/typedlua/test.lua
Executable file
File diff suppressed because it is too large
Load diff
66
06/lpeglabel/examples/typedlua/tlerror.lua
Normal file
66
06/lpeglabel/examples/typedlua/tlerror.lua
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
local errors = {}
|
||||
local function new_error (label, msg)
|
||||
table.insert(errors, { label = label, msg = msg })
|
||||
end
|
||||
|
||||
new_error("Number", "malformed <number>")
|
||||
new_error("String", "malformed <string>")
|
||||
new_error("LongString", "unfinished long string")
|
||||
new_error("LongComment", "unfinished long comment")
|
||||
new_error("MissingOP", "missing '('")
|
||||
new_error("MissingCP", "missing ')'")
|
||||
new_error("MissingCC", "missing '}'")
|
||||
new_error("MissingCB", "missing ']'")
|
||||
new_error("UnionType", "expecting <type> after '|'")
|
||||
new_error("FunctionType", "expecting <type> after '->'")
|
||||
new_error("MethodType", "expecting <type> after '=>'")
|
||||
new_error("TupleType", "expecting <type> after ','")
|
||||
new_error("Type", "expecting <type> after ':'")
|
||||
new_error("TypeDecEnd", "missing 'end' in type declaration")
|
||||
new_error("TypeAliasName", "expecting <name> after 'typealias'")
|
||||
new_error("MissingEqTypeAlias", "missing '=' in 'typealias'")
|
||||
new_error("DotIndex", "expecting <name> after '.'")
|
||||
new_error("MethodName", "expecting <name> after ':'")
|
||||
new_error("Then", "missing 'then'")
|
||||
new_error("IfEnd", "missing 'end' to close if statement")
|
||||
new_error("WhileDo", "missing 'do' in while statement")
|
||||
new_error("WhileEnd", "missing 'end' to close while statement")
|
||||
new_error("BlockEnd", "missing 'end' to close block")
|
||||
new_error("ForDo", "missing 'do' in for statement")
|
||||
new_error("ForEnd", "missing 'end' to close for statement")
|
||||
new_error("Until", "missing 'until' in repeat statement")
|
||||
new_error("FuncEnd", "missing 'end' to close function declaration")
|
||||
new_error("ParList", "expecting '...'")
|
||||
new_error("MethodCall", "expecting '(' for method call")
|
||||
new_error("Label1", "expecting <name> after '::'")
|
||||
new_error("Label2", "expecting '::' to close label declaration")
|
||||
new_error("LocalAssign1", "expecting expression list after '='")
|
||||
new_error("LocalAssign2", "invalid local declaration")
|
||||
new_error("ForGen", "expecting 'in'")
|
||||
new_error("LocalFunc", "expecting <name> in local function declaration")
|
||||
new_error("RetStat", "invalid statement after 'return'")
|
||||
new_error("ElseIf", "expecting <exp> after 'elseif'")
|
||||
new_error("SubExpr_1", "malformed 'or' expression")
|
||||
new_error("SubExpr_2", "malformed 'and' expression")
|
||||
new_error("SubExpr_3", "malformed relational expression")
|
||||
new_error("SubExpr_4", "malformed '|' expression")
|
||||
new_error("SubExpr_5", "malformed '~' expression")
|
||||
new_error("SubExpr_6", "malformed '&' expression")
|
||||
new_error("SubExpr_7", "malformed shift expression")
|
||||
new_error("SubExpr_8", "malformed '..' expression")
|
||||
new_error("SubExpr_9", "malformed addition expression")
|
||||
new_error("SubExpr_10", "malformed multiplication expression")
|
||||
new_error("SubExpr_11", "malformed unary expression")
|
||||
new_error("SubExpr_12", "malformed '^' expression")
|
||||
new_error("Stat", "invalid statement")
|
||||
|
||||
local labels = {}
|
||||
for k, v in ipairs(errors) do
|
||||
labels[v.label] = k
|
||||
end
|
||||
|
||||
return {
|
||||
errors = errors,
|
||||
labels = labels,
|
||||
}
|
105
06/lpeglabel/examples/typedlua/tllexer.lua
Normal file
105
06/lpeglabel/examples/typedlua/tllexer.lua
Normal file
|
@ -0,0 +1,105 @@
|
|||
local tllexer = {}
|
||||
|
||||
local lpeg = require "lpeglabel"
|
||||
lpeg.locale(lpeg)
|
||||
|
||||
local tlerror = require "tlerror"
|
||||
|
||||
function tllexer.try (pat, label)
|
||||
return pat + lpeg.T(tlerror.labels[label])
|
||||
end
|
||||
|
||||
local function setffp (s, i, t, n)
|
||||
if not t.ffp or i > t.ffp then
|
||||
t.ffp = i
|
||||
t.list = {}
|
||||
t.list[n] = true
|
||||
t.expected = "'" .. n .. "'"
|
||||
elseif i == t.ffp then
|
||||
if not t.list[n] then
|
||||
t.list[n] = true
|
||||
t.expected = "'" .. n .. "', " .. t.expected
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function updateffp (name)
|
||||
return lpeg.Cmt(lpeg.Carg(1) * lpeg.Cc(name), setffp)
|
||||
end
|
||||
|
||||
tllexer.Shebang = lpeg.P("#") * (lpeg.P(1) - lpeg.P("\n"))^0 * lpeg.P("\n")
|
||||
|
||||
local Space = lpeg.space^1
|
||||
|
||||
local Equals = lpeg.P("=")^0
|
||||
local Open = "[" * lpeg.Cg(Equals, "init") * "[" * lpeg.P("\n")^-1
|
||||
local Close = "]" * lpeg.C(Equals) * "]"
|
||||
local CloseEQ = lpeg.Cmt(Close * lpeg.Cb("init"),
|
||||
function (s, i, a, b) return a == b end)
|
||||
|
||||
local LongString = Open * (lpeg.P(1) - CloseEQ)^0 * tllexer.try(Close, "LongString") /
|
||||
function (s, o) return s end
|
||||
|
||||
local LongStringCm1 = Open * (lpeg.P(1) - CloseEQ)^0 * Close /
|
||||
function (s, o) return s end
|
||||
|
||||
local Comment = lpeg.Rec(lpeg.P"--" * #Open * (LongStringCm1 / function() return end + lpeg.T(tlerror.labels["LongString"])),
|
||||
lpeg.T(tlerror.labels["LongComment"]), tlerror.labels["LongString"]) +
|
||||
lpeg.P("--") * (lpeg.P(1) - lpeg.P("\n"))^0
|
||||
|
||||
tllexer.Skip = (Space + Comment)^0
|
||||
|
||||
local idStart = lpeg.alpha + lpeg.P("_")
|
||||
local idRest = lpeg.alnum + lpeg.P("_")
|
||||
|
||||
local Keywords = lpeg.P("and") + "break" + "do" + "elseif" + "else" + "end" +
|
||||
"false" + "for" + "function" + "goto" + "if" + "in" +
|
||||
"local" + "nil" + "not" + "or" + "repeat" + "return" +
|
||||
"then" + "true" + "until" + "while"
|
||||
|
||||
tllexer.Reserved = Keywords * -idRest
|
||||
|
||||
local Identifier = idStart * idRest^0
|
||||
|
||||
tllexer.Name = -tllexer.Reserved * Identifier * -idRest
|
||||
|
||||
function tllexer.token (pat, name)
|
||||
return pat * tllexer.Skip + updateffp(name) * lpeg.P(false)
|
||||
end
|
||||
|
||||
function tllexer.symb (str)
|
||||
return tllexer.token(lpeg.P(str), str)
|
||||
end
|
||||
|
||||
function tllexer.kw (str)
|
||||
return tllexer.token(lpeg.P(str) * -idRest, str)
|
||||
end
|
||||
|
||||
local Hex = (lpeg.P("0x") + lpeg.P("0X")) * tllexer.try(lpeg.xdigit^1, "Number")
|
||||
local Expo = lpeg.S("eE") * lpeg.S("+-")^-1 * tllexer.try(lpeg.digit^1, "Number")
|
||||
local Float = (((lpeg.digit^1 * lpeg.P(".") * lpeg.digit^0 * tllexer.try(-lpeg.P("."), "Number")) +
|
||||
(lpeg.P(".") * lpeg.digit^1)) * Expo^-1) +
|
||||
(lpeg.digit^1 * Expo)
|
||||
local Int = lpeg.digit^1
|
||||
|
||||
tllexer.Number = Hex + Float + Int
|
||||
|
||||
local ShortString = lpeg.P('"') *
|
||||
((lpeg.P('\\') * lpeg.P(1)) + (lpeg.P(1) - lpeg.P('"')))^0 *
|
||||
tllexer.try(lpeg.P('"'), "String") +
|
||||
lpeg.P("'") *
|
||||
((lpeg.P("\\") * lpeg.P(1)) + (lpeg.P(1) - lpeg.P("'")))^0 *
|
||||
tllexer.try(lpeg.P("'"), "String")
|
||||
|
||||
tllexer.String = LongString + ShortString
|
||||
|
||||
-- for error reporting
|
||||
tllexer.OneWord = tllexer.Name +
|
||||
tllexer.Number +
|
||||
tllexer.String +
|
||||
tllexer.Reserved +
|
||||
lpeg.P("...") +
|
||||
lpeg.P(1)
|
||||
|
||||
return tllexer
|
20
06/lpeglabel/examples/typedlua/tlp.lua
Normal file
20
06/lpeglabel/examples/typedlua/tlp.lua
Normal file
|
@ -0,0 +1,20 @@
|
|||
local tlparser = require "tlparser"
|
||||
|
||||
local function getcontents(filename)
|
||||
file = assert(io.open(filename, "r"))
|
||||
contents = file:read("*a")
|
||||
file:close()
|
||||
return contents
|
||||
end
|
||||
|
||||
if #arg ~= 1 then
|
||||
print ("Usage: lua tlp.lua <file>")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local filename = arg[1]
|
||||
local subject = getcontents(filename)
|
||||
local r, msg = tlparser.parse(subject, filename, false, true)
|
||||
if not r then print(msg) end
|
||||
|
||||
os.exit(0)
|
245
06/lpeglabel/examples/typedlua/tlparser.lua
Normal file
245
06/lpeglabel/examples/typedlua/tlparser.lua
Normal file
|
@ -0,0 +1,245 @@
|
|||
local tlparser = {}
|
||||
|
||||
local lpeg = require "lpeglabel"
|
||||
lpeg.locale(lpeg)
|
||||
|
||||
local tllexer = require "tllexer"
|
||||
local tlerror = require "tlerror"
|
||||
|
||||
local function chainl1 (pat, sep, label)
|
||||
return pat * (sep * tllexer.try(pat, label))^0
|
||||
end
|
||||
|
||||
local G = lpeg.P { "TypedLua";
|
||||
TypedLua = tllexer.Shebang^-1 * tllexer.Skip * lpeg.V("Chunk") * tllexer.try(-1, "Stat");
|
||||
-- type language
|
||||
Type = lpeg.V("NilableType");
|
||||
NilableType = lpeg.V("UnionType") * tllexer.symb("?")^-1;
|
||||
UnionType = lpeg.V("PrimaryType") * (tllexer.symb("|") * tllexer.try(lpeg.V("PrimaryType"), "UnionType"))^0;
|
||||
PrimaryType = lpeg.V("LiteralType") +
|
||||
lpeg.V("BaseType") +
|
||||
lpeg.V("NilType") +
|
||||
lpeg.V("ValueType") +
|
||||
lpeg.V("AnyType") +
|
||||
lpeg.V("SelfType") +
|
||||
lpeg.V("FunctionType") +
|
||||
lpeg.V("TableType") +
|
||||
lpeg.V("VariableType");
|
||||
LiteralType = tllexer.token("false", "false") +
|
||||
tllexer.token("true", "true") +
|
||||
tllexer.token(tllexer.Number, "Number") +
|
||||
tllexer.token(tllexer.String, "String");
|
||||
BaseType = tllexer.token("boolean", "boolean") +
|
||||
tllexer.token("number", "number") +
|
||||
tllexer.token("string", "string") +
|
||||
tllexer.token("integer", "integer");
|
||||
NilType = tllexer.token("nil", "nil");
|
||||
ValueType = tllexer.token("value", "value");
|
||||
AnyType = tllexer.token("any", "any");
|
||||
SelfType = tllexer.token("self", "self");
|
||||
FunctionType = lpeg.V("InputType") * tllexer.symb("->") * tllexer.try(lpeg.V("NilableTuple"), "FunctionType");
|
||||
MethodType = lpeg.V("InputType") * tllexer.symb("=>") * tllexer.try(lpeg.V("NilableTuple"), "MethodType");
|
||||
InputType = tllexer.symb("(") * lpeg.V("TupleType")^-1 * tllexer.try(tllexer.symb(")"), "MissingCP");
|
||||
NilableTuple = lpeg.V("UnionlistType") * tllexer.symb("?")^-1;
|
||||
UnionlistType = lpeg.V("OutputType") * (tllexer.symb("|") * tllexer.try(lpeg.V("OutputType"), "UnionType"))^0;
|
||||
OutputType = tllexer.symb("(") * lpeg.V("TupleType")^-1 * tllexer.try(tllexer.symb(")"), "MissingCP");
|
||||
TupleType = lpeg.V("Type") * (tllexer.symb(",") * tllexer.try(lpeg.V("Type"), "TupleType"))^0 * tllexer.symb("*")^-1;
|
||||
TableType = tllexer.symb("{") * lpeg.V("TableTypeBody")^-1 * tllexer.try(tllexer.symb("}"), "MissingCC");
|
||||
TableTypeBody = lpeg.V("RecordType") +
|
||||
lpeg.V("HashType") +
|
||||
lpeg.V("ArrayType");
|
||||
RecordType = lpeg.V("RecordField") * (tllexer.symb(",") * lpeg.V("RecordField"))^0 *
|
||||
(tllexer.symb(",") * (lpeg.V("HashType") + lpeg.V("ArrayType")))^-1;
|
||||
RecordField = tllexer.kw("const")^-1 *
|
||||
lpeg.V("LiteralType") * tllexer.symb(":") * tllexer.try(lpeg.V("Type"), "Type");
|
||||
HashType = lpeg.V("KeyType") * tllexer.symb(":") * tllexer.try(lpeg.V("FieldType"), "Type");
|
||||
ArrayType = lpeg.V("FieldType");
|
||||
KeyType = lpeg.V("BaseType") + lpeg.V("ValueType") + lpeg.V("AnyType");
|
||||
FieldType = lpeg.V("Type");
|
||||
VariableType = tllexer.token(tllexer.Name, "Name");
|
||||
RetType = lpeg.V("NilableTuple") +
|
||||
lpeg.V("Type");
|
||||
Id = tllexer.token(tllexer.Name, "Name");
|
||||
TypeDecId = (tllexer.kw("const") * lpeg.V("Id")) +
|
||||
lpeg.V("Id");
|
||||
IdList = lpeg.V("TypeDecId") * (tllexer.symb(",") * tllexer.try(lpeg.V("TypeDecId"), "TupleType"))^0;
|
||||
IdDec = lpeg.V("IdList") * tllexer.symb(":") * tllexer.try((lpeg.V("Type") + lpeg.V("MethodType")), "Type");
|
||||
IdDecList = (lpeg.V("IdDec")^1)^-1;
|
||||
TypeDec = tllexer.token(tllexer.Name, "Name") * lpeg.V("IdDecList") * tllexer.try(tllexer.kw("end"), "TypeDecEnd");
|
||||
Interface = tllexer.kw("interface") * lpeg.V("TypeDec") +
|
||||
tllexer.kw("typealias") *
|
||||
tllexer.try(tllexer.token(tllexer.Name, "Name"), "TypeAliasName") *
|
||||
tllexer.try(tllexer.symb("="), "MissingEqTypeAlias") * lpeg.V("Type");
|
||||
-- parser
|
||||
Chunk = lpeg.V("Block");
|
||||
StatList = (tllexer.symb(";") + lpeg.V("Stat"))^0;
|
||||
Var = lpeg.V("Id");
|
||||
TypedId = tllexer.token(tllexer.Name, "Name") * (tllexer.symb(":") * tllexer.try(lpeg.V("Type"), "Type"))^-1;
|
||||
FunctionDef = tllexer.kw("function") * lpeg.V("FuncBody");
|
||||
FieldSep = tllexer.symb(",") + tllexer.symb(";");
|
||||
Field = ((tllexer.symb("[") * lpeg.V("Expr") * tllexer.try(tllexer.symb("]"), "MissingCB")) +
|
||||
(tllexer.token(tllexer.Name, "Name"))) *
|
||||
tllexer.symb("=") * lpeg.V("Expr") +
|
||||
lpeg.V("Expr");
|
||||
TField = (tllexer.kw("const") * lpeg.V("Field")) +
|
||||
lpeg.V("Field");
|
||||
FieldList = (lpeg.V("TField") * (lpeg.V("FieldSep") * lpeg.V("TField"))^0 *
|
||||
lpeg.V("FieldSep")^-1)^-1;
|
||||
Constructor = tllexer.symb("{") * lpeg.V("FieldList") * tllexer.try(tllexer.symb("}"), "MissingCC");
|
||||
NameList = lpeg.V("TypedId") * (tllexer.symb(",") * lpeg.V("TypedId"))^0;
|
||||
ExpList = lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^0;
|
||||
FuncArgs = tllexer.symb("(") *
|
||||
(lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^0)^-1 *
|
||||
tllexer.try(tllexer.symb(")"), "MissingCP") +
|
||||
lpeg.V("Constructor") +
|
||||
tllexer.token(tllexer.String, "String");
|
||||
OrOp = tllexer.kw("or");
|
||||
AndOp = tllexer.kw("and");
|
||||
RelOp = tllexer.symb("~=") +
|
||||
tllexer.symb("==") +
|
||||
tllexer.symb("<=") +
|
||||
tllexer.symb(">=") +
|
||||
tllexer.symb("<") +
|
||||
tllexer.symb(">");
|
||||
BOrOp = tllexer.symb("|");
|
||||
BXorOp = tllexer.symb("~") * -lpeg.P("=");
|
||||
BAndOp = tllexer.symb("&");
|
||||
ShiftOp = tllexer.symb("<<") +
|
||||
tllexer.symb(">>");
|
||||
ConOp = tllexer.symb("..");
|
||||
AddOp = tllexer.symb("+") +
|
||||
tllexer.symb("-");
|
||||
MulOp = tllexer.symb("*") +
|
||||
tllexer.symb("//") +
|
||||
tllexer.symb("/") +
|
||||
tllexer.symb("%");
|
||||
UnOp = tllexer.kw("not") +
|
||||
tllexer.symb("-") +
|
||||
tllexer.symb("~") +
|
||||
tllexer.symb("#");
|
||||
PowOp = tllexer.symb("^");
|
||||
Expr = lpeg.V("SubExpr_1");
|
||||
SubExpr_1 = chainl1(lpeg.V("SubExpr_2"), lpeg.V("OrOp"), "SubExpr_1");
|
||||
SubExpr_2 = chainl1(lpeg.V("SubExpr_3"), lpeg.V("AndOp"), "SubExpr_2");
|
||||
SubExpr_3 = chainl1(lpeg.V("SubExpr_4"), lpeg.V("RelOp"), "SubExpr_3");
|
||||
SubExpr_4 = chainl1(lpeg.V("SubExpr_5"), lpeg.V("BOrOp"), "SubExpr_4");
|
||||
SubExpr_5 = chainl1(lpeg.V("SubExpr_6"), lpeg.V("BXorOp"), "SubExpr_5");
|
||||
SubExpr_6 = chainl1(lpeg.V("SubExpr_7"), lpeg.V("BAndOp"), "SubExpr_6");
|
||||
SubExpr_7 = chainl1(lpeg.V("SubExpr_8"), lpeg.V("ShiftOp"), "SubExpr_7");
|
||||
SubExpr_8 = lpeg.V("SubExpr_9") * lpeg.V("ConOp") * tllexer.try(lpeg.V("SubExpr_8"), "SubExpr_8") +
|
||||
lpeg.V("SubExpr_9");
|
||||
SubExpr_9 = chainl1(lpeg.V("SubExpr_10"), lpeg.V("AddOp"), "SubExpr_9");
|
||||
SubExpr_10 = chainl1(lpeg.V("SubExpr_11"), lpeg.V("MulOp"), "SubExpr_10");
|
||||
SubExpr_11 = lpeg.V("UnOp") * tllexer.try(lpeg.V("SubExpr_11"), "SubExpr_11") +
|
||||
lpeg.V("SubExpr_12");
|
||||
SubExpr_12 = lpeg.V("SimpleExp") * (lpeg.V("PowOp") * tllexer.try(lpeg.V("SubExpr_11"), "SubExpr_12"))^-1;
|
||||
SimpleExp = tllexer.token(tllexer.Number, "Number") +
|
||||
tllexer.token(tllexer.String, "String") +
|
||||
tllexer.kw("nil") +
|
||||
tllexer.kw("false") +
|
||||
tllexer.kw("true") +
|
||||
tllexer.symb("...") +
|
||||
lpeg.V("FunctionDef") +
|
||||
lpeg.V("Constructor") +
|
||||
lpeg.V("SuffixedExp");
|
||||
SuffixedExp = lpeg.V("PrimaryExp") * (
|
||||
(tllexer.symb(".") * tllexer.try(tllexer.token(tllexer.Name, "Name"), "DotIndex")) / "index" +
|
||||
(tllexer.symb("[") * lpeg.V("Expr") * tllexer.try(tllexer.symb("]"), "MissingCB")) / "index" +
|
||||
(tllexer.symb(":") * tllexer.try(tllexer.token(tllexer.Name, "Name"), "MethodName") * tllexer.try(lpeg.V("FuncArgs"), "MethodCall")) / "call" +
|
||||
lpeg.V("FuncArgs") / "call")^0 / function (...) local l = {...}; return l[#l] end;
|
||||
PrimaryExp = lpeg.V("Var") / "var" +
|
||||
tllexer.symb("(") * lpeg.V("Expr") * tllexer.try(tllexer.symb(")"), "MissingCP");
|
||||
Block = lpeg.V("StatList") * lpeg.V("RetStat")^-1;
|
||||
IfStat = tllexer.kw("if") * lpeg.V("Expr") * tllexer.try(tllexer.kw("then"), "Then") * lpeg.V("Block") *
|
||||
(tllexer.kw("elseif") * tllexer.try(lpeg.V("Expr"), "ElseIf") * tllexer.try(tllexer.kw("then"), "Then") * lpeg.V("Block"))^0 *
|
||||
(tllexer.kw("else") * lpeg.V("Block"))^-1 *
|
||||
tllexer.try(tllexer.kw("end"), "IfEnd");
|
||||
WhileStat = tllexer.kw("while") * lpeg.V("Expr") *
|
||||
tllexer.try(tllexer.kw("do"), "WhileDo") * lpeg.V("Block") * tllexer.try(tllexer.kw("end"), "WhileEnd");
|
||||
DoStat = tllexer.kw("do") * lpeg.V("Block") * tllexer.try(tllexer.kw("end"), "BlockEnd");
|
||||
ForBody = tllexer.try(tllexer.kw("do"), "ForDo") * lpeg.V("Block");
|
||||
ForNum = lpeg.V("Id") * tllexer.symb("=") * lpeg.V("Expr") * tllexer.symb(",") *
|
||||
lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^-1 *
|
||||
lpeg.V("ForBody");
|
||||
ForGen = lpeg.V("NameList") * tllexer.try(tllexer.kw("in"), "ForGen") *
|
||||
lpeg.V("ExpList") * lpeg.V("ForBody");
|
||||
ForStat = tllexer.kw("for") * (lpeg.V("ForNum") + lpeg.V("ForGen")) * tllexer.try(tllexer.kw("end"), "ForEnd");
|
||||
RepeatStat = tllexer.kw("repeat") * lpeg.V("Block") *
|
||||
tllexer.try(tllexer.kw("until"), "Until") * lpeg.V("Expr");
|
||||
FuncName = lpeg.V("Id") * (tllexer.symb(".") *
|
||||
(tllexer.token(tllexer.Name, "Name")))^0 *
|
||||
(tllexer.symb(":") * (tllexer.token(tllexer.Name, "Name")))^-1;
|
||||
ParList = lpeg.V("NameList") * (tllexer.symb(",") * tllexer.try(lpeg.V("TypedVarArg"), "ParList"))^-1 +
|
||||
lpeg.V("TypedVarArg");
|
||||
TypedVarArg = tllexer.symb("...") * (tllexer.symb(":") * tllexer.try(lpeg.V("Type"), "Type"))^-1;
|
||||
FuncBody = tllexer.try(tllexer.symb("("), "MissingOP") * lpeg.V("ParList")^-1 * tllexer.try(tllexer.symb(")"), "MissingCP") *
|
||||
(tllexer.symb(":") * tllexer.try(lpeg.V("RetType"), "Type"))^-1 *
|
||||
lpeg.V("Block") * tllexer.try(tllexer.kw("end"), "FuncEnd");
|
||||
FuncStat = tllexer.kw("const")^-1 *
|
||||
tllexer.kw("function") * lpeg.V("FuncName") * lpeg.V("FuncBody");
|
||||
LocalFunc = tllexer.kw("function") *
|
||||
tllexer.try(lpeg.V("Id"), "LocalFunc") * lpeg.V("FuncBody");
|
||||
LocalAssign = lpeg.V("NameList") * tllexer.symb("=") * tllexer.try(lpeg.V("ExpList"), "LocalAssign1") +
|
||||
lpeg.V("NameList") * (#(-tllexer.symb("=") * (lpeg.V("Stat") + -1)) * lpeg.P(true)) + lpeg.T(tlerror.labels["LocalAssign2"]);
|
||||
LocalStat = tllexer.kw("local") *
|
||||
(lpeg.V("LocalTypeDec") + lpeg.V("LocalFunc") + lpeg.V("LocalAssign"));
|
||||
LabelStat = tllexer.symb("::") * tllexer.try(tllexer.token(tllexer.Name, "Name"), "Label1") * tllexer.try(tllexer.symb("::"), "Label2");
|
||||
BreakStat = tllexer.kw("break");
|
||||
GoToStat = tllexer.kw("goto") * tllexer.token(tllexer.Name, "Name");
|
||||
RetStat = tllexer.kw("return") * tllexer.try(-lpeg.V("Stat"), "RetStat") *
|
||||
(lpeg.V("Expr") * (tllexer.symb(",") * lpeg.V("Expr"))^0)^-1 *
|
||||
tllexer.symb(";")^-1;
|
||||
TypeDecStat = lpeg.V("Interface");
|
||||
LocalTypeDec = lpeg.V("TypeDecStat");
|
||||
LVar = (tllexer.kw("const") * lpeg.V("SuffixedExp")) +
|
||||
lpeg.V("SuffixedExp");
|
||||
ExprStat = lpeg.Cmt(lpeg.V("LVar") * lpeg.V("Assignment"),
|
||||
function (s, i, ...)
|
||||
local l = {...}
|
||||
local i = 1
|
||||
while l[i] ~= "=" do
|
||||
local se = l[i]
|
||||
if se ~= "var" and se ~= "index" then return false end
|
||||
i = i + 1
|
||||
end
|
||||
return true
|
||||
end) +
|
||||
lpeg.Cmt(lpeg.V("SuffixedExp"),
|
||||
function (s, i, se)
|
||||
if se ~= "call" then return false end
|
||||
return true
|
||||
end);
|
||||
Assignment = ((tllexer.symb(",") * lpeg.V("LVar"))^1)^-1 * (tllexer.symb("=") / "=") * lpeg.V("ExpList");
|
||||
Stat = lpeg.V("IfStat") + lpeg.V("WhileStat") + lpeg.V("DoStat") + lpeg.V("ForStat") +
|
||||
lpeg.V("RepeatStat") + lpeg.V("FuncStat") + lpeg.V("LocalStat") +
|
||||
lpeg.V("LabelStat") + lpeg.V("BreakStat") + lpeg.V("GoToStat") +
|
||||
lpeg.V("TypeDecStat") + lpeg.V("ExprStat");
|
||||
}
|
||||
|
||||
local function lineno (s, i)
|
||||
if i == 1 then return 1, 1 end
|
||||
local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
|
||||
local r = #rest
|
||||
return 1 + num, r ~= 0 and r or 1
|
||||
end
|
||||
|
||||
function tlparser.parse (subject, filename, strict, integer)
|
||||
local errorinfo = {}
|
||||
lpeg.setmaxstack(1000)
|
||||
local ast, label, suffix = lpeg.match(G, subject, nil, errorinfo, strict, integer)
|
||||
if not ast then
|
||||
local line, col = lineno(subject, string.len(subject) - string.len(suffix))
|
||||
local error_msg = string.format("%s:%d:%d: ", filename, line, col)
|
||||
if label ~= 0 then
|
||||
error_msg = error_msg .. tlerror.errors[label].msg
|
||||
else
|
||||
local u = lpeg.match(lpeg.C(tllexer.OneWord) + lpeg.Cc("EOF"), subject, errorinfo.ffp)
|
||||
error_msg = error_msg .. string.format("unexpected '%s', expecting %s", u, errorinfo.expected)
|
||||
end
|
||||
return nil, error_msg
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return tlparser
|
Loading…
Add table
Add a link
Reference in a new issue