1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2025-10-27 17:59:30 +00:00
* Fixed HORRIBLE parsing bugs with short functions and right assignemnt
operators
* Allowed to omit then, do and end for some statements
* Fixed hexa numbers parsing
* Run the Lua 5.3 test suite through Candran, everything that should
work worked! Yay!

Lacks tests and README
This commit is contained in:
Étienne Fildadut 2017-08-31 19:17:34 +02:00
parent 724249555f
commit 70d3aba121
8 changed files with 369 additions and 95 deletions

View file

@ -27,13 +27,11 @@ for _, file in ipairs(args) do
local input = inputFile:read("*a") local input = inputFile:read("*a")
inputFile:close() inputFile:close()
if args.ast then
pp.dump(assert(parse(input)))
return
end
if args.chunkname == nil then
args.chunkname = file args.chunkname = file
if args.ast then
pp.dump(assert(parse(input, args.chunkname)))
return
end end
local out = input local out = input

View file

@ -10,7 +10,7 @@
#import("lib.lua-parser.parser") #import("lib.lua-parser.parser")
local candran = { local candran = {
VERSION = "0.5.0" VERSION = "0.6.0"
} }
--- Default options. --- Default options.
@ -124,7 +124,7 @@ end
function candran.compile(input, options={}) function candran.compile(input, options={})
options = util.merge(default, options) options = util.merge(default, options)
local ast, errmsg = parser.parse(input, "candran") local ast, errmsg = parser.parse(input, options.chunkname)
if not ast then if not ast then
error("Compiler: error while parsing file: "..errmsg) error("Compiler: error while parsing file: "..errmsg)

View file

@ -135,7 +135,7 @@ local function _()
return function(code, ast, options) return function(code, ast, options)
local lastInputPos = 1 local lastInputPos = 1
local prevLinePos = 1 local prevLinePos = 1
local lastSource = "nil" local lastSource = options["chunkname"] or "nil"
local lastLine = 1 local lastLine = 1
local indentLevel = 0 local indentLevel = 0
local function newline() local function newline()
@ -449,10 +449,10 @@ end,
return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword") return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword")
end, end,
["Goto"] = function(t) ["Goto"] = function(t)
return "goto " .. lua(t[1], "Id") return "goto " .. lua(t, "Id")
end, end,
["Label"] = function(t) ["Label"] = function(t)
return "::" .. lua(t[1], "Id") .. "::" return "::" .. lua(t, "Id") .. "::"
end, end,
["Return"] = function(t) ["Return"] = function(t)
local push = peek("push") local push = peek("push")
@ -509,7 +509,7 @@ if t[1][1] then
if t[1][1]["tag"] == "ParPair" then if t[1][1]["tag"] == "ParPair" then
local id = lua(t[1][1][1]) local id = lua(t[1][1][1])
indentLevel = indentLevel + (1) indentLevel = indentLevel + (1)
table["insert"](decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) table["insert"](decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][1][2]) .. " end")
indentLevel = indentLevel - (1) indentLevel = indentLevel - (1)
r = r .. (id) r = r .. (id)
else else
@ -616,7 +616,7 @@ end,
["IfExpr"] = function(t) ["IfExpr"] = function(t)
for i = 2, # t do for i = 2, # t do
local block = t[i] local block = t[i]
if block[# block]["tag"] == "Push" then if block[# block] and block[# block]["tag"] == "Push" then
block[# block]["tag"] = "Return" block[# block]["tag"] = "Return"
end end
end end
@ -694,7 +694,7 @@ local function _()
return function(code, ast, options) return function(code, ast, options)
local lastInputPos = 1 local lastInputPos = 1
local prevLinePos = 1 local prevLinePos = 1
local lastSource = "nil" local lastSource = options["chunkname"] or "nil"
local lastLine = 1 local lastLine = 1
local indentLevel = 0 local indentLevel = 0
local function newline() local function newline()
@ -1008,10 +1008,10 @@ end,
return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword") return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword")
end, end,
["Goto"] = function(t) ["Goto"] = function(t)
return "goto " .. lua(t[1], "Id") return "goto " .. lua(t, "Id")
end, end,
["Label"] = function(t) ["Label"] = function(t)
return "::" .. lua(t[1], "Id") .. "::" return "::" .. lua(t, "Id") .. "::"
end, end,
["Return"] = function(t) ["Return"] = function(t)
local push = peek("push") local push = peek("push")
@ -1068,7 +1068,7 @@ if t[1][1] then
if t[1][1]["tag"] == "ParPair" then if t[1][1]["tag"] == "ParPair" then
local id = lua(t[1][1][1]) local id = lua(t[1][1][1])
indentLevel = indentLevel + (1) indentLevel = indentLevel + (1)
table["insert"](decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) table["insert"](decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][1][2]) .. " end")
indentLevel = indentLevel - (1) indentLevel = indentLevel - (1)
r = r .. (id) r = r .. (id)
else else
@ -1175,7 +1175,7 @@ end,
["IfExpr"] = function(t) ["IfExpr"] = function(t)
for i = 2, # t do for i = 2, # t do
local block = t[i] local block = t[i]
if block[# block]["tag"] == "Push" then if block[# block] and block[# block]["tag"] == "Push" then
block[# block]["tag"] = "Return" block[# block]["tag"] = "Return"
end end
end end
@ -2178,7 +2178,7 @@ lpeg["locale"](lpeg)
local P, S, V = lpeg["P"], lpeg["S"], lpeg["V"] local P, S, V = lpeg["P"], lpeg["S"], lpeg["V"]
local C, Carg, Cb, Cc = lpeg["C"], lpeg["Carg"], lpeg["Cb"], lpeg["Cc"] local C, Carg, Cb, Cc = lpeg["C"], lpeg["Carg"], lpeg["Cb"], lpeg["Cc"]
local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg["Cf"], lpeg["Cg"], lpeg["Cmt"], lpeg["Cp"], lpeg["Cs"], lpeg["Ct"] local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg["Cf"], lpeg["Cg"], lpeg["Cmt"], lpeg["Cp"], lpeg["Cs"], lpeg["Ct"]
local Lc, T = lpeg["Lc"], lpeg["T"] local Rec, T = lpeg["Rec"], lpeg["T"]
local alpha, digit, alnum = lpeg["alpha"], lpeg["digit"], lpeg["alnum"] local alpha, digit, alnum = lpeg["alpha"], lpeg["digit"], lpeg["alnum"]
local xdigit = lpeg["xdigit"] local xdigit = lpeg["xdigit"]
local space = lpeg["space"] local space = lpeg["space"]
@ -2610,52 +2610,176 @@ return {
[2] = t2[1] [2] = t2[1]
} }
end end
local function fixAnonymousMethodParams(t1, t2) local function fixShortFunc(t)
if t1 == ":" then if t[1] == ":" then
t1 = t2 table["insert"](t[2], 1, {
table["insert"](t1, 1, {
["tag"] = "Id", ["tag"] = "Id",
"self" "self"
}) })
table["remove"](t, 1)
t["is_method"] = true
end end
return t1 t["is_short"] = true
return t
end end
local function statToExpr(t) local function statToExpr(t)
t["tag"] = t["tag"] .. "Expr" t["tag"] = t["tag"] .. "Expr"
return t return t
end end
local function fixStructure(t)
local i = 1
while i <= # t do
if type(t[i]) == "table" then
fixStructure(t[i])
for j = # t[i], 1, - 1 do
local stat = t[i][j]
if type(stat) == "table" and stat["move_up_block"] and stat["move_up_block"] > 0 then
table["remove"](t[i], j)
table["insert"](t, i + 1, stat)
if t["tag"] == "Block" or t["tag"] == "Do" then
stat["move_up_block"] = stat["move_up_block"] - 1
end
end
end
end
i = i + 1
end
return t
end
local function searchEndRec(block, isRecCall)
for i, stat in ipairs(block) do
if stat["tag"] == "Set" or stat["tag"] == "Push" or stat["tag"] == "Return" or stat["tag"] == "Local" or stat["tag"] == "Let" or stat["tag"] == "Localrec" then
local exprlist
if stat["tag"] == "Set" or stat["tag"] == "Local" or stat["tag"] == "Let" or stat["tag"] == "Localrec" then
exprlist = stat[# stat]
elseif stat["tag"] == "Push" or stat["tag"] == "Return" then
exprlist = stat
end
local last = exprlist[# exprlist]
if last["tag"] == "Function" and last["is_short"] and not last["is_method"] and # last[1] == 1 then
local p = i
for j, fstat in ipairs(last[2]) do
p = i + j
table["insert"](block, p, fstat)
if stat["move_up_block"] then
fstat["move_up_block"] = (fstat["move_up_block"] or 0) + stat["move_up_block"]
end
if block["is_singlestatblock"] then
fstat["move_up_block"] = (fstat["move_up_block"] or 0) + 1
end
end
exprlist[# exprlist] = last[1]
exprlist[# exprlist]["tag"] = "Paren"
if not isRecCall then
for j = p + 1, # block, 1 do
block[j]["move_up_block"] = (block[j]["move_up_block"] or 0) + 1
end
end
return block, i
elseif last["tag"]:match("Expr$") then
local r = searchEndRec({ last })
if r then
for j = 2, # r, 1 do
table["insert"](block, i + j - 1, r[j])
end
return block, i
end
elseif last["tag"] == "Function" then
local r = searchEndRec(last[2])
if r then
return block, i
end
end
elseif stat["tag"]:match("^If") or stat["tag"]:match("^While") or stat["tag"]:match("^Repeat") or stat["tag"]:match("^Do") or stat["tag"]:match("^Fornum") or stat["tag"]:match("^Forin") then
local blocks
if stat["tag"]:match("^If") or stat["tag"]:match("^While") or stat["tag"]:match("^Repeat") or stat["tag"]:match("^Fornum") or stat["tag"]:match("^Forin") then
blocks = stat
elseif stat["tag"]:match("^Do") then
blocks = { stat }
end
for _, iblock in ipairs(blocks) do
if iblock["tag"] == "Block" then
local oldLen = # iblock
local newiBlock, newEnd = searchEndRec(iblock, true)
if newiBlock then
local p = i
for j = newEnd + (# iblock - oldLen) + 1, # iblock, 1 do
p = p + 1
table["insert"](block, p, iblock[j])
iblock[j] = nil
end
if not isRecCall then
for j = p + 1, # block, 1 do
block[j]["move_up_block"] = (block[j]["move_up_block"] or 0) + 1
end
end
return block, i
end
end
end
end
end
return nil
end
local function searchEnd(s, p, t)
local r = searchEndRec(fixStructure(t))
if not r then
return false
end
return true, r
end
local function expectBlockOrSingleStatWithStartEnd(start, startLabel, stopLabel, canFollow)
if canFollow then
return (- start * V("SingleStatBlock") * canFollow ^ - 1) + (expect(start, startLabel) * ((V("Block") * (canFollow + kw("end"))) + (Cmt(V("Block"), searchEnd) + throw(stopLabel))))
else
return (- start * V("SingleStatBlock")) + (expect(start, startLabel) * ((V("Block") * kw("end")) + (Cmt(V("Block"), searchEnd) + throw(stopLabel))))
end
end
local function expectBlockWithEnd(label)
return (V("Block") * kw("end")) + (Cmt(V("Block"), searchEnd) + throw(label))
end
local function maybeBlockWithEnd()
return (V("BlockNoErr") * kw("end")) + Cmt(V("BlockNoErr"), searchEnd)
end
local G = { local G = {
V("Lua"), V("Lua"),
["Lua"] = V("Shebang") ^ - 1 * V("Skip") * V("Block") * expect(P(- 1), "Extra"), ["Lua"] = (V("Shebang") ^ - 1 * V("Skip") * V("Block") * expect(P(- 1), "Extra")) / fixStructure,
["Shebang"] = P("#!") * (P(1) - P("\ ["Shebang"] = P("#!") * (P(1) - P("\
")) ^ 0, ")) ^ 0,
["Block"] = tagC("Block", V("Stat") ^ 0 * (V("RetStat") + V("ImplicitPushStat")) ^ - 1), ["Block"] = tagC("Block", (V("Stat") + - V("BlockEnd") * throw("InvalidStat")) ^ 0 * ((V("RetStat") + V("ImplicitPushStat")) * sym(";") ^ - 1) ^ - 1),
["Stat"] = V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat") + V("LocalStat") + V("FuncStat") + V("BreakStat") + V("LabelStat") + V("GoToStat") + V("FuncCall") + V("Assignment") + V("LetStat") + V("ContinueStat") + V("PushStat") + sym(";") + - V("BlockEnd") * throw("InvalidStat"), ["Stat"] = V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat") + V("LocalStat") + V("FuncStat") + V("BreakStat") + V("LabelStat") + V("GoToStat") + V("FuncCall") + V("Assignment") + V("LetStat") + V("ContinueStat") + V("PushStat") + sym(";"),
["BlockEnd"] = P("return") + "end" + "elseif" + "else" + "until" + "]" + - 1 + V("ImplicitPushStat"), ["BlockEnd"] = P("return") + "end" + "elseif" + "else" + "until" + "]" + - 1 + V("ImplicitPushStat") + V("Assignment"),
["IfStat"] = tagC("If", V("IfPart") * V("ElseIfPart") ^ 0 * V("ElsePart") ^ - 1 * expect(kw("end"), "EndIf")), ["SingleStatBlock"] = tagC("Block", V("Stat") + V("RetStat") + V("ImplicitPushStat")) / function(t)
["IfPart"] = kw("if") * expect(V("Expr"), "ExprIf") * expect(kw("then"), "ThenIf") * V("Block"), t["is_singlestatblock"] = true
["ElseIfPart"] = kw("elseif") * expect(V("Expr"), "ExprEIf") * expect(kw("then"), "ThenEIf") * V("Block"), return t
["ElsePart"] = kw("else") * V("Block"), end,
["DoStat"] = kw("do") * V("Block") * expect(kw("end"), "EndDo") / tagDo, ["BlockNoErr"] = tagC("Block", V("Stat") ^ 0 * ((V("RetStat") + V("ImplicitPushStat")) * sym(";") ^ - 1) ^ - 1),
["IfStat"] = tagC("If", V("IfPart")),
["IfPart"] = kw("if") * expect(V("Expr"), "ExprIf") * expectBlockOrSingleStatWithStartEnd(kw("then"), "ThenIf", "EndIf", V("ElseIfPart") + V("ElsePart")),
["ElseIfPart"] = kw("elseif") * expect(V("Expr"), "ExprEIf") * expectBlockOrSingleStatWithStartEnd(kw("then"), "ThenEIf", "EndIf", V("ElseIfPart") + V("ElsePart")),
["ElsePart"] = kw("else") * expectBlockWithEnd("EndIf"),
["DoStat"] = kw("do") * expectBlockWithEnd("EndDo") / tagDo,
["WhileStat"] = tagC("While", kw("while") * expect(V("Expr"), "ExprWhile") * V("WhileBody")), ["WhileStat"] = tagC("While", kw("while") * expect(V("Expr"), "ExprWhile") * V("WhileBody")),
["WhileBody"] = expect(kw("do"), "DoWhile") * V("Block") * expect(kw("end"), "EndWhile"), ["WhileBody"] = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoWhile", "EndWhile"),
["RepeatStat"] = tagC("Repeat", kw("repeat") * V("Block") * expect(kw("until"), "UntilRep") * expect(V("Expr"), "ExprRep")), ["RepeatStat"] = tagC("Repeat", kw("repeat") * V("Block") * expect(kw("until"), "UntilRep") * expect(V("Expr"), "ExprRep")),
["ForStat"] = kw("for") * expect(V("ForNum") + V("ForIn"), "ForRange") * expect(kw("end"), "EndFor"), ["ForStat"] = kw("for") * expect(V("ForNum") + V("ForIn"), "ForRange"),
["ForNum"] = tagC("Fornum", V("Id") * sym("=") * V("NumRange") * V("ForBody")), ["ForNum"] = tagC("Fornum", V("Id") * sym("=") * V("NumRange") * V("ForBody")),
["NumRange"] = expect(V("Expr"), "ExprFor1") * expect(sym(","), "CommaFor") * expect(V("Expr"), "ExprFor2") * (sym(",") * expect(V("Expr"), "ExprFor3")) ^ - 1, ["NumRange"] = expect(V("Expr"), "ExprFor1") * expect(sym(","), "CommaFor") * expect(V("Expr"), "ExprFor2") * (sym(",") * expect(V("Expr"), "ExprFor3")) ^ - 1,
["ForIn"] = tagC("Forin", V("NameList") * expect(kw("in"), "InFor") * expect(V("ExprList"), "EListFor") * V("ForBody")), ["ForIn"] = tagC("Forin", V("NameList") * expect(kw("in"), "InFor") * expect(V("ExprList"), "EListFor") * V("ForBody")),
["ForBody"] = expect(kw("do"), "DoFor") * V("Block"), ["ForBody"] = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoFor", "EndFor"),
["LocalStat"] = kw("local") * expect(V("LocalFunc") + V("LocalAssign"), "DefLocal"), ["LocalStat"] = kw("local") * expect(V("LocalFunc") + V("LocalAssign"), "DefLocal"),
["LocalFunc"] = tagC("Localrec", kw("function") * expect(V("Id"), "NameLFunc") * V("FuncBody")) / fixFuncStat, ["LocalFunc"] = tagC("Localrec", kw("function") * expect(V("Id"), "NameLFunc") * V("FuncBody")) / fixFuncStat,
["LocalAssign"] = tagC("Local", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))), ["LocalAssign"] = tagC("Local", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))),
["LetStat"] = kw("let") * expect(V("LetAssign"), "DefLet"), ["LetStat"] = kw("let") * expect(V("LetAssign"), "DefLet"),
["LetAssign"] = tagC("Let", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))), ["LetAssign"] = tagC("Let", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))),
["Assignment"] = tagC("Set", V("VarList") * V("BinOp") ^ - 1 * (sym("=") / "=") * V("BinOp") ^ - 1 * expect(V("ExprList"), "EListAssign")), ["Assignment"] = tagC("Set", V("VarList") * V("BinOp") ^ - 1 * (P("=") / "=") * V("BinOp") ^ - 1 * V("Skip") * expect(V("ExprList"), "EListAssign")),
["FuncStat"] = tagC("Set", kw("function") * expect(V("FuncName"), "FuncName") * V("FuncBody")) / fixFuncStat, ["FuncStat"] = tagC("Set", kw("function") * expect(V("FuncName"), "FuncName") * V("FuncBody")) / fixFuncStat,
["FuncName"] = Cf(V("Id") * (sym(".") * expect(V("StrId"), "NameFunc1")) ^ 0, insertIndex) * (sym(":") * expect(V("StrId"), "NameFunc2")) ^ - 1 / markMethod, ["FuncName"] = Cf(V("Id") * (sym(".") * expect(V("StrId"), "NameFunc1")) ^ 0, insertIndex) * (sym(":") * expect(V("StrId"), "NameFunc2")) ^ - 1 / markMethod,
["FuncBody"] = tagC("Function", V("FuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["FuncBody"] = tagC("Function", V("FuncParams") * expectBlockWithEnd("EndFunc")),
["FuncParams"] = expect(sym("("), "OParenPList") * V("ParList") * expect(sym(")"), "CParenPList"), ["FuncParams"] = expect(sym("("), "OParenPList") * V("ParList") * expect(sym(")"), "CParenPList"),
["ParList"] = V("NamedParList") * (sym(",") * expect(tagC("Dots", sym("...")), "ParList")) ^ - 1 / addDots + Ct(tagC("Dots", sym("..."))) + Ct(Cc()), ["ParList"] = V("NamedParList") * (sym(",") * expect(tagC("Dots", sym("...")), "ParList")) ^ - 1 / addDots + Ct(tagC("Dots", sym("..."))) + Ct(Cc()),
["ShortFuncDef"] = tagC("Function", V("ShortFuncParams") * maybeBlockWithEnd()) / fixShortFunc,
["ShortFuncParams"] = (sym(":") / ":") ^ - 1 * sym("(") * V("ParList") * sym(")"),
["NamedParList"] = tagC("NamedParList", commaSep(V("NamedPar"))), ["NamedParList"] = tagC("NamedParList", commaSep(V("NamedPar"))),
["NamedPar"] = tagC("ParPair", V("ParKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Id"), ["NamedPar"] = tagC("ParPair", V("ParKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Id"),
["ParKey"] = V("Id") * # ("=" * - P("=")), ["ParKey"] = V("Id") * # ("=" * - P("=")),
@ -2663,9 +2787,9 @@ V("Lua"),
["GoToStat"] = tagC("Goto", kw("goto") * expect(V("Name"), "Goto")), ["GoToStat"] = tagC("Goto", kw("goto") * expect(V("Name"), "Goto")),
["BreakStat"] = tagC("Break", kw("break")), ["BreakStat"] = tagC("Break", kw("break")),
["ContinueStat"] = tagC("Continue", kw("continue")), ["ContinueStat"] = tagC("Continue", kw("continue")),
["RetStat"] = tagC("Return", kw("return") * commaSep(V("Expr"), "RetList") ^ - 1 * sym(";") ^ - 1), ["RetStat"] = tagC("Return", kw("return") * commaSep(V("Expr"), "RetList") ^ - 1),
["PushStat"] = tagC("Push", kw("push") * commaSep(V("Expr"), "RetList") ^ - 1 * sym(";") ^ - 1), ["PushStat"] = tagC("Push", kw("push") * commaSep(V("Expr"), "RetList") ^ - 1),
["ImplicitPushStat"] = tagC("Push", commaSep(V("Expr"), "RetList") * sym(";") ^ - 1), ["ImplicitPushStat"] = tagC("Push", commaSep(V("Expr"), "RetList")),
["NameList"] = tagC("NameList", commaSep(V("Id"))), ["NameList"] = tagC("NameList", commaSep(V("Id"))),
["VarList"] = tagC("VarList", commaSep(V("VarExpr"))), ["VarList"] = tagC("VarList", commaSep(V("VarExpr"))),
["ExprList"] = tagC("ExpList", commaSep(V("Expr"), "ExprList")), ["ExprList"] = tagC("ExpList", commaSep(V("Expr"), "ExprList")),
@ -2682,7 +2806,7 @@ V("Lua"),
["MulExpr"] = chainOp(V("UnaryExpr"), V("MulOp"), "MulExpr"), ["MulExpr"] = chainOp(V("UnaryExpr"), V("MulOp"), "MulExpr"),
["UnaryExpr"] = V("UnaryOp") * expect(V("UnaryExpr"), "UnaryExpr") / unaryOp + V("PowExpr"), ["UnaryExpr"] = V("UnaryOp") * expect(V("UnaryExpr"), "UnaryExpr") / unaryOp + V("PowExpr"),
["PowExpr"] = V("SimpleExpr") * (V("PowOp") * expect(V("UnaryExpr"), "PowExpr")) ^ - 1 / binaryOp, ["PowExpr"] = V("SimpleExpr") * (V("PowOp") * expect(V("UnaryExpr"), "PowExpr")) ^ - 1 / binaryOp,
["SimpleExpr"] = tagC("Number", V("Number")) + tagC("String", V("String")) + tagC("Nil", kw("nil")) + tagC("Boolean", kw("false") * Cc(false)) + tagC("Boolean", kw("true") * Cc(true)) + tagC("Dots", sym("...")) + V("FuncDef") + V("Table") + V("SuffixedExpr") + V("TableCompr") + V("StatExpr"), ["SimpleExpr"] = tagC("Number", V("Number")) + tagC("String", V("String")) + tagC("Nil", kw("nil")) + tagC("Boolean", kw("false") * Cc(false)) + tagC("Boolean", kw("true") * Cc(true)) + tagC("Dots", sym("...")) + V("FuncDef") + V("Table") + V("ShortFuncDef") + V("SuffixedExpr") + V("TableCompr") + V("StatExpr"),
["StatExpr"] = (V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat")) / statToExpr, ["StatExpr"] = (V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat")) / statToExpr,
["FuncCall"] = Cmt(V("SuffixedExpr"), function(s, i, exp) ["FuncCall"] = Cmt(V("SuffixedExpr"), function(s, i, exp)
return exp["tag"] == "Call" or exp["tag"] == "Invoke", exp return exp["tag"] == "Call" or exp["tag"] == "Invoke", exp
@ -2696,9 +2820,7 @@ end),
["Call"] = tagC("Invoke", Cg(sym(":" * - P(":")) * expect(V("StrId"), "NameMeth") * expect(V("FuncArgs"), "MethArgs"))) + tagC("Call", V("FuncArgs")), ["Call"] = tagC("Invoke", Cg(sym(":" * - P(":")) * expect(V("StrId"), "NameMeth") * expect(V("FuncArgs"), "MethArgs"))) + tagC("Call", V("FuncArgs")),
["SelfIndex"] = tagC("DotIndex", V("StrId")), ["SelfIndex"] = tagC("DotIndex", V("StrId")),
["SelfCall"] = tagC("Invoke", Cg(V("StrId") * V("FuncArgs"))), ["SelfCall"] = tagC("Invoke", Cg(V("StrId") * V("FuncArgs"))),
["ShortFuncDef"] = tagC("Function", V("ShortFuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["FuncDef"] = (kw("function") * V("FuncBody")),
["ShortFuncParams"] = (sym(":") / ":") ^ - 1 * sym("(") * V("ParList") * sym(")") / fixAnonymousMethodParams,
["FuncDef"] = (kw("function") * V("FuncBody")) + V("ShortFuncDef"),
["FuncArgs"] = sym("(") * commaSep(V("Expr"), "ArgList") ^ - 1 * expect(sym(")"), "CParenArgs") + V("Table") + tagC("String", V("String")), ["FuncArgs"] = sym("(") * commaSep(V("Expr"), "ArgList") ^ - 1 * expect(sym(")"), "CParenArgs") + V("Table") + tagC("String", V("String")),
["Table"] = tagC("Table", sym("{") * V("FieldList") ^ - 1 * expect(sym("}"), "CBraceTable")), ["Table"] = tagC("Table", sym("{") * V("FieldList") ^ - 1 * expect(sym("}"), "CBraceTable")),
["FieldList"] = sepBy(V("Field"), V("FieldSep")) * V("FieldSep") ^ - 1, ["FieldList"] = sepBy(V("Field"), V("FieldSep")) * V("FieldSep") ^ - 1,
@ -2721,11 +2843,13 @@ end + P("--") * (P(1) - P("\
["Ident"] = V("IdStart") * V("IdRest") ^ 0, ["Ident"] = V("IdStart") * V("IdRest") ^ 0,
["IdStart"] = alpha + P("_"), ["IdStart"] = alpha + P("_"),
["IdRest"] = alnum + P("_"), ["IdRest"] = alnum + P("_"),
["Number"] = token((V("Hex") + V("Float") + V("Int")) / tonumber), ["Number"] = token(C(V("Hex") + V("Float") + V("Int"))),
["Hex"] = (P("0x") + "0X") * expect(xdigit ^ 1, "DigitHex"), ["Hex"] = (P("0x") + "0X") * ((xdigit ^ 0 * V("DeciHex")) + (expect(xdigit ^ 1, "DigitHex") * V("DeciHex") ^ - 1)) * V("ExpoHex") ^ - 1,
["Float"] = V("Decimal") * V("Expo") ^ - 1 + V("Int") * V("Expo"), ["Float"] = V("Decimal") * V("Expo") ^ - 1 + V("Int") * V("Expo"),
["Decimal"] = digit ^ 1 * "." * digit ^ 0 + P(".") * - P(".") * expect(digit ^ 1, "DigitDeci"), ["Decimal"] = digit ^ 1 * "." * digit ^ 0 + P(".") * - P(".") * expect(digit ^ 1, "DigitDeci"),
["DeciHex"] = P(".") * xdigit ^ 0,
["Expo"] = S("eE") * S("+-") ^ - 1 * expect(digit ^ 1, "DigitExpo"), ["Expo"] = S("eE") * S("+-") ^ - 1 * expect(digit ^ 1, "DigitExpo"),
["ExpoHex"] = S("pP") * S("+-") ^ - 1 * expect(xdigit ^ 1, "DigitExpo"),
["Int"] = digit ^ 1, ["Int"] = digit ^ 1,
["String"] = token(V("ShortStr") + V("LongStr")), ["String"] = token(V("ShortStr") + V("LongStr")),
["ShortStr"] = P("\"") * Cs((V("EscSeq") + (P(1) - S("\"\ ["ShortStr"] = P("\"") * Cs((V("EscSeq") + (P(1) - S("\"\
@ -2782,7 +2906,7 @@ return parser
end end
local parser = _() or parser local parser = _() or parser
package["loaded"]["lib.lua-parser.parser"] = parser or true package["loaded"]["lib.lua-parser.parser"] = parser or true
local candran = { ["VERSION"] = "0.5.0" } local candran = { ["VERSION"] = "0.6.0" }
local default = { local default = {
["target"] = "lua53", ["target"] = "lua53",
["indentation"] = "", ["indentation"] = "",
@ -2872,7 +2996,7 @@ end
candran["compile"] = function(input, options) candran["compile"] = function(input, options)
if options == nil then options = {} end if options == nil then options = {} end
options = util["merge"](default, options) options = util["merge"](default, options)
local ast, errmsg = parser["parse"](input, "candran") local ast, errmsg = parser["parse"](input, options["chunkname"])
if not ast then if not ast then
error("Compiler: error while parsing file: " .. errmsg) error("Compiler: error while parsing file: " .. errmsg)
end end

View file

@ -2,7 +2,7 @@ return function(code, ast, options)
--- Line mapping --- Line mapping
local lastInputPos = 1 -- last token position in the input code local lastInputPos = 1 -- last token position in the input code
local prevLinePos = 1 -- last token position in the previous line of code in the input code local prevLinePos = 1 -- last token position in the previous line of code in the input code
local lastSource = "nil" -- last found code source name (from the original file) local lastSource = options.chunkname or "nil" -- last found code source name (from the original file)
local lastLine = 1 -- last found line number (from the original file) local lastLine = 1 -- last found line number (from the original file)
--- Newline management --- Newline management
@ -282,17 +282,17 @@ return function(code, ast, options)
end end
return r return r
end, end,
-- Localrec{ ident expr } -- Localrec{ {ident} {expr} }
Localrec = (t) Localrec = (t)
return "local function "..lua(t[1][1])..lua(t[2][1], "_functionWithoutKeyword") return "local function "..lua(t[1][1])..lua(t[2][1], "_functionWithoutKeyword")
end, end,
-- Goto{ <string> } -- Goto{ <string> }
Goto = (t) Goto = (t)
return "goto " .. lua(t[1], "Id") return "goto " .. lua(t, "Id")
end, end,
-- Label{ <string> } -- Label{ <string> }
Label = (t) Label = (t)
return "::" .. lua(t[1], "Id") .. "::" return "::" .. lua(t, "Id") .. "::"
end, end,
-- Return{ <expr*> } -- Return{ <expr*> }
Return = (t) Return = (t)
@ -347,7 +347,7 @@ return function(code, ast, options)
Boolean = (t) Boolean = (t)
return tostring(t[1]) return tostring(t[1])
end, end,
-- Number{ <number> } -- Number{ <string> }
Number = (t) Number = (t)
return tostring(t[1]) return tostring(t[1])
end, end,
@ -363,7 +363,7 @@ return function(code, ast, options)
if t[1][1].tag == "ParPair" then if t[1][1].tag == "ParPair" then
local id = lua(t[1][1][1]) local id = lua(t[1][1][1])
indentLevel += 1 indentLevel += 1
table.insert(decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) table.insert(decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][1][2]) .. " end")
indentLevel -= 1 indentLevel -= 1
r ..= id r ..= id
else else
@ -483,7 +483,7 @@ return function(code, ast, options)
IfExpr = (t) IfExpr = (t)
for i=2, #t do -- convert final pushes to returns for i=2, #t do -- convert final pushes to returns
local block = t[i] local block = t[i]
if block[#block].tag == "Push" then if block[#block] and block[#block].tag == "Push" then
block[#block].tag = "Return" block[#block].tag = "Return"
end end
end end

View file

@ -8,13 +8,11 @@ To be implemented, theese need to:
* be significantly useful compared to existing Candran/Lua code. * be significantly useful compared to existing Candran/Lua code.
* be useful without having to rewrite APIs specifically for Candran. Candran intends to make Lua easier, not supersede it. * be useful without having to rewrite APIs specifically for Candran. Candran intends to make Lua easier, not supersede it.
Example rejected ideas: Example currently rejected ideas:
* Python-style function decorators (implemented in Candran 0.1.0): * Python-style function decorators (implemented in Candran 0.1.0):
Useless 95% of the time because most Lua APIs applying something to a function are used like applySomething(someArg,func) instead of func=applySomething(someArg)(func). Useless 95% of the time because most Lua APIs applying something to a function are used like applySomething(someArg,func) instead of func=applySomething(someArg)(func).
This could be adapted, but this will mean unecessary named functions in the environment and it will only works when the decorator returns the functions. This could be adapted, but this will mean unecessary named functions in the environment and it will only works when the decorator returns the functions.
In this state, these didn't provide anothing useful over short anonymous functions. In this state, these didn't provide anothing useful over short anonymous functions.
* Making then and do optional:
This actually doesn't work because if condition then (doStuff)() end would become ambigous ("condition(doStuff)() then" or "condition then doStuff()"?).
* Whitespace significance. * Whitespace significance.
Doesn't fit with Lua's verbose keywords. Doesn't fit with Lua's verbose keywords.

View file

@ -16,7 +16,7 @@ stat:
| `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end | `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end
| `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2... | `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2...
| `Let{ {ident+} {expr+}? } -- let i1, i2... = e1, e2... | `Let{ {ident+} {expr+}? } -- let i1, i2... = e1, e2...
| `Localrec{ ident expr } -- only used for 'local function' | `Localrec{ {ident} {expr} } -- only used for 'local function'
| `Goto{ <string> } -- goto str | `Goto{ <string> } -- goto str
| `Label{ <string> } -- ::str:: | `Label{ <string> } -- ::str::
| `Return{ <expr*> } -- return e1, e2... | `Return{ <expr*> } -- return e1, e2...
@ -29,7 +29,7 @@ expr:
`Nil `Nil
| `Dots | `Dots
| `Boolean{ <boolean> } | `Boolean{ <boolean> }
| `Number{ <number> } | `Number{ <string> } -- we don't use convert to number to avoid losing precision when tostring()-ing it later
| `String{ <string> } | `String{ <string> }
| `Function{ { ( `ParPair{ Id expr } | `Id{ <string> } )* `Dots? } block } | `Function{ { ( `ParPair{ Id expr } | `Id{ <string> } )* `Dots? } block }
| `Table{ ( `Pair{ expr expr } | expr )* } | `Table{ ( `Pair{ expr expr } | expr )* }
@ -69,7 +69,7 @@ lpeg.locale(lpeg)
local P, S, V = lpeg.P, lpeg.S, lpeg.V local P, S, V = lpeg.P, lpeg.S, lpeg.V
local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc
local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
local Lc, T = lpeg.Lc, lpeg.T local Rec, T = lpeg.Rec, lpeg.T
local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum
local xdigit = lpeg.xdigit local xdigit = lpeg.xdigit
@ -273,48 +273,199 @@ local function makeIndexOrCall (t1, t2)
return { tag = "Index", pos = t1.pos, [1] = t1, [2] = t2[1] } return { tag = "Index", pos = t1.pos, [1] = t1, [2] = t2[1] }
end end
local function fixAnonymousMethodParams(t1, t2) local function fixShortFunc(t)
if t1 == ":" then if t[1] == ":" then -- self method
t1 = t2 table.insert(t[2], 1, { tag = "Id", "self" })
table.insert(t1, 1, { tag = "Id", "self" }) table.remove(t, 1)
t.is_method = true
end end
return t1 t.is_short = true
return t
end end
local function statToExpr(t) local function statToExpr(t) -- tag a StatExpr
t.tag = t.tag .. "Expr" t.tag = t.tag .. "Expr"
return t return t
end end
local function fixStructure(t) -- fix the AST structure if needed
local i = 1
while i <= #t do
if type(t[i]) == "table" then
fixStructure(t[i])
for j=#t[i], 1, -1 do
local stat = t[i][j]
if type(stat) == "table" and stat.move_up_block and stat.move_up_block > 0 then
table.remove(t[i], j)
table.insert(t, i+1, stat)
if t.tag == "Block" or t.tag == "Do" then
stat.move_up_block = stat.move_up_block - 1
end
end
end
end
i = i + 1
end
return t
end
local function searchEndRec(block, isRecCall) -- recursively search potential "end" keyword wrongly consumed by a short anonymous function on stat end (yeah, too late to change the syntax to something easier to parse)
for i, stat in ipairs(block) do
-- Non recursive statements
if stat.tag == "Set" or stat.tag == "Push" or stat.tag == "Return" or stat.tag == "Local" or stat.tag == "Let" or stat.tag == "Localrec" then
local exprlist
if stat.tag == "Set" or stat.tag == "Local" or stat.tag == "Let" or stat.tag == "Localrec" then
exprlist = stat[#stat]
elseif stat.tag == "Push" or stat.tag == "Return" then
exprlist = stat
end
local last = exprlist[#exprlist] -- last value in ExprList
-- Stuff parse shittily only for short function declaration which are not method and whith strictly one variable name between the parenthesis.
-- Otherwise it's invalid Lua anyway, so not my problem.
if last.tag == "Function" and last.is_short and not last.is_method and #last[1] == 1 then
local p = i
for j, fstat in ipairs(last[2]) do
p = i + j
table.insert(block, p, fstat) -- copy stats from func body to block
if stat.move_up_block then -- extracted stats inherit move_up_block from statement
fstat.move_up_block = (fstat.move_up_block or 0) + stat.move_up_block
end
if block.is_singlestatblock then -- if it's a single stat block, mark them to move them outside of the block
fstat.move_up_block = (fstat.move_up_block or 0) + 1
end
end
exprlist[#exprlist] = last[1] -- replace func with paren and expressions
exprlist[#exprlist].tag = "Paren"
if not isRecCall then -- if superfluous statements won't be moved in a next recursion, let fixStructure handle things
for j=p+1, #block, 1 do
block[j].move_up_block = (block[j].move_up_block or 0) + 1
end
end
return block, i
-- I lied, stuff can also be recursive here (StatExpr & Function)
elseif last.tag:match("Expr$") then
local r = searchEndRec({ last })
if r then
for j=2, #r, 1 do
table.insert(block, i+j-1, r[j]) -- move back superflous statements from our new table to our real block
end
return block, i
end
elseif last.tag == "Function" then
local r = searchEndRec(last[2])
if r then
return block, i
end
end
-- Recursive statements
elseif stat.tag:match("^If") or stat.tag:match("^While") or stat.tag:match("^Repeat") or stat.tag:match("^Do") or stat.tag:match("^Fornum") or stat.tag:match("^Forin") then
local blocks
if stat.tag:match("^If") or stat.tag:match("^While") or stat.tag:match("^Repeat") or stat.tag:match("^Fornum") or stat.tag:match("^Forin") then
blocks = stat
elseif stat.tag:match("^Do") then
blocks = { stat }
end
for _, iblock in ipairs(blocks) do
if iblock.tag == "Block" then -- blocks
local oldLen = #iblock
local newiBlock, newEnd = searchEndRec(iblock, true)
if newiBlock then -- if end in the block
local p = i
for j=newEnd+(#iblock-oldLen)+1, #iblock, 1 do -- move all statements after the newely added statements to the parent block
p = p + 1
table.insert(block, p, iblock[j])
iblock[j] = nil
end
if not isRecCall then -- if superfluous statements won't be moved in a next recursion, let fixStructure handle things
for j=p+1, #block, 1 do
block[j].move_up_block = (block[j].move_up_block or 0) + 1
end
end
return block, i
end
end
end
end
end
return nil
end
local function searchEnd(s, p, t) -- match time capture which try to restructure the AST to free an "end" for us
local r = searchEndRec(fixStructure(t))
if not r then
return false
end
return true, r
end
local function expectBlockOrSingleStatWithStartEnd(start, startLabel, stopLabel, canFollow) -- will try a SingleStat if start doesn't match
if canFollow then
return (-start * V"SingleStatBlock" * canFollow^-1)
+ (expect(start, startLabel) * ((V"Block" * (canFollow + kw("end")))
+ (Cmt(V"Block", searchEnd) + throw(stopLabel))))
else
return (-start * V"SingleStatBlock")
+ (expect(start, startLabel) * ((V"Block" * kw("end"))
+ (Cmt(V"Block", searchEnd) + throw(stopLabel))))
end
end
local function expectBlockWithEnd(label) -- can't work *optionnaly* with SingleStat unfortunatly
return (V"Block" * kw("end"))
+ (Cmt(V"Block", searchEnd) + throw(label))
end
local function maybeBlockWithEnd() -- same as above but don't error if it doesn't match
return (V"BlockNoErr" * kw("end"))
+ Cmt(V"BlockNoErr", searchEnd)
end
-- grammar -- grammar
local G = { V"Lua", local G = { V"Lua",
Lua = V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra"); Lua = (V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra")) / fixStructure;
Shebang = P"#!" * (P(1) - P"\n")^0; Shebang = P"#!" * (P(1) - P"\n")^0;
Block = tagC("Block", V"Stat"^0 * (V"RetStat" + V"ImplicitPushStat")^-1); Block = tagC("Block", (V"Stat" + -V"BlockEnd" * throw("InvalidStat"))^0 * ((V"RetStat" + V"ImplicitPushStat") * sym(";")^-1)^-1);
Stat = V"IfStat" + V"DoStat" + V"WhileStat" + V"RepeatStat" + V"ForStat" Stat = V"IfStat" + V"DoStat" + V"WhileStat" + V"RepeatStat" + V"ForStat"
+ V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat" + V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat"
+ V"FuncCall" + V"Assignment" + V"FuncCall" + V"Assignment"
+ V"LetStat" + V"ContinueStat" + V"PushStat" + V"LetStat" + V"ContinueStat" + V"PushStat"
+ sym(";") + -V"BlockEnd" * throw("InvalidStat"); + sym(";");
BlockEnd = P"return" + "end" + "elseif" + "else" + "until" + "]" + -1 + V"ImplicitPushStat"; BlockEnd = P"return" + "end" + "elseif" + "else" + "until" + "]" + -1 + V"ImplicitPushStat" + V"Assignment";
IfStat = tagC("If", V"IfPart" * V"ElseIfPart"^0 * V"ElsePart"^-1 * expect(kw("end"), "EndIf")); SingleStatBlock = tagC("Block", V"Stat" + V"RetStat" + V"ImplicitPushStat") / function(t) t.is_singlestatblock = true return t end;
IfPart = kw("if") * expect(V"Expr", "ExprIf") * expect(kw("then"), "ThenIf") * V"Block"; BlockNoErr = tagC("Block", V"Stat"^0 * ((V"RetStat" + V"ImplicitPushStat") * sym(";")^-1)^-1); -- used to check if something a valid block without throwing an error
ElseIfPart = kw("elseif") * expect(V"Expr", "ExprEIf") * expect(kw("then"), "ThenEIf") * V"Block";
ElsePart = kw("else") * V"Block";
DoStat = kw("do") * V"Block" * expect(kw("end"), "EndDo") / tagDo; IfStat = tagC("If", V"IfPart");
IfPart = kw("if") * expect(V"Expr", "ExprIf") * expectBlockOrSingleStatWithStartEnd(kw("then"), "ThenIf", "EndIf", V"ElseIfPart" + V"ElsePart");
ElseIfPart = kw("elseif") * expect(V"Expr", "ExprEIf") * expectBlockOrSingleStatWithStartEnd(kw("then"), "ThenEIf", "EndIf", V"ElseIfPart" + V"ElsePart");
ElsePart = kw("else") * expectBlockWithEnd("EndIf");
DoStat = kw("do") * expectBlockWithEnd("EndDo") / tagDo;
WhileStat = tagC("While", kw("while") * expect(V"Expr", "ExprWhile") * V"WhileBody"); WhileStat = tagC("While", kw("while") * expect(V"Expr", "ExprWhile") * V"WhileBody");
WhileBody = expect(kw("do"), "DoWhile") * V"Block" * expect(kw("end"), "EndWhile"); WhileBody = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoWhile", "EndWhile");
RepeatStat = tagC("Repeat", kw("repeat") * V"Block" * expect(kw("until"), "UntilRep") * expect(V"Expr", "ExprRep")); RepeatStat = tagC("Repeat", kw("repeat") * V"Block" * expect(kw("until"), "UntilRep") * expect(V"Expr", "ExprRep"));
ForStat = kw("for") * expect(V"ForNum" + V"ForIn", "ForRange") * expect(kw("end"), "EndFor"); ForStat = kw("for") * expect(V"ForNum" + V"ForIn", "ForRange");
ForNum = tagC("Fornum", V"Id" * sym("=") * V"NumRange" * V"ForBody"); ForNum = tagC("Fornum", V"Id" * sym("=") * V"NumRange" * V"ForBody");
NumRange = expect(V"Expr", "ExprFor1") * expect(sym(","), "CommaFor") *expect(V"Expr", "ExprFor2") NumRange = expect(V"Expr", "ExprFor1") * expect(sym(","), "CommaFor") *expect(V"Expr", "ExprFor2")
* (sym(",") * expect(V"Expr", "ExprFor3"))^-1; * (sym(",") * expect(V"Expr", "ExprFor3"))^-1;
ForIn = tagC("Forin", V"NameList" * expect(kw("in"), "InFor") * expect(V"ExprList", "EListFor") * V"ForBody"); ForIn = tagC("Forin", V"NameList" * expect(kw("in"), "InFor") * expect(V"ExprList", "EListFor") * V"ForBody");
ForBody = expect(kw("do"), "DoFor") * V"Block"; ForBody = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoFor", "EndFor");
LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal"); LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal");
LocalFunc = tagC("Localrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat; LocalFunc = tagC("Localrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat;
@ -323,17 +474,20 @@ local G = { V"Lua",
LetStat = kw("let") * expect(V"LetAssign", "DefLet"); LetStat = kw("let") * expect(V"LetAssign", "DefLet");
LetAssign = tagC("Let", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc()))); LetAssign = tagC("Let", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())));
Assignment = tagC("Set", V"VarList" * V"BinOp"^-1 * (sym("=") / "=") * V"BinOp"^-1 * expect(V"ExprList", "EListAssign")); Assignment = tagC("Set", V"VarList" * V"BinOp"^-1 * (P"=" / "=") * V"BinOp"^-1 * V"Skip" * expect(V"ExprList", "EListAssign"));
FuncStat = tagC("Set", kw("function") * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat; FuncStat = tagC("Set", kw("function") * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat;
FuncName = Cf(V"Id" * (sym(".") * expect(V"StrId", "NameFunc1"))^0, insertIndex) FuncName = Cf(V"Id" * (sym(".") * expect(V"StrId", "NameFunc1"))^0, insertIndex)
* (sym(":") * expect(V"StrId", "NameFunc2"))^-1 / markMethod; * (sym(":") * expect(V"StrId", "NameFunc2"))^-1 / markMethod;
FuncBody = tagC("Function", V"FuncParams" * V"Block" * expect(kw("end"), "EndFunc")); FuncBody = tagC("Function", V"FuncParams" * expectBlockWithEnd("EndFunc"));
FuncParams = expect(sym("("), "OParenPList") * V"ParList" * expect(sym(")"), "CParenPList"); FuncParams = expect(sym("("), "OParenPList") * V"ParList" * expect(sym(")"), "CParenPList");
ParList = V"NamedParList" * (sym(",") * expect(tagC("Dots", sym("...")), "ParList"))^-1 / addDots ParList = V"NamedParList" * (sym(",") * expect(tagC("Dots", sym("...")), "ParList"))^-1 / addDots
+ Ct(tagC("Dots", sym("..."))) + Ct(tagC("Dots", sym("...")))
+ Ct(Cc()); -- Cc({}) generates a bug since the {} would be shared across parses + Ct(Cc()); -- Cc({}) generates a bug since the {} would be shared across parses
ShortFuncDef = tagC("Function", V"ShortFuncParams" * maybeBlockWithEnd()) / fixShortFunc;
ShortFuncParams = (sym(":") / ":")^-1 * sym("(") * V"ParList" * sym(")");
NamedParList = tagC("NamedParList", commaSep(V"NamedPar")); NamedParList = tagC("NamedParList", commaSep(V"NamedPar"));
NamedPar = tagC("ParPair", V"ParKey" * expect(sym("="), "EqField") * expect(V"Expr", "ExprField")) NamedPar = tagC("ParPair", V"ParKey" * expect(sym("="), "EqField") * expect(V"Expr", "ExprField"))
+ V"Id"; + V"Id";
@ -343,10 +497,10 @@ local G = { V"Lua",
GoToStat = tagC("Goto", kw("goto") * expect(V"Name", "Goto")); GoToStat = tagC("Goto", kw("goto") * expect(V"Name", "Goto"));
BreakStat = tagC("Break", kw("break")); BreakStat = tagC("Break", kw("break"));
ContinueStat = tagC("Continue", kw("continue")); ContinueStat = tagC("Continue", kw("continue"));
RetStat = tagC("Return", kw("return") * commaSep(V"Expr", "RetList")^-1 * sym(";")^-1); RetStat = tagC("Return", kw("return") * commaSep(V"Expr", "RetList")^-1);
PushStat = tagC("Push", kw("push") * commaSep(V"Expr", "RetList")^-1 * sym(";")^-1); PushStat = tagC("Push", kw("push") * commaSep(V"Expr", "RetList")^-1);
ImplicitPushStat = tagC("Push", commaSep(V"Expr", "RetList") * sym(";")^-1); ImplicitPushStat = tagC("Push", commaSep(V"Expr", "RetList"));
NameList = tagC("NameList", commaSep(V"Id")); NameList = tagC("NameList", commaSep(V"Id"));
VarList = tagC("VarList", commaSep(V"VarExpr")); VarList = tagC("VarList", commaSep(V"VarExpr"));
@ -375,6 +529,7 @@ local G = { V"Lua",
+ tagC("Dots", sym("...")) + tagC("Dots", sym("..."))
+ V"FuncDef" + V"FuncDef"
+ V"Table" + V"Table"
+ V"ShortFuncDef"
+ V"SuffixedExpr" + V"SuffixedExpr"
+ V"TableCompr" + V"TableCompr"
+ V"StatExpr"; + V"StatExpr";
@ -395,10 +550,7 @@ local G = { V"Lua",
SelfIndex = tagC("DotIndex", V"StrId"); SelfIndex = tagC("DotIndex", V"StrId");
SelfCall = tagC("Invoke", Cg(V"StrId" * V"FuncArgs")); SelfCall = tagC("Invoke", Cg(V"StrId" * V"FuncArgs"));
ShortFuncDef = tagC("Function", V"ShortFuncParams" * V"Block" * expect(kw("end"), "EndFunc")); FuncDef = (kw("function") * V"FuncBody");
ShortFuncParams = (sym(":") / ":")^-1 * sym("(") * V"ParList" * sym(")") / fixAnonymousMethodParams;
FuncDef = (kw("function") * V"FuncBody") + V"ShortFuncDef";
FuncArgs = sym("(") * commaSep(V"Expr", "ArgList")^-1 * expect(sym(")"), "CParenArgs") FuncArgs = sym("(") * commaSep(V"Expr", "ArgList")^-1 * expect(sym(")"), "CParenArgs")
+ V"Table" + V"Table"
+ tagC("String", V"String"); + tagC("String", V"String");
@ -433,13 +585,15 @@ local G = { V"Lua",
IdStart = alpha + P"_"; IdStart = alpha + P"_";
IdRest = alnum + P"_"; IdRest = alnum + P"_";
Number = token((V"Hex" + V"Float" + V"Int") / tonumber); Number = token(C(V"Hex" + V"Float" + V"Int"));
Hex = (P"0x" + "0X") * expect(xdigit^1, "DigitHex"); Hex = (P"0x" + "0X") * ((xdigit^0 * V"DeciHex") + (expect(xdigit^1, "DigitHex") * V"DeciHex"^-1)) * V"ExpoHex"^-1;
Float = V"Decimal" * V"Expo"^-1 Float = V"Decimal" * V"Expo"^-1
+ V"Int" * V"Expo"; + V"Int" * V"Expo";
Decimal = digit^1 * "." * digit^0 Decimal = digit^1 * "." * digit^0
+ P"." * -P"." * expect(digit^1, "DigitDeci"); + P"." * -P"." * expect(digit^1, "DigitDeci");
DeciHex = P"." * xdigit^0;
Expo = S"eE" * S"+-"^-1 * expect(digit^1, "DigitExpo"); Expo = S"eE" * S"+-"^-1 * expect(digit^1, "DigitExpo");
ExpoHex = S"pP" * S"+-"^-1 * expect(xdigit^1, "DigitExpo");
Int = digit^1; Int = digit^1;
String = token(V"ShortStr" + V"LongStr"); String = token(V"ShortStr" + V"LongStr");

View file

@ -1,6 +1,6 @@
package = "candran" package = "candran"
version = "0.5.0-1" version = "0.6.0-1"
description = { description = {
summary = "A simple Lua dialect and preprocessor.", summary = "A simple Lua dialect and preprocessor.",
@ -17,7 +17,7 @@ description = {
source = { source = {
url = "git://github.com/Reuh/candran", url = "git://github.com/Reuh/candran",
tag = "v0.5.0" tag = "v0.6.0"
} }
dependencies = { dependencies = {

View file

@ -5,8 +5,8 @@ version = "scm-1"
description = { description = {
summary = "A simple Lua dialect and preprocessor.", summary = "A simple Lua dialect and preprocessor.",
detailed = [[ detailed = [[
Candran is a dialect of the Lua 5.3 programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds a preprocessor and several useful syntax additions. Candran is a dialect of the Lua 5.3 programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds several useful syntax additions which aims to make Lua faster and easier to write, and a simple preprocessor.
Unlike Moonscript, Candran tries to stay close to the Lua syntax. Unlike Moonscript, Candran tries to stay close to the Lua syntax, and existing Lua code can run on Candran unmodified.
]], ]],
license = "MIT", license = "MIT",
homepage = "https://github.com/Reuh/candran", homepage = "https://github.com/Reuh/candran",