1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2026-02-04 02:08:40 +00:00

feat: implement global keyword in lua55 writer

This commit is contained in:
Étienne Fildadut 2025-11-24 19:11:06 +01:00
parent e2a1c51c2d
commit 73e3f95636
5 changed files with 62 additions and 13 deletions

View file

@ -1,5 +1,5 @@
--[[ --[[
This module implements a parser for Lua 5.3 with LPeg, This module implements a parser for Lua 5.5 with LPeg,
and generates an Abstract Syntax Tree that is similar to the one generated by Metalua. and generates an Abstract Syntax Tree that is similar to the one generated by Metalua.
For more information about Metalua, please, visit: For more information about Metalua, please, visit:
https://github.com/fab13n/metalua-parser https://github.com/fab13n/metalua-parser
@ -15,8 +15,11 @@ stat:
| `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end | `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end
| `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{ {attributeident+} {expr+}? } -- local i1, i2... = e1, e2... | `Local{ {attributeident+} {expr+}? } -- local i1, i2... = e1, e2...
| `Global{ {attributeident+} {expr+}? } -- global 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'
| `Globalrec{ {ident} {expr} } -- only used for 'global function'
| `GlobalAll{ attribute? } -- only used for 'global *'
| `Goto{ <string> } -- goto str | `Goto{ <string> } -- goto str
| `Label{ <string> } -- ::str:: | `Label{ <string> } -- ::str::
| `Return{ <expr*> } -- return e1, e2... | `Return{ <expr*> } -- return e1, e2...
@ -112,6 +115,7 @@ local labels = {
{ "ErrEListFor", "expected one or more expressions after 'in'" }, { "ErrEListFor", "expected one or more expressions after 'in'" },
{ "ErrDoFor", "expected 'do' after the range of the for loop" }, { "ErrDoFor", "expected 'do' after the range of the for loop" },
{ "ErrDefGlobal", "expected a function definition or assignment after global" },
{ "ErrDefLocal", "expected a function definition or assignment after local" }, { "ErrDefLocal", "expected a function definition or assignment after local" },
{ "ErrDefLet", "expected an assignment after let" }, { "ErrDefLet", "expected an assignment after let" },
{ "ErrDefClose", "expected an assignment after close" }, { "ErrDefClose", "expected an assignment after close" },
@ -340,10 +344,10 @@ 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) 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 for i, stat in ipairs(block) do
-- Non recursive statements -- 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 if stat.tag == "Set" or stat.tag == "Push" or stat.tag == "Return" or stat.tag == "Local" or stat.tag == "Let" or stat.tag == "Localrec" or stat.tag == "Global" or stat.tag == "Globalrec" then
local exprlist local exprlist
if stat.tag == "Set" or stat.tag == "Local" or stat.tag == "Let" or stat.tag == "Localrec" then if stat.tag == "Set" or stat.tag == "Local" or stat.tag == "Let" or stat.tag == "Localrec" or stat.tag == "Global" or stat.tag == "Globalrec" then
exprlist = stat[#stat] exprlist = stat[#stat]
elseif stat.tag == "Push" or stat.tag == "Return" then elseif stat.tag == "Push" or stat.tag == "Return" then
exprlist = stat exprlist = stat
@ -515,7 +519,7 @@ local G = { V"Lua",
Block = tagC("Block", (V"Stat" + -V"BlockEnd" * throw("InvalidStat"))^0 * ((V"RetStat" + V"ImplicitPushStat") * sym(";")^-1)^-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"GlobalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat"
+ V"LetStat" + V"ConstStat" + V"CloseStat" + V"LetStat" + V"ConstStat" + V"CloseStat"
+ V"FuncCall" + V"Assignment" + V"FuncCall" + V"Assignment"
+ V"ContinueStat" + V"PushStat" + V"ContinueStat" + V"PushStat"
@ -542,6 +546,12 @@ local G = { V"Lua",
ForIn = tagC("Forin", V"DestructuringNameList" * expect(kw("in"), "InFor") * expect(V"ExprList", "EListFor") * V"ForBody"); ForIn = tagC("Forin", V"DestructuringNameList" * expect(kw("in"), "InFor") * expect(V"ExprList", "EListFor") * V"ForBody");
ForBody = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoFor", "EndFor"); ForBody = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoFor", "EndFor");
GlobalStat = kw("global") * expect(V"GlobalFunc" + V"GlobalAssign", "DefGlobal");
GlobalFunc = tagC("Globalrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat;
GlobalAssign = tagC("Global", V"AttributeNameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())))
+ tagC("Global", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign"))
+ tagC("GlobalAll", V "Attribute"^-1 * sym("*")),
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"AttributeNameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) LocalAssign = tagC("Local", V"AttributeNameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())))
@ -551,9 +561,9 @@ local G = { V"Lua",
LetAssign = tagC("Let", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) LetAssign = tagC("Let", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())))
+ tagC("Let", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign")); + tagC("Let", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign"));
ConstStat = kw("const") * expect(V"AttributeAssign" / setAttribute("const"), "DefConst"); ConstStat = kw("const") * expect(V "LocalAssignNoAttribute" / setAttribute("const"), "DefConst"),
CloseStat = kw("close") * expect(V"AttributeAssign" / setAttribute("close"), "DefClose"); CloseStat = kw("close") * expect(V "LocalAssignNoAttribute" / setAttribute("close"), "DefClose"),
AttributeAssign = tagC("Local", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) LocalAssignNoAttribute = tagC("Local", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())))
+ tagC("Local", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign")); + tagC("Local", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign"));
Assignment = tagC("Set", (V"VarList" + V"DestructuringNameList") * V"BinOp"^-1 * (P"=" / "=") * ((V"BinOp" - P"-") + #(P"-" * V"Space") * V"BinOp")^-1 * V"Skip" * expect(V"ExprList", "EListAssign")); Assignment = tagC("Set", (V"VarList" + V"DestructuringNameList") * V"BinOp"^-1 * (P"=" / "=") * ((V"BinOp" - P"-") + #(P"-" * V"Space") * V"BinOp")^-1 * V"Skip" * expect(V"ExprList", "EListAssign"));
@ -677,7 +687,7 @@ local G = { V"Lua",
Reserved = V"Keywords" * -V"IdRest"; Reserved = V"Keywords" * -V"IdRest";
Keywords = P"and" + "break" + "do" + "elseif" + "else" + "end" Keywords = P"and" + "break" + "do" + "elseif" + "else" + "end"
+ "false" + "for" + "function" + "goto" + "if" + "in" + "false" + "for" + "function" + "goto" + "if" + "in"
+ "local" + "nil" + "not" + "or" + "repeat" + "return" + "local" + "global" + "nil" + "not" + "or" + "repeat" + "return"
+ "then" + "true" + "until" + "while"; + "then" + "true" + "until" + "while";
Ident = V"IdStart" * V"IdRest"^0; Ident = V"IdStart" * V"IdRest"^0;
IdStart = alpha + P"_"; IdStart = alpha + P"_";

View file

@ -245,7 +245,7 @@ function stm2str (stm)
str = str .. explist2str(stm[2]) .. ", " str = str .. explist2str(stm[2]) .. ", "
str = str .. block2str(stm[3]) str = str .. block2str(stm[3])
str = str .. " }" str = str .. " }"
elseif tag == "Local" then -- `Local{ {ident+} {expr+}? } elseif tag == "Local" or tag == "Global" then -- `Local|Global{ {ident+} {expr+}? }
str = str .. "{ " str = str .. "{ "
str = str .. varlist2str(stm[1]) str = str .. varlist2str(stm[1])
if #stm[2] > 0 then if #stm[2] > 0 then
@ -254,7 +254,7 @@ function stm2str (stm)
str = str .. ", " .. "{ }" str = str .. ", " .. "{ }"
end end
str = str .. " }" str = str .. " }"
elseif tag == "Localrec" then -- `Localrec{ ident expr } elseif tag == "Localrec" or tag == "Globalrec" then -- `Localrec|Globalrec{ ident expr }
str = str .. "{ " str = str .. "{ "
str = str .. "{ " .. var2str(stm[1][1]) .. " }, " str = str .. "{ " .. var2str(stm[1][1]) .. " }, "
str = str .. "{ " .. exp2str(stm[2][1]) .. " }" str = str .. "{ " .. exp2str(stm[2][1]) .. " }"

View file

@ -1,5 +1,9 @@
--[[ --[[
This module impements a validator for the AST This module impements a validator for the AST.
TODO/Checks that could be added in the future:
- Check if attributes are valid in declarations: in AttributeNameList, PrefixedAttributeNameList, and GlobalAll
- Check global variable declarations
]] ]]
local scope = require "candran.can-parser.scope" local scope = require "candran.can-parser.scope"
@ -395,10 +399,14 @@ function traverse_stm (env, stm)
elseif tag == "Forin" then -- `Forin{ {ident+} {expr+} block } elseif tag == "Forin" then -- `Forin{ {ident+} {expr+} block }
return traverse_forin(env, stm) return traverse_forin(env, stm)
elseif tag == "Local" or -- `Local{ {ident+} {expr+}? } elseif tag == "Local" or -- `Local{ {ident+} {expr+}? }
tag == "Let" then -- `Let{ {ident+} {expr+}? } tag == "Let" or -- `Let{ {ident+} {expr+}? }
tag == "Global" then -- `Global{ {ident+} {expr+}? }
return traverse_let(env, stm) return traverse_let(env, stm)
elseif tag == "Localrec" then -- `Localrec{ ident expr } elseif tag == "Localrec" or -- `Localrec{ ident expr }
tag == "Globalrec" then -- `Globalrec{ ident expr }
return traverse_letrec(env, stm) return traverse_letrec(env, stm)
elseif tag == "GlobalAll" then -- GlobalAll{ attribute? }
return true
elseif tag == "Goto" then -- `Goto{ <string> } elseif tag == "Goto" then -- `Goto{ <string> }
return traverse_goto(env, stm) return traverse_goto(env, stm)
elseif tag == "Label" then -- `Label{ <string> } elseif tag == "Label" then -- `Label{ <string> }

View file

@ -1,5 +1,15 @@
targetName = "Lua 5.4" targetName = "Lua 5.4"
tags.Global = (t)
error("NYI")
end
tags.Globalrec = (t)
error("NYI")
end
tags.GlobalAll = (t)
error("NYI")
end
#placeholder("patch") #placeholder("patch")
#local patch = output #local patch = output

View file

@ -471,6 +471,15 @@ return function(code, ast, options, macros={functions={}, variables={}})
end end
return r..DESTRUCTURING_ASSIGN(destructured) return r..DESTRUCTURING_ASSIGN(destructured)
end, end,
-- Global{ {attributeident+} {expr+}? }
Global = (t)
local destructured = {}
local r = "global "..push("destructuring", destructured)..lua(t[1])..pop("destructuring")
if t[2][1] then
r ..= " = "..lua(t[2], "_lhs")
end
return r..DESTRUCTURING_ASSIGN(destructured)
end,
-- Let{ {ident+} {expr+}? } -- Let{ {ident+} {expr+}? }
Let = (t) Let = (t)
local destructured = {} local destructured = {}
@ -489,6 +498,18 @@ return function(code, ast, options, macros={functions={}, variables={}})
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,
-- Globalrec{ {ident} {expr} }
Globalrec = (t)
return "global function "..lua(t[1][1])..lua(t[2][1], "_functionWithoutKeyword")
end,
-- GlobalAll{ attribute? }
GlobalAll = (t)
if #t == 1 then
return "global <" .. t[1] .. "> *"
else
return "global *"
end
end,
-- Goto{ <string> } -- Goto{ <string> }
Goto = (t) Goto = (t)
return "goto "..lua(t, "Id") return "goto "..lua(t, "Id")