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 ''

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*]*>", "", 1)

source = source:gsub("]*>%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, "")

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)()