Module:User:Cscott/mlua
return (function()
local builders = {}
local function register(name, f)
builders[name] = f
end
register('llpeg.lpegrex', function() return require [[Module:User:Cscott/lpegrex]] end)
register('mlua.lua', function(myrequire)
--[[
This grammar is based on Lua 5.4
As seen in https://www.lua.org/manual/5.4/manual.html#9
]]
local Grammar = [==[
chunk <-- SHEBANG? SKIP Block (!.)^UnexpectedSyntax
Block <== ( Label / Return / Break / Goto / Do / While / Repeat / If / ForNum / ForIn
/ FuncDef / FuncDecl / VarDecl / Assign / call / `;`)*
Label <== `::` @NAME @`::`
Return <== `return` exprlist?
Break <== `break`
Goto <== `goto` @NAME
Do <== `do` Block p2 @`end`
While <== `while` @expr p2 @`do` Block p3 @`end`
Repeat <== `repeat` Block p2 @`until` @expr
If <== `if` @expr p2 @`then` Block (`elseif` @expr @`then` Block)* (`else` Block)? p3 @`end`
ForNum <== `for` Id `=` @expr @`,` @expr ((`,` @expr) / $false) p2 (ForWith / $false) p3 @ForBody
ForIn <== `for` @idlist `in` @exprlist p2 (ForWith / $false) p3 @ForBody
ForWith <== `with` @Id @`,` @Id
ForBody <== `do` Block p2 (`then` Block p3 / $false) (`else` Block p4 / $false) @`end`
FuncDef <== `function` @funcname funcbody
FuncDecl <== `local` `function` @Id funcbody
VarDecl <== `local` @iddecllist (p2 `=` @exprlist)?
Assign <== varlist p2 `=` @exprlist
Number <== NUMBER->tonumber SKIP
String <== STRING SKIP
Boolean <== `false`->tofalse / `true`->totrue
Nil <== `nil`
Varargs <== `...`
Id <== NAME
IdDecl <== NAME (`<` @NAME @`>`)?
Function <== `function` $false funcbody
Table <== `{` (field (fieldsep field)* fieldsep?)? p2 @`}`
Paren <== `(` @expr p2 @`)`
Pair <== `[` @expr @`]` p2 @`=` @expr / NAME p2 `=` @expr
Call <== $false callargs
CallMethod <== `:` @NAME @callargs
DotIndex <== `.` @NAME
ColonIndex <== `:` @NAME
KeyIndex <== `[` @expr p2 @`]`
indexsuffix <-- DotIndex / KeyIndex
callsuffix <-- Call / CallMethod
p2 <-- {:p2:{}:}
p3 <-- {:p3:{}:}
p4 <-- {:p4:{}:}
var <-- (exprprimary (callsuffix+ indexsuffix / indexsuffix)+)~>rfoldright / Id
call <-- (exprprimary (indexsuffix+ callsuffix / callsuffix)+)~>rfoldright
exprsuffixed <-- (exprprimary (indexsuffix / callsuffix)*)~>rfoldright
funcname <-- (Id DotIndex* ColonIndex?)~>rfoldright
funcbody <-- @`(` funcargs p2 @`)` Block p3 @`end`
field <-- Pair / expr
fieldsep <-- `,` / `;`
callargs <-| `(` (expr (`,` @expr)*)? p2 @`)` / Table / String
idlist <-| Id (`,` @Id)*
iddecllist <-| IdDecl (`,` @IdDecl)*
funcargs <-| (Id (`,` Id)* (`,` Varargs)? / Varargs)?
exprlist <-| expr (`,` @expr)*
varlist <-| var (`,` @var)*
opor :BinaryOp <== `or`->'or' @exprand
opand :BinaryOp <== `and`->'and' @exprcmp
opcmp :BinaryOp <== (`==`->'eq' / `~=`->'ne' / `<=`->'le' / `>=`->'ge' / `<`->'lt' / `>`->'gt') @exprbor
opbor :BinaryOp <== `|`->'bor' @exprbxor
opbxor :BinaryOp <== `~`->'bxor' @exprband
opband :BinaryOp <== `&`->'band' @exprbshift
opbshift :BinaryOp <== (`<<`->'shl' / `>>`->'shr') @exprconcat
opconcat :BinaryOp <== `..`->'concat' @exprconcat
oparit :BinaryOp <== (`+`->'add' / `-`->'sub') @exprfact
opfact :BinaryOp <== (`*`->'mul' / `//`->'idiv' / `/`->'div' / `%`->'mod') @exprunary
oppow :BinaryOp <== `^`->'pow' @exprunary
opunary :UnaryOp <== (`not`->'not' / `#`->'len' / `-`->'unm' / `~`->'bnot') @exprunary
expr <-- expror
expror <-- (exprand opor*)~>foldleft
exprand <-- (exprcmp opand*)~>foldleft
exprcmp <-- (exprbor opcmp*)~>foldleft
exprbor <-- (exprbxor opbor*)~>foldleft
exprbxor <-- (exprband opbxor*)~>foldleft
exprband <-- (exprbshift opband*)~>foldleft
exprbshift <-- (exprconcat opbshift*)~>foldleft
exprconcat <-- (exprarit opconcat*)~>foldleft
exprarit <-- (exprfact oparit*)~>foldleft
exprfact <-- (exprunary opfact*)~>foldleft
exprunary <-- opunary / exprpow
exprpow <-- (exprsimple oppow*)~>foldleft
exprsimple <-- Nil / Boolean / Number / String / Varargs / Function / Table / exprsuffixed
exprprimary <-- Id / Paren
STRING <-- STRING_SHRT / STRING_LONG
STRING_LONG <-- {:LONG_OPEN {LONG_CONTENT} @LONG_CLOSE:}
STRING_SHRT <-- {:QUOTE_OPEN {~QUOTE_CONTENT~} @QUOTE_CLOSE:}
QUOTE_OPEN <-- {:qe: ['"] :} / '«' {:qe: '' -> '»' :} / '‹' {:qe: '' -> '›' :}
QUOTE_CONTENT <-- (ESCAPE_SEQ / !(QUOTE_CLOSE / LINEBREAK) .)*
QUOTE_CLOSE <-- =qe
ESCAPE_SEQ <-- '\'->'' @ESCAPE
ESCAPE <-- [\'"»] /
('n' $10 / 't' $9 / 'r' $13 / 'a' $7 / 'b' $8 / 'v' $11 / 'f' $12)->tochar /
('x' {HEX_DIGIT^2} $16)->tochar /
('u' '{' {HEX_DIGIT^+1} '}' $16)->toutf8char /
('z' SPACE*)->'' /
(DEC_DIGIT DEC_DIGIT^-1 !DEC_DIGIT / [012] DEC_DIGIT^2)->tochar /
(LINEBREAK $10)->tochar
NUMBER <-- {HEX_NUMBER / DEC_NUMBER}
HEX_NUMBER <-- '0' [xX] @HEX_PREFIX ([pP] @EXP_DIGITS)?
DEC_NUMBER <-- DEC_PREFIX ([eE] @EXP_DIGITS)?
HEX_PREFIX <-- HEX_DIGIT+ ('.' HEX_DIGIT*)? / '.' HEX_DIGIT+
DEC_PREFIX <-- DEC_DIGIT+ ('.' DEC_DIGIT*)? / '.' DEC_DIGIT+
EXP_DIGITS <-- [+-]? DEC_DIGIT+
COMMENT <-- '--' (COMMENT_LONG / COMMENT_SHRT)
COMMENT_LONG <-- (LONG_OPEN LONG_CONTENT @LONG_CLOSE)->0
COMMENT_SHRT <-- (!LINEBREAK .)*
LONG_CONTENT <-- (!LONG_CLOSE .)*
LONG_OPEN <-- '[' {:eq: '='*:} '[' LINEBREAK?
LONG_CLOSE <-- ']' =eq ']'
NAME <-- !KEYWORD {NAME_PREFIX NAME_SUFFIX?} SKIP
-- We should really accept a proper unicode set here for name characters,
-- but for the time being hack in some ISO-8859-1 characters
NAME_PREFIX <-- [_a-zA-ZÀ-ÿ]
NAME_SUFFIX <-- [_a-zA-Z0-9À-ÿ]+
SHEBANG <-- '#!' (!LINEBREAK .)* LINEBREAK?
SKIP <-- (SPACE+ / COMMENT)*
LINEBREAK <-- %cn %cr / %cr %cn / %cn / %cr
SPACE <-- %sp
HEX_DIGIT <-- [0-9a-fA-F]
DEC_DIGIT <-- [0-9]
EXTRA_TOKENS <-- `[[` `[=` `--` -- unused rule, here just to force defining these tokens
]==]
-- List of syntax errors
local SyntaxErrorLabels = {
["Expected_::"] = "unclosed label, did you forget `::`?",
["Expected_)"] = "unclosed parenthesis, did you forget a `)`?",
["Expected_>"] = "unclosed angle bracket, did you forget a `>`?",
["Expected_]"] = "unclosed square bracket, did you forget a `]`?",
["Expected_}"] = "unclosed curly brace, did you forget a `}`?",
["Expected_LONG_CLOSE"] = "unclosed long string or comment, did your forget a ']]'?",
["Expected_QUOTE_CLOSE"]= "unclosed short string or comment, did your forget a quote?",
["Expected_("] = "expected parenthesis token `(`",
["Expected_,"] = "expected comma token `,`",
["Expected_="] = "expected equals token `=`",
["Expected_callargs"] = "expected arguments",
["Expected_expr"] = "expected an expression",
["Expected_exprand"] = "expected an expression after operator",
["Expected_exprcmp"] = "expected an expression after operator",
["Expected_exprbor"] = "expected an expression after operator",
["Expected_exprbxor"] = "expected an expression after operator",
["Expected_exprband"] = "expected an expression after operator",
["Expected_exprbshift"] = "expected an expression after operator",
["Expected_exprconcat"] = "expected an expression after operator",
["Expected_exprfact"] = "expected an expression after operator",
["Expected_exprunary"] = "expected an expression after operator",
["Expected_exprlist"] = "expected expressions",
["Expected_funcname"] = "expected a function name",
["Expected_do"] = "expected `do` keyword to begin a statement block",
["Expected_end"] = "expected `end` keyword to close a statement block",
["Expected_then"] = "expected `then` keyword to begin a statement block",
["Expected_until"] = "expected `until` keyword to close repeat statement",
["Expected_ESCAPE"] = "malformed escape sequence",
["Expected_EXP_DIGITS"] = "malformed exponential number",
["Expected_HEX_PREFIX"] = "malformed hexadecimal number",
["Expected_Id"] = "expected an identifier name",
["Expected_NAME"] = "expected an identifier name",
["Expected_IdDecl"] = "expected an identifier name declaration",
["Expected_iddecllist"] = "expected identifiers names declaration",
["Expected_idlist"] = "expected identifiers names",
["Expected_var"] = "expected a variable",
["Expected_ForBody"] = "expected `with`, `do` or `if` keyword to begin a for-loop body",
["UnexpectedSyntax"] = "unexpected syntax",
}
-- Compile grammar
local lpegrex = myrequire('llpeg.lpegrex')
local function make_parse(defs)
local patt = lpegrex.compile(Grammar, defs)
-- Parse Lua source into an AST.
local function parse(source, name)
local ast, errlabel, errpos = patt:match(source)
if not ast then
name = name or '<source>'
local lineno, colno, line = lpegrex.calcline(source, errpos)
local colhelp = string.rep(' ', colno-1)..'^'
local errmsg = SyntaxErrorLabels[errlabel] or errlabel
error('syntax error: '..name..':'..lineno..':'..colno..': '..errmsg..
'\n'..line..'\n'..colhelp)
end
return ast
end
return parse
end
return {
make_parse = make_parse,
--parse = make_parse(),
}
end)
register('mlua.node', function(myrequire)
-- Parent class for expression nodes
local Expr = {}
Expr.__index = Expr
-- Parent class for literal expression nodes
local LiteralExpr = {}
LiteralExpr.__index = LiteralExpr
-- Parent class for statement nodes
local Stmt = {}
Stmt.__index = Stmt
-- Parent class for nodes which encode syntax (not an expression or statement)
local Syntax = {}
Syntax.__index = Syntax
-- Define node types and map to their parent class
local Node = {
Block = Stmt,
Label = Stmt,
Return = Stmt,
Break = Stmt,
Goto = Stmt,
Do = Stmt,
While = Stmt,
Repeat = Stmt,
If = Stmt,
ForNum = Stmt,
ForIn = Stmt,
ForWith = Syntax,
ForBody = Syntax,
FuncDef = Stmt,
FuncDecl = Stmt,
Function = Expr,
VarDecl = Stmt,
Assign = Stmt,
Number = LiteralExpr,
String = LiteralExpr,
Boolean = LiteralExpr,
Nil = LiteralExpr,
Varargs = Expr,
Id = Expr,
IdDecl = Syntax,
Table = Expr,
Paren = Expr,
Pair = Syntax,
Call = Expr,
CallMethod = Expr,
DotIndex = Expr,
ColonIndex = Syntax,
KeyIndex = Expr,
BinaryOp = Expr,
UnaryOp = Expr,
}
Node.__index = Node
setmetatable(Syntax, Node)
setmetatable(Stmt, Node)
setmetatable(Expr, Node)
setmetatable(LiteralExpr, Expr)
for tag, parent in pairs(Node) do
local val = {}
val.__index = val
setmetatable(val, parent)
val.__tostring = function(self)
local result = { tag, '{ ' }
for i,v in ipairs(self) do
if type(v) ~= 'table' or v.tag ~= nil then
table.insert(result, tostring(v))
else
table.insert(result, '{ ')
for j,vv in ipairs(v) do
table.insert(result, tostring(vv))
if v[j+1] ~= nil then
table.insert(result, ", ")
end
end
table.insert(result, ' }')
end
if self[i+1] ~= nil then
table.insert(result, ", ")
end
end
table.insert(result, ' }')
return table.concat(result)
end
Node[tag] = val
end
Node.__index = Node
Node.LiteralExpr = LiteralExpr
Node.Expr = Expr
Node.Syntax = Syntax
Node.Stmt = Stmt
local function make_node(tag, node)
setmetatable(node, Node[tag])
node.tag = tag
return node
end
return {
Node = Node,
make_node = make_node,
}
end)
register('advent.compat', function() return require [[Module:User:Cscott/compat]] end)
register('mlua.define', function(myrequire)
-- Human-friendly names for functions, for easier debugging/tracing.
local M = {}
local function_name_registry = {}
function M.register_fname(name, f)
assert(type(name) == "string")
assert(type(f) == "function")
function_name_registry[f] = name
end
local function report_ferror(f, msg)
local fname = function_name_registry[f]
if fname ~= nil then
msg = fname .. ": " .. msg
end
error(msg)
end
-- helper for visitor pattern definitions
function M.define(dispatch, which, f)
for _,v in pairs(which) do
assert(v ~= nil) -- catch typos
dispatch[v] = f
end
end
function M.define_ast(obj, funcname, tbl)
for keys,func in pairs(tbl) do
if type(keys) ~= "table" then
keys = { keys }
end
for _,v in pairs(keys) do
obj[v][funcname] = func
end
end
end
function M.define_ast_visitor(tbl)
local dispatch = {}
for keys,func in pairs(tbl) do
if type(keys) ~= "table" then
keys = { keys }
end
M.define(dispatch, keys, func)
end
local visit
visit = function(node, ...)
if node == nil then report_ferror(visit, "nil node") end
local a = dispatch["assert"]
if a ~= nil then a(node, ...) end -- assert preconditions
local f = dispatch[node.tag]
if f ~= nil then return f(node, ...) end
f = dispatch.default
if f == nil then
report_ferror(visit, "no default for " .. node.tag)
end
return f(node, ...)
end
return visit
end
return M
end)
register('mlua.eval', function(myrequire)
local L = myrequire('mlua.node').Node
local make_node = myrequire('mlua.node').make_node
local compat = myrequire('advent.compat')
local define = myrequire('mlua.define').define
local hasBit32, bit32 = pcall(require, 'bit32')
if not hasBit32 then bit32 = {} end
--[[ debugging options ]]--
-- when true, show char position of each statement executed
local TRACE=false
-- set false when testing to ensure general case of multires args handled
-- properly; the optimizations might otherwise cause the general case not
-- to be reached during testing.
local MULTIRES_OPT=true
local function ripairs(val)
local i = 1
while val[i] ~= nil do
i = i + 1
end
local f = function(_, i)
i = i - 1
if i == 0 then return nil end
return i, val[i]
end
return f, nil, i
end
local function makePushEnv(cont)
return function(env) return cont({ parent=env }) end
end
local function makePopEnv(cont)
return function(env) return cont(env.parent) end
end
local State = {}
State.__index = State
function State:new(parent)
local s = setmetatable({ labels={}, locals={}, oldLocals={}, localCount = 0, parent = parent }, self)
if parent == nil then
s.depth = 0
else
s.depth = 1 + parent.depth
end
if parent == nil then
s:defineLocal("_ENV") -- first top-level local is always for _ENV
elseif parent.breakCont ~= nil then
s.breakCont = makePopEnv(parent.breakCont)
end
return s
end
function State:newBlockState()
return State:new(self)
end
function State:pushLabel(name, cont)
self.labels[name] = cont
end
function State:lookupLabel(name)
local f = nil -- cache
local labels = self.labels
return function(env)
if f == nil then
-- deferred lookup of label, since it isn't yet defined when the
-- goto is compiled. But cache the result to reuse it next time.
f = labels[name]
labels = nil -- free up some memory
end
return f(env)
end
end
function State:setBreak(cont)
local old = self.breakCont
self.breakCont = cont
end
function State:lookupLocal(name)
local i = 0
local s = self
while s ~= nil do
local id = s.locals[name]
if id ~= nil then return i, id end
i = i + 1
s = s.parent
end
return nil, nil -- not found
end
function State:defineInvisibleLocal(undef)
if undef == nil then
self.localCount = self.localCount + 1
else
self.localCount = self.localCount - 1
end
return self.localCount
end
function State:defineLocal(name, undef)
if undef == nil then
self.localCount = self.localCount + 1
self.oldLocals[self.localCount] = self.locals[name] -- usually nil
self.locals[name] = self.localCount
else
assert(self.locals[name] == self.localCount)
self.locals[name] = self.oldLocals[self.localCount] -- usually nil
self.localCount = self.localCount - 1
end
return self.localCount
end
local Env = {}
Env.__index = Env
function Env:new()
return setmetatable({}, self)
end
function L:defineLocals()
error("missing defineLocals case for "..self.tag)
end
function L.Stmt:defineLocals()
-- we don't recurse into inner blocks; hence do nothing by default
end
function L.Expr:defineLocals()
-- expressions don't have declarations
end
function L.ForWith:defineLocals(state, undef)
if undef == nil then
for _,v in ipairs(self) do v:defineLocals(state, undef) end
return state.locals[self[1][1]], state.locals[self[2][1]]
else
for _,v in ripairs(self) do v:defineLocals(state, undef) end
end
end
function L.FuncDecl:defineLocals(state, undef)
self[1]:defineLocals(state, undef)
end
function L.VarDecl:defineLocals(state, undef)
if undef == nil then
for _,v in ipairs(self[1]) do v:defineLocals(state, undef) end
else
for _,v in ripairs(self[1]) do v:defineLocals(state, undef) end
end
end
function L.Id:defineLocals(state, undef)
state:defineLocal(self[1], undef)
end
function L.IdDecl:defineLocals(state, undef)
local name,attrib = self[1], self[2]
-- if attrib ~= nil then error("attributes unimplemented") end
state:defineLocal(name, undef)
end
function L:compile(state, cont)
error("Missing compiler for "..self.tag)
end
function L:compileExpr(state)
error("Missing expression compiler for "..self.tag)
end
function L:compileLhs(state)
error("Missing left-hand-side compiler for "..self.tag)
end
local withNewBlock = function(f)
return function(self, state, cont)
state = state:newBlockState()
return makePushEnv(f(self, state, makePopEnv(cont)))
end
end
function L.Block:compileInSameBlock(state, cont)
-- define all the locals in a forward pass
for _,node in ipairs(self) do
node:defineLocals(state)
end
-- XXX create continuation that clears/closes all these locals
-- create continuations in a backward pass
for _,node in ripairs(self) do
cont = node:compile(state, cont)
node:defineLocals(state, 'undef')
if TRACE then
local pos = node.pos
local cont2 = cont
cont = function(env)
if _G.mw and _G.mw.log then
_G.mw.log("Pos "..pos)
else
print("Pos", pos)
end
return cont2(env)
end
end
end
return cont
end
L.Block.compile = withNewBlock(L.Block.compileInSameBlock)
function L.Label:compile(state, cont)
state.pushLabel(self[1], cont)
return cont
end
function L.Return:compile(state, cont)
if self[1] == nil or #self[1] == 0 then
return function() return end -- ignore continuation, finally return
end
local t = {}
for _,v in ipairs(self[1]) do
table.insert(t, v:compileExpr(state))
end
-- special case for tail call
if #t == 1 then
-- tail call to expression evaluator, which ought to be a tail call to
-- the function being called; see Call:compileExpr()
return t[1]
elseif #t == 2 and MULTIRES_OPT then
-- fast path, uses lua itself to handle multires case of t2
local t1, t2 = t[1], t[2]
return function(env) return t1(env), t2(env) end
end
return function(env)
local r,n = {}, #t
for i=1,n-1 do
r[i] = t[i](env) -- evaluate each expression in order
end
-- handle multires expressions (n==0 case already handled above)
local last = compat.pack(t[n](env))
n = n - 1 -- we haven't stored the last item yet
for i=1,last.n do
n = n + 1
r[n] = last[i]
end
-- we use n instead of #r in case some of the values are null
return compat.unpack(r, 1, n) -- ignore continuation, return
end
end
function L.Break:compile(state, cont)
-- ignore continuation, execute from 'breakCont'
return state.breakCont
end
function L.Goto:compile(state, cont)
return state.lookupLabel(self[1]) -- use a different continuation
end
function L.Do:compile(state, cont)
return self[1]:compile(state, cont)
end
function L.While:compile(state, cont)
local expr = self[1]:compileExpr(state)
local body
local loop = function(env)
if expr(env) then return body(env) end
return cont(env)
end
local oldBreak = state:setBreak(cont)
body = self[2]:compile(state, loop)
state:setBreak(oldBreak)
return loop
end
function L.Repeat:compile(state, cont)
-- the test is executed in the same new block as the body
local condAst = make_node('If', { self[2], make_node("Break", {}) })
local oldBody = self[1]
assert(oldBody.tag == 'Block')
local newBody = make_node('Block', { pos=oldBody.pos, endpos=oldBody.endpos })
for i,v in ipairs(oldBody) do
newBody[i] = v
end
newBody[#newBody+1] = condAst
local body
local loop = function(env) return body(env) end
local oldBreak = state:setBreak(cont)
body = newBody:compile(state, loop)
state:setBreak(oldBreak)
return body
end
function L.If:compile(state, cont)
-- 1,2 = if/then
-- 1,2,3 = if/then/else
-- 1,2,3,4 = if/then/elseif/then
-- 1,2,3,4,5 = if/then/elseif/then/else
local n = 1
while self[n] ~= nil do
n = n + 2
end
local ifRest = cont
for i=n-2,1,-2 do
if self[i+1] == nil then -- final else clause
ifRest = self[i]:compile(state, cont)
else
local test = self[i]:compileExpr(state)
local thenStmt = self[i+1]:compile(state, cont)
local rest = ifRest;
ifRest = function(env)
if test(env) then return thenStmt(env) else return rest(env) end
end
end
end
return ifRest
end
-- If only 'then' is present, it executes after any normal completion
-- Otherwise, if 'else' is present, then 'then' only executes after
-- one (or more) successful iterations of the loop body
function makeFinalize(thenAst, elseAst, state, cont, first, idlist, idlistShadow)
if thenAst == false and elseAst == false then
return cont
end
local thenAssign = make_node("VarDecl", { idlist, idlistShadow })
local elseAssign = make_node("VarDecl", { idlist }) -- will nil out
-- only declare the idlist variables in a then block (including a
-- combined then/else block)
local thenFunc, elseFunc
if thenAst ~= false then
local thenState = state:newBlockState()
thenAssign:defineLocals(thenState)
local thenFuncCont = thenAst:compileInSameBlock(thenState, makePopEnv(cont))
thenFunc = thenAssign:compile(thenState, thenFuncCont)
-- this else func will be overwritten if actual else clause present
elseFunc = elseAssign:compile(thenState, thenFuncCont)
end
if elseAst ~= false then
local elseState = state:newBlockState()
elseFunc = elseAst:compileInSameBlock(elseState, makePopEnv(cont))
end
return function(env)
local nenv = { parent=env }
if env[first] == true then
return elseFunc(nenv)
elseif thenFunc ~= nil then
return thenFunc(nenv)
else
return cont(env)
end
end
end
function L.ForNum:compile(state, cont)
local name,start,stop,optStep,optWith,forBody = self[1],self[2],self[3],self[4],self[5],self[6]
local bodyAst, thenAst, elseAst = forBody[1], forBody[2], forBody[3]
local var, varExpr = state:defineInvisibleLocal(), start:compileExpr(state)
local limit, limitExpr = state:defineInvisibleLocal(), stop:compileExpr(state)
local step, stepExpr = state:defineInvisibleLocal(), function() return 1 end
local first = state:defineInvisibleLocal()
local last = state:defineInvisibleLocal()
local lastVarAst = make_node("Id", { "0var" }) -- see ForIn below
local lastVar = state:defineLocal("0var")
if optStep ~= false then
stepExpr = optStep:compileExpr(state)
end
local nstate = state:newBlockState()
local id = nstate:defineLocal(name[1])
local firstId, lastId
if optWith ~= false then
firstId, lastId = optWith:defineLocals(nstate)
end
local finalize = makeFinalize(thenAst, elseAst, state, cont, first,
{ name }, { lastVarAst })
local body
local loop = function(env)
if (env[step] > 0 and env[var] <= env[limit]) or (env[step] <= 0 and env[var] >= env[limit]) then
local nenv = { parent=env }
nenv[id] = env[var]
if env[step] > 0 then
env[last] = env[var] + env[step] > env[limit]
else
env[last] = env[var] + env[step] < env[limit]
end
if firstId ~= nil then nenv[firstId] = env[first] end
if lastId ~= nil then nenv[lastId] = env[last] end
return body(nenv)
end
return finalize(env)
end
local init = function(env)
env[var] = tonumber(varExpr(env))
env[limit] = tonumber(limitExpr(env))
env[step] = tonumber(stepExpr(env))
env[first] = true
if not (env[var] and env[limit] and env[step]) then error() end
return loop(env)
end
local incr = function(env)
env[lastVar] = env[var]
env[var] = env[var] + env[step]
env[first] = false
return loop(env)
end
local oldBreak = nstate:setBreak(makePopEnv(cont))
body = bodyAst:compileInSameBlock(nstate, makePopEnv(incr))
nstate:setBreak(oldBreak)
-- okay, now pop all those local vars off
if optWith ~= false then
optWith:defineLocals(nstate, "undef")
end
nstate:defineLocal(name[1], "undef")
state:defineLocal("0var", "undef") -- lastVar
state:defineInvisibleLocal("undef") -- last
state:defineInvisibleLocal("undef") -- first
state:defineInvisibleLocal("undef") -- step
state:defineInvisibleLocal("undef") -- limit
state:defineInvisibleLocal("undef") -- var
return init
end
--[[
To guide understanding of this method, here is the equivalent code for
for-in loop execution:
** WITHOUT A WITH CLAUSE **
do
-- init
local f', s', var_1', cl' = explist -- initAst / initFunc
first' = true
-- goto loop
while true do
-- loop:
local var_1, ···, var_n = f'(s', var_1') -- loopStart
if var_1 == nil then break end
var_1', ..., var_n' = var_1, ..., var_n -- save for then clause
block
-- goto loop
end
end
** WITH A WITH CLAUSE (maintaining 'last') **
do
-- init
local f', s', var_1', cl' = explist -- initAst / initFunc
local var_1', ···, var_n' = f'(s', var_1')
local first' = true
local last' = (var_1' == nil)
-- goto loop
-- loop:
while not last' do
first = first'
first' = false
var_1, ..., var_n = var_1', ..., var_n'
var_1', ···, var_n' = f'(s', var_1')
last' = (var_1' == nil)
last = last'
if last' then
var_1', ···, var_n' = var_1, ..., var_n -- save for 'then' clause
end
block
-- goto loop
end
end
]]--
function L.ForIn:compile(state, cont)
local nstate = state:newBlockState() -- inner block
local idlist,exprlist,optWith,forBody = self[1],self[2],self[3],self[4]
local bodyAst, thenAst, elseAst = forBody[1], forBody[2], forBody[3]
-- this is a "shadow" copy of the idlist, used for 'last' and then clauses
local idlist2 = {}
for i,_ in ipairs(idlist) do
local node = make_node("Id", { "0" .. i })
table.insert(idlist2, node)
end
-- use names starting with a number to ensure they don't conflict w/
-- any user locals (this is an alternative to :defineInvisibleLocal()
local fAst = make_node("Id", { "0f" })
local sAst = make_node("Id", { "0s" })
local varAst = idlist2[1]
local clAst = make_node("Id", { "0cl" })
-- this is used to save iteration variables to their shadows for the then block
local assignBackAst = make_node("Assign", { idlist2, idlist })
local loopidlist = idlist
local assignAst
if optWith ~= false then
assignAst = make_node("Assign", { idlist, idlist2 })
loopidlist = idlist2
end
local declareAst = make_node("VarDecl", {
-- declare all of our shadow variables
compat.move(idlist2, 1, #idlist2, 4, { fAst, sAst, clAst})
})
local innerDeclareAst = make_node("VarDecl", { idlist } )
local initAst = make_node("Assign", { { fAst, sAst, varAst, clAst }, exprlist })
local loopStart = make_node("Assign", { loopidlist, { make_node("Call", { false, { sAst, varAst }, fAst }) } })
-- outer block:
declareAst:defineLocals(state)
local _,varId = state:lookupLocal(idlist2[1][1])
local _, clId = state:lookupLocal("0cl")
-- inner block:
innerDeclareAst:defineLocals(nstate)
local _,var1Id = nstate:lookupLocal(idlist[1][1])
-- optional 'with'
local first, last, firstId, lastId
first = state:defineLocal("0first")
if optWith ~= false then
last = state:defineLocal("0last")
firstId, lastId = optWith:defineLocals(nstate)
end
local finalize = makeFinalize(thenAst, elseAst, state, cont, first, idlist, idlist2)
-- loop starts in outer block and enters inner block
-- body is executed in inner block
-- finalize is executed in outer block, so must pop first when breaking from inner
local body, loop, init, saveAndExecBody
if optWith == false then
loop = makePushEnv(loopStart:compile(nstate, function(env)
if env[var1Id] == nil then return finalize(env.parent) end -- break
env.parent[first] = false
return saveAndExecBody(env)
end))
else
local loopMid
loop = makePushEnv(function(env)
if env.parent[last] then return finalize(env.parent) end -- break
env[firstId] = env.parent[first]
env.parent[first] = false
return loopMid(env)
end)
loopMid = assignAst:compile(nstate, loopStart:compile(nstate, function(env)
env.parent[last] = (env.parent[varId] == nil)
env[lastId] = env.parent[last]
if env[lastId] then
return saveAndExecBody(env)
else
return body(env)
end
end))
end
saveAndExecBody = assignBackAst:compile(nstate, function(env)
return body(env)
end)
-- init is executed in the outer block
local function checkClosure(cont)
return function(env)
if env[clId] ~= nil then
error("Closures not yet supported")
end
env[first] = true
return cont(env)
end
end
local init
if optWith == false then
init = initAst:compile(
state, checkClosure(loop)
)
else
init = initAst:compile(
state, checkClosure(loopStart:compile(state, function(env)
env[last] = (env[varId] == nil)
return loop(env)
end)))
end
local oldBreak = nstate:setBreak(makePopEnv(cont))
body = bodyAst:compileInSameBlock(nstate, makePopEnv(loop))
nstate:setBreak(oldBreak)
-- okay, now pop all those local vars off
if optWith ~= false then
state:defineLocal("0last", "undef")
optWith:defineLocals(nstate, "undef")
end
state:defineLocal("0first", "undef")
innerDeclareAst:defineLocals(nstate, "undef")
declareAst:defineLocals(state, "undef")
return init
end
function L.FuncDecl:compile(state, cont)
local level, id = state:lookupLocal(self[1][1])
local func = L.Function.compileExpr(self, state)
assert(level == 0)
return function(env)
env[id] = func(env)
return cont(env)
end
end
function L.FuncDef:compile(state, cont)
local name, args, body = self[1], self[2], self[3]
local lhs, func
local addSelf = false
if name.tag ~= "ColonIndex" then
lhs = name:compileLhs(state)
func = L.Function.compileExpr(self, state)
else
-- compile it as a dot index
lhs = L.DotIndex.compileLhs(name, state)
-- but add an implicit 'self' argument
local nargs = { make_node("Id", { "self" } ) }
for i,v in ipairs(args) do
nargs[i+1] = v
end
func = make_node("Function", { false, nargs, body }):compileExpr(state)
end
return function(env)
lhs(env, func(env))
return cont(env)
end
end
function L.VarDecl:compile(state, cont)
local iddecllist, exprlist = self[1], (self[2] or {})
local lhs = {}
for i,v in ipairs(iddecllist) do
local name,attrib = v[1],v[2]
local level, id = state:lookupLocal(name)
assert(level == 0)
table.insert(lhs, id)
end
local rhs = {}
for i,v in ipairs(exprlist) do
table.insert(rhs, v:compileExpr(state))
end
local n = #exprlist
if #lhs == 1 and #rhs == 1 then -- fast path
local l,r = lhs[1], rhs[1]
return function(env)
env[l] = r(env)
return cont(env)
end
end
return function(env)
-- evaluate all expressions on the right hand side
local r = {}
for i=1,#rhs-1 do
r[i] = rhs[i](env)
end
-- "if the list of expressions ends with a function call, then all
-- values returned by that call enter the list of values" thus:
-- unpack last argument into remaining values
if #rhs > 0 then
local last = compat.pack(rhs[#rhs](env))
for i=1,last.n do
r[#rhs+i-1] = last[i]
end
end
-- perform all assignments
for i=1,#lhs do
env[lhs[i]] = r[i]
end
return cont(env)
end
end
function L.Assign:compile(state, cont)
local varlist, exprlist = self[1], self[2]
local rhs = {}
for _,e in ipairs(exprlist) do
table.insert(rhs, e:compileExpr(state))
end
local lhs = {}
for _,v in ipairs(varlist) do
table.insert(lhs, v:compileLhs(state))
end
if #rhs == 1 and #lhs == 1 then -- fast path
local l,r = lhs[1], rhs[1]
return function(env)
l(env, r(env))
return cont(env)
end
end
return function(env)
-- evaluate all expressions on the right hand side
local r = {}
for i=1,#rhs-1 do
r[i] = rhs[i](env)
end
-- "if the list of expressions ends with a function call, then all
-- values returned by that call enter the list of values" thus:
-- unpack last argument into remaining values
if #rhs > 0 then
local last = compat.pack(rhs[#rhs](env))
for i=1,last.n do
r[#rhs+i-1] = last[i]
end
end
-- perform all assignments
for i=1,#lhs do
lhs[i](env, r[i])
end
return cont(env)
end
end
function L.Function:compileExpr(state)
local args, body = self[2], self[3]
local nargs = #args
-- create a new state for this function
local ns = state:newBlockState()
-- define new local variables corresponding to the function arguments
local seenVarargs = false
for _,v in ipairs(args) do
if v.tag == 'Id' and not seenVarargs then
ns:defineLocal(v[1])
elseif v.tag == 'Varargs' and not seenVarargs then
seenVarargs = true
ns:defineLocal("0varargs") -- hidden local
nargs = nargs - 1
else
error("unknown argument type")
end
end
-- compile body in this new state
local f = body:compileInSameBlock(ns, function() return end)
-- return a value!
return function(env)
return function(...)
-- create a new environment with arguments appropriately set
local nenv = { parent = env }
for i=1,nargs do
nenv[i] = select(i, ...)
end
if seenVarargs then
nenv[nargs+1] = compat.pack(select(nargs+1, ...))
end
return f(nenv)
end
end
end
function L.Expr:compile(state, cont)
-- evaluate expression for side effects, throw value away, invoke continuation
local f = self:compileExpr(state)
return function(env)
f(env) -- throw result away
return cont(env) -- continue with next statement
end
end
function L.LiteralExpr:compileExpr(state)
local n = self[1]
return function() return n end
end
function L.Nil:compileExpr(state)
return function() return nil end
end
function L.Varargs:compileExpr(state)
local getter = make_node("Id", { "0varargs" }):compileExpr(state)
return function(env)
local varargs = getter(env)
return compat.unpack(varargs, 1, varargs.n)
end
end
function L.Id:compileLhs(state)
local level, id = state:lookupLocal(self[1])
if id ~= nil then
if level == 0 then
-- bound local variable in this environment
return function(env, val) env[id] = val end
else
return function(env,val)
local p = env
for i=1,level do p = p.parent end
p[id] = val
end
end
end
-- free name
local level,_env = state:lookupLocal('_ENV')
id = self[1]
return function(env, val)
local p = env
for i=1,level do p = p.parent end
p[_env][id] = val
end
end
function L.Id:compileExpr(state)
local level, id = state:lookupLocal(self[1])
if id ~= nil then
if level == 0 then
-- bound local variable in this environment
return function(env) return env[id] end
else
return function(env)
local p = env
for i=1,level do p = p.parent end
return p[id]
end
end
end
-- free name
local level,_env = state:lookupLocal('_ENV')
id = self[1]
return function(env)
local p = env
for i=1,level do p = p.parent end
return p[_env][id]
end
end
function L.Table:compileExpr(state)
local i = 1
local keys = {}
for j,v in ipairs(self) do
keys[j] = i
if v.tag ~= 'Pair' then
i = i + 1
end
end
if #self == 0 then
-- fast path, empty table
return function() return {} end
elseif #self == 1 and self[1].tag ~= 'Pair' and MULTIRES_OPT then
-- fast path, let lua handle multires internally
local f = self[1]:compileExpr(state)
return function(env) return { f(env) } end
end
local initialize = function(env, t) return t end
local last = true
for j,v in ripairs(self) do
initialize = v:compileTableInit(state, initialize, keys[j], last)
last = false
end
return function(env)
return initialize(env, {})
end
end
function L.Pair:compileTableInit(state, initialize)
local left, right = self[1], self[2]:compileExpr(state)
if type(left) == 'string' then
return function(env, t)
t[left] = right(env)
return initialize(env, t)
end
end
left = left:compileExpr(state)
return function(env, t)
t[left(env)] = right(env)
return initialize(env, t)
end
end
function L.Expr:compileTableInit(state, initialize, idx, last)
local f = self:compileExpr(state)
if not last then
return function(env, t)
t[idx] = f(env)
return initialize(env, t)
end
end
-- multires expression as final table initializer
return function(env, t)
local r = compat.pack(f(env))
for i=1,r.n do
t[idx+i-1] = r[i]
end
return initialize(env, t)
end
end
function L.Paren:compileExpr(state)
-- this is not entirely a no-op, it down-selects multires expressions to
-- a single result.
local f = self[1]:compileExpr(state)
return function(env)
return (f(env)) -- select just a single value
end
end
function L.Call:compileExpr(state)
local _,args,name = self[1],self[2],self[3]
local f = name:compileExpr(state)
local t = {}
for _,v in ipairs(args) do
table.insert(t, v:compileExpr(state))
end
-- fast cases for small # of args; this lets lua handle the multires
-- argument handling internally
if #args == 0 then
return function(env) return f(env)() end
elseif #args == 1 and MULTIRES_OPT then
local t1 = t[1]
return function(env) return f(env)(t1(env)) end
elseif #args == 2 and MULTIRES_OPT then
local t1,t2 = t[1],t[2]
return function(env) return f(env)(t1(env),t2(env)) end
elseif #args == 3 and MULTIRES_OPT then
local t1,t2,t3 = t[1],t[2],t[3]
return function(env) return f(env)(t1(env),t2(env),t3(env)) end
end
-- ok, the slightly-slower case for an arbitrary # of arguments
return function(env)
local func = f(env)
local args = {}
local n = #t
for i=1,n-1 do
args[i] = t[i](env) -- evaluate each argument expression in order
end
-- handle multires expressions; the n==0 case was already handled above
local last = compat.pack(t[n](env))
n = n - 1 -- we haven't stored the last item yet
for i=1,last.n do
n = n + 1
args[n] = last[i]
end
-- we use n instead of #r in case some of the values are null
return func(compat.unpack(args, 1, n))
end
end
function L.CallMethod:compileExpr(state)
local method,args,recv = self[1],self[2],self[3]
local f = recv:compileExpr(state)
local t = {}
for _,v in ipairs(args) do
table.insert(t, v:compileExpr(state))
end
if #args == 0 then
-- fast path for no arguments (except implicit receiver)
return function(env)
local recv = f(env)
return recv[method](recv)
end
elseif #args == 1 and MULTIRES_OPT then
-- fast path: let lua handle multires case
local t1 = t[1]
return function(env)
local recv = f(env)
return recv[method](recv, t1(env))
end
end
-- slightly slower case to handle arbitrary # arguments
return function(env)
local recv = f(env)
local func = recv[method]
local args = { recv }
local n = #t
for i=1,n-1 do
args[i + 1] = t[i](env) -- evaluate each argument expression in order
end
-- handle multires expressions (n==0 case already handled above)
local last = compat.pack(t[n](env))
n = n - 1 -- we haven't stored the last item yet
for i=1,last.n do
n = n + 1
args[n + 1] = last[i]
end
-- we use n instead of #r in case some of the values are null
return func(compat.unpack(args, 1, 1+n))
end
end
function L.DotIndex:compileLhs(state)
local left,right = self[2],self[1]
left = left:compileExpr(state)
return function(env, val)
left(env)[right] = val
end
end
function L.DotIndex:compileExpr(state)
local left,right = self[2],self[1]
left = left:compileExpr(state)
return function(env)
return left(env)[right]
end
end
function L.KeyIndex:compileLhs(state)
local left,right = self[2],self[1]
left, right = left:compileExpr(state), right:compileExpr(state)
return function(env, val)
left(env)[right(env)] = val
end
end
function L.KeyIndex:compileExpr(state)
local left,right = self[2],self[1]
left, right = left:compileExpr(state), right:compileExpr(state)
return function(env)
return left(env)[right(env)]
end
end
local function trymt(name,f)
return function(l,r)
-- if both arguments are numbers, don't use a metamethod
if type(l)=='number' and type(r)=='number' then return f(l,r) end
local op = nil
local mt = getmetatable(l)
if mt ~= nil then op = mt[name] end
if op == nil then
mt = getmetatable(r)
if mt ~= nil then op = mt[name] end
end
if op ~= nil then
local result = op(l,r) -- adjusted to one value
return result
end
return f(l,r)
end
end
local optable = {
['or'] = function(l,r) return l or r end, -- no metamethod
['and'] = function(l,r) return l and r end, -- no metamethod
eq = function(l,r) return l == r end,
ne = function(l,r) return l ~= r end,
le = function(l,r) return l <= r end,
ge = function(l,r) return l >= r end,
lt = function(l,r) return l < r end,
gt = function(l,r) return l > r end,
bor = trymt('__bor', bit32.bor),
bxor = trymt('__bxor', bit32.bxor),
band = trymt('__band', bit32.band),
shl = trymt('__shl', bit32.lshift),
shr = trymt('__shr', bit32.rshift),
concat = function(l,r) return l .. r end,
add = function(l,r) return l + r end,
sub = function(l,r) return l - r end,
mul = function(l,r) return l * r end,
idiv = trymt('__idiv', function(l,r) return math.floor(l / r) end),
div = function(l,r) return l / r end,
mod = function(l,r) return l % r end,
pow = function(l,r) return l ^ r end,
['not'] = function(r) return not r end,
len = function(r)
-- support for lua 5.4 __len metamethod on tables
-- (lua 5.1 always used primitive length on tables)
if type(r) == 'table' then
local mt = getmetatable(r)
if mt ~= nil then
local len = mt.__len
if len ~= nil then
local l = len(r) -- adjust to one value
return l
end
end
end
return #r
end,
unm = function(r) return -r end,
bnot = trymt('__bnot', bit32.bnot),
}
-- This could be optimized: for example we could specialize for constants,
-- We could fold in the function call that evaluates l and r, etc.
function L.BinaryOp:compileExpr(state)
local left,op,right = self[1],self[2],self[3]
left, right = left:compileExpr(state), right:compileExpr(state)
if op == 'or' then
-- short cut evaluation
return function(env)
return left(env) or right(env)
end
elseif op == 'and' then
-- short cut evaluation
return function(env)
return left(env) and right(env)
end
else
op = optable[op]
return function(env)
return op(left(env), right(env))
end
end
end
function L.UnaryOp:compileExpr(state)
local op,right = self[1],self[2]
op, right = optable[op], right:compileExpr(state)
return function(env)
return op(right(env))
end
end
local function compile(ast)
local state = State:new()
return ast:compile(state, function() return end)
end
local function eval(ast)
-- xxx create an appropriate environment
local GLOBALS = {
['assert']=assert, -- XXX might want to customize to give debugging info
['error']=error, -- XXX might want to customize to give debugging info
['getmetatable']=getmetatable,
['ipairs']=ipairs,
['math']=math,
['next']=next,
['pairs']=pairs,
['pcall']=pcall,
['rawequal']=rawequal,
['rawget']=rawget,
['rawset']=rawset,
['require']=require, -- could add support for sideloading l18n?
['select']=select,
['setmetatable']=setmetatable,
['string']=string, -- might want some compatibility thunks here
['table']={
concat = function(list, sep, i, j)
if sep == nil then sep = "" end
if i == nil then i = 1 end
if j == nil then j = compat.len(list) end
return table.concat(list, sep, i, j)
end,
insert = function(list, pos, val)
if val == nil then val = pos ; pos = nil end
if pos == nil then pos = compat.len(list) + 1 end
return table.insert(list, pos, val)
end,
maxn = function() error("not in Lua 5.4") end,
move = compat.move, -- not in Lua 5.1
pack = compat.pack, -- not in Lua 5.1
remove = function(l,p)
if p == nil then p = compat.len(l) end
return table.remove(l, p)
end,
sort = table.sort, -- might have issues with table length in Lua 5.1
unpack = compat.unpack, -- not in Lua 5.1
},
['tonumber']=tonumber,
['tostring']=tostring,
['type']=type,
-- add utf8?
['_VERSION']="Lua 5.4", -- that's what we support!
-- add warn? (not present in Lua 5.3, etc)
['xpcall']=xpcall,
}
GLOBALS._G = GLOBALS
-- Lua 5.1 support
if rawget(_G, "rawlen") then GLOBALS.rawlen = _G.rawlen end
-- Scribunto support
if rawget(_G, "mw") ~= nil then GLOBALS.mw = _G.mw end
-- CLI support
if rawget(_G, 'print') then GLOBALS.print = _G.print end
if rawget(_G, 'io') then GLOBALS.io = _G.io end
local env = { GLOBALS }
local f = compile(ast)
return f(env)
end
return {
compile = compile,
eval = eval,
}
end)
register('mlua.french', function(myrequire)
local lua = myrequire('mlua.lua')
local node = myrequire('mlua.node')
-- Translation table
local french = {
['and'] = 'et',
['break'] = 'arrêter',
['do'] = 'faire',
['else'] = 'sinon',
['elseif'] = 'sinonsi',
['end'] = 'fin',
['false'] = 'faux',
['for'] = 'pour',
['function'] = 'fonction',
['goto'] = 'allerà',
['if'] = 'si',
['in'] = 'de',
['local'] = 'locale',
['nil'] = 'nulle',
['not'] = 'pas',
['or'] = 'ou',
['repeat'] = 'répéter',
['return'] = 'renvoyer',
['then'] = 'alors',
['true'] = 'vrai',
['until'] = "jusqu’à", -- fun
['while'] = 'tant que', -- also fun
-- Bartosz' addition
['with'] = 'avec',
}
local function cli(filename, to_or_from)
local pprint = require "mlua.pprint" -- hide this from make_one_file
-- Read input file contents
local file = io.open(filename)
if not file then
print('failed to open file: '..filename)
os.exit(false)
end
local source = file:read('*a')
file:close()
if to_or_from == nil then
to_or_from = "to"
end
if to_or_from == "to" then
-- To french:
local p = lua.make_parse({
__options = {
tag = node.make_node,
}
})
local ast = p(source, filename)
print(pprint.print_ast(source, ast, french))
else
-- From french:
local p = lua.make_parse({
__options = {
kw = function(s) return french[s] or s end,
tag = node.make_node,
}
})
local ast = p(source, filename)
if to_or_from == 'from' then
print(pprint.print_ast(source, ast))
else
-- execute!
local eval = myrequire('mlua.eval').eval
print(eval(ast))
end
end
end
--cli(arg[1], arg[2])
return french
end)
register('mlua.wiki', function(myrequire)
--[[
a Scribunto module to evaluate a given module, but with our interpreter
]]--
local lua = myrequire('mlua.lua')
local make_node = myrequire('mlua.node').make_node
local eval = myrequire('mlua.eval').eval
local french = myrequire('mlua.french')
-- This could include language-specific options.
local parse
local function get_parse()
if parse == nil then
parse = lua.make_parse{
__options = { tag = make_node },
}
end
return parse
end
local parse_fr
local function get_parse_fr()
if parse_fr == nil then
parse_fr = lua.make_parse{
__options = {
kw = function(s) return french[s] or s end,
tag = make_node,
},
}
end
return parse_fr
end
local function get_source(frame, default_to_module)
local title = frame.args[1]
if string.find(title, "^Module:") == nil and default_to_module then
title = mw.title.makeTitle("Module", title)
else
title = mw.title.new(title)
end
local source = title:getContent()
if source == nil then
error("Can't find title " .. tostring(title))
end
-- strip syntaxhighlight tags if present
source = source:gsub("^%s*<syntaxhighlight[^>]*>", "", 1)
source = source:gsub("</syntaxhighlight[^>]*>%s*$", "", 1)
return title, source
end
local function invoke_from_ast(frame, ast)
local M = eval(ast)
local f = M[frame.args[2]]
-- now pop the first two arguments off and invoke the function
-- this is harder than it should be, since frame.args isn't a
-- real table.
-- XXX should also hook frame:getArgument, frame.args.__pairs,
-- frame.args.__ipairs, etc. and also be more careful w/r/t
-- string/number.
local args_proxy = function(_,v)
if type(v) == 'number' then
return frame.args[v+2]
else
return frame.args[v]
end
end
local nargs = setmetatable({}, {__index = args_proxy})
local nframe = {}
local frame_proxy = function(_,v)
if v == 'args' then return nargs end
local res = frame[v]
if type(res) == 'function' then
return function(...)
if select(1, ...) == nframe then
-- substitute in the proper receiver
return res(frame, select(2, ...))
end
return res(...)
end
end
return res
end
setmetatable(nframe, {__index = frame_proxy})
return f(nframe)
end
return {
get_parse = get_parse,
get_parse_fr = get_parse_fr,
eval = function(frame, ...)
if type(frame)=='string' then
frame = { args = { frame, ... } }
end
local source = frame.args[1]
local lang = frame.args[2] or 'en'
local parse
if lang == 'fr' then
parse = get_parse_fr()
else
parse = get_parse()
end
local ast = parse(source, "<console>")
return eval(ast)
end,
invoke = function(frame, ...)
if type(frame)=='string' then
frame = { args = { frame, ... } }
end
local title, source = get_source(frame, true)
local parse = get_parse()
local ast = parse(source, title.fullText)
return invoke_from_ast(frame, ast)
end,
invokeUser = function(frame, ...)
if type(frame)=='string' then
frame = { args = { frame, ... } }
end
-- If we're using Bartosz' for-loop syntax,
-- we can't get source from module space because this isn't
-- "syntax-error-free lua"
local title, source = get_source(frame)
local parse = get_parse()
local ast = parse(source, title.fullText)
return invoke_from_ast(frame, ast)
end,
invokeFr = function(frame, ...)
if type(frame)=='string' then
frame = { args = { frame, ... } }
end
-- we can't get source from module space because this isn't
-- "syntax-error-free lua"
local title, source = get_source(frame)
local parse = get_parse_fr()
local ast = parse(source, title.fullText)
return invoke_from_ast(frame, ast)
end
}
end)
local modules = {}
modules['bit32'] = require('bit32')
modules['string'] = require('string')
modules['strict'] = {}
modules['table'] = require('table')
local function myrequire(name)
if modules[name] == nil then
modules[name] = true
modules[name] = (builders[name])(myrequire)
end
return modules[name]
end
return myrequire('mlua.wiki')
end)()
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.