diff --git a/README.md b/README.md index aac8d42..fdd8804 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,9 @@ Candran is a dialect of the [Lua 5.3](http://www.lua.org) programming language w Unlike Moonscript, Candran tries to stay close to the Lua syntax. -Candran code example : - ````lua #import("lib.thing") -#local debug = debug or false +#local debug or= false local function calculate(toadd=25) local result = thing.do() @@ -19,11 +17,22 @@ local function calculate(toadd=25) return result end -print(calculate()) +local a = { + hey = true, + + newHop = :(foo, thing) + @hey = thing(foo) + end +} + +a:newHop(42, (foo) + return "something " .. foo +end) + ```` -##### Quick setup -Install LPegLabel (```luarocks install LPegLabel```), download this repository and use Candran through ```canc.lua``` or ```candran.lua```. +#### Quick setup +Install LPegLabel (```luarocks install LPegLabel```), download this repository and use Candran through the scripts in ```bin/``` or use it as a library with the self-contained ```candran.lua```. The language ------------ @@ -53,7 +62,8 @@ The preprocessor has access to the following variables : ### Syntax additions After the preprocessor is run the Candran code is compiled to Lua. The Candran code adds the folowing syntax to Lua : -##### New assignment operators + +##### Assignment operators * ````var += nb```` * ````var -= nb```` * ````var *= nb```` @@ -71,6 +81,10 @@ After the preprocessor is run the Candran code is compiled to Lua. The Candran c For example, a ````var += nb```` assignment will be compiled into ````var = var + nb````. +All theses operators can also be put right of the assigment operator, in which case ```var =+ nb``` will be compiled into ```var = nb + var```. + +If you feel like writing hard to understand code, right and left operator can be used at the same time. + ##### Default function parameters ```lua function foo(bar = "default", other = thing.do()) @@ -83,43 +97,87 @@ It is equivalent to doing ```if arg == nil then arg = default end``` for each ar The default values can be complete Lua expressions, and will be evaluated each time the function is run. +##### @ self aliases +```lua +a = { + foo = "Hoi" +} + +function a:hey() + print(@foo) -- Hoi + print(@["foo"]) -- also works + print(@ == self) -- true +end +``` +When a variable name is prefied with ```@```, the name will be accessed in ```self```. + +When used by itself, ```@``` is an alias for ```self```. + +##### Short anonymous function declaration +```lua +a = (arg1, arg2) + print(arg1) +end + +b = :(hop) + print(self, hop) +end +``` +Anonymous function (functions values) can be created in a more concise way by omitting the ```function``` keyword. + +A ```:``` can prefix the parameters paranthesis to automatically add a ```self``` parameter. + Compile targets --------------- Candran is based on the Lua 5.3 syntax, but can be compiled to both Lua 5.3 and Lua 5.1/LuaJit. -To chose a compile target, either explicitly give ```lua53``` or ```luajit``` as a second argument to ```candran.compile```, or set the ```target``` preprocessor argument when using ```candran.make``` or the command line tools. +To chose a compile target, set the ```target``` option to ```lua53``` (default) or ```luajit``` in the option table when using the library or the command line tools. -Lua 5.3 specific syntax (bitwise operators, integer division) will automatically be translated in valid Lua 5.1 code, using LuaJit's ```bit``` library if necessary. +Lua 5.3 specific syntax (bitwise operators, integer division) will automatically be translated in valid Lua 5.1 code, using LuaJIT's ```bit``` library if necessary. The library ----------- ### Command-line usage -The library can be used standalone through the ```canc``` utility: +The library can be used standalone through the ```canc``` and ```can``` utility: -* ````lua canc.lua```` +* ````canc```` Display the information text (version and basic command-line usage). -* ````lua canc.lua [arguments] filename...```` +* ````canc [options] filename...```` Preprocess and compile each _filename_ Candran files, and creates the assiociated ```.lua``` files in the same directories. - _arguments_ is of type ````-somearg -anotherarg thing=somestring other=5 ...````, which will generate a Lua table ```{ somearg = true, anotherarg = true, thing = "somestring", other = 5 }```. + _options_ is of type ````-somearg -anotherarg thing=somestring other=5 ...````, which will generate a Lua table ```{ somearg = true, anotherarg = true, thing = "somestring", other = 5 }```. You can choose to use another directory where files should be written using the ```dest=destinationDirectory``` argument. ```canc``` can write to the standard output instead of creating files using the ```-print``` argument. + You can choosed to run only the preprocessor or compile using the ```-preprocess``` and ```-compile``` flags. + * example uses : - ````lua canc.lua foo.can```` + ````canc foo.can```` preprocess and compile _foo.can_ and write the result in _foo.lua_. - ````lua canc.lua foo.can -verbose -print | lua```` + ````canc foo.can -verbose -print | lua```` preprocess _foo.can_ with _verbose_ set to _true_, compile it and execute it. +* ```can``` + + Start a simplisitic Candran REPL. + +* ````canc [options] filename```` + + Preprocess, compile and run _filename_ using the options provided. + + This will automatically register the Candran package searcher so required file will be compiled as they are needed. + + This command will use error rewriting if enabled. + ### Library usage Candran can also be used as a Lua library. For example, ````lua @@ -132,14 +190,70 @@ f:close() local compiled = candran.make(contents, { lang = "fr" }) load(compiled)() + +-- or simpler... +candran.dofile("foo.can") ```` Will load Candran, read the file _foo.can_, compile its contents with the argument _lang_ set to _"fr"_, and then execute the result. The table returned by _require("candran")_ gives you access to : + +##### Compiler & preprocessor API * ````candran.VERSION```` : Candran's version string. -* ````candran.preprocess(code[, args])```` : return the Candran code _code_, preprocessed with _args_ as argument table. -* ````candran.compile(code[, target])```` : return the Candran code compiled to Lua. -* ````candran.make(code[, args])```` : return the Candran code, preprocessed with _args_ as argument table and compilled to Lua. +* ````candran.preprocess(code[, options])```` : return the Candran code _code_, preprocessed with _options_ as options table. +* ````candran.compile(code[, options])```` : return the Candran code compiled to Lua with _options_ as the option table. +* ````candran.make(code[, options])```` : return the Candran code, preprocessed and compiled with _options_ as options table. + +##### Code loading helpers +* ```candran.loadfile(filepath, env, options)``` : Candran equivalent to the Lua 5.3's loadfile funtion. Will rewrite errors by default. +* ```candran.load(chunk, chunkname, env, options)``` : Candran equivalent to the Lua 5.3's load funtion. Will rewrite errors by default. +* ```candran.dofile(filepath, options)``` : Candran equivalent to the Lua 5.3's dofile funtion. Will rewrite errors by default. + +#### Error rewriting +When using the command-line tools or the code loading helpers, Candran will automatically setup error rewriting: because the code is reformated when +compiled and preprocessed, lines numbers given by Lua in case of error are hardly usable. To fix that, Candran map each line from the compiled file to +the lines from the original file(s), inspired by MoonScript. Errors will be displayed as: + +``` +example.can:12(5): attempt to call a nil value (global 'iWantAnError') +``` + +12 is the line number in the original Candran file, and 5 is the line number in the compiled file. + +If you are using the preprocessor ```import()``` function, the source Candran file and destination Lua file might not have the same name. In this case, the error will be: + +``` +example.can:12(final.lua:5): attempt to call a nil value (global 'iWantAnError') +``` + +* ```candran.messageHandler(message)``` : The error message handler used by Candran. Use it in xpcall to rewrite stacktraces to display Candran source file lines instead of compiled Lua lines. + +##### Package searching helpers +Candran comes with a custom package searcher which will automatically find, preprocesses and compile ```.can``` files. If you want to use Candran in your project without worrying about +compiling the files, you can simply call + +```lua +require("candran").setup() +``` + +at the top of your main Lua file. If a Candran is found when you call ```require()```, it will be automatically compiled and loaded. If both a Lua and Candran file match a module name, the Candran +file will be loaded. + +* ```candran.searcher(modpath)``` : Candran package searcher function. Use the existing package.path. +* ```candran.setup()``` : Register the Candran package searcher. + +##### Available compiler & preprocessor options +You can give arbitrary options which will be gived to the preprocessor, but Candran already provide and uses these with their associated default values: + +```lua +target = "lua53" -- Compiler target. "lua53" or "luajit". +indentation = "" -- Character(s) used for indentation in the compiled file. +newline = "\n" -- Character(s) used for newlines in the compiled file. +requirePrefix = "CANDRAN_" -- Prefix used when Candran needs to require an external library to provide some functionality (example: LuaJIT's bit lib when using bitwise operators). +mapLines = true -- If true, compiled files will contain comments at the end of each line indicating the associated line and source file. Needed for error rewriting. +chunkname = "nil" -- The chunkname used when running code using the helper functions and writing the line origin comments. Candran will try to set it to the original filename if it knows it. +rewriteErrors = true -- True to enable error rewriting when loading code using the helper functions. Will wrap the whole code in a xpcall(). +``` ### Compiling the library The Candran library itself is written is Candran, so you have to compile it with an already compiled Candran library. @@ -149,9 +263,11 @@ The compiled _candran.lua_ should include every Lua library needed to run it. Yo This command will use the precompilled version of this repository (candran.lua) to compile _candran.can_ and write the result in _candran.lua_ : ```` -lua canc.lua candran.can +canc candran.can ```` +The Candran build included in this repository were made using the ```mapLines=false``` options. + You can then run the tests on your build : ```` diff --git a/bin/can b/bin/can new file mode 100644 index 0000000..c0db232 --- /dev/null +++ b/bin/can @@ -0,0 +1,43 @@ +#!/bin/lua +local candran = require("candran") +local cmdline = require("lib.cmdline") + +local args = cmdline(arg) + +if args.help or args.h then + print("Candran interpreter version "..candran.VERSION.." by Reuh") + print("Usage: "..arg[0].." [target=] [options] filename") + return +end + +if #args >= 1 then + candran.dofile(args[1], args) +else -- REPL + print("Candran " .. candran.VERSION) + candran.setup() + while true do + io.write("> ") + local line = io.read() + if line:match("^=") then + line = line:gsub("^=", "return tostring(") .. ")" + end + + local p = dofile("lib/lua-parser/parser.lua") + local d = dofile("lib/lua-parser/pp.lua") + print(p.parse(line)) + print(d.dump(p.parse(line))) + print(require"compiler.lua53"(p.parse(line))) + + local t = { pcall(candran.load, line, "stdin") } + if t[1] == false then + print(t[2]) + else + t = { pcall(t[2]) } + if t[1] == false then + print(t[2]) + elseif #t > 1 then + print(unpack(t, 2)) + end + end + end +end diff --git a/canc.lua b/bin/canc similarity index 69% rename from canc.lua rename to bin/canc index 1454f8d..5cfed4b 100644 --- a/canc.lua +++ b/bin/canc @@ -1,10 +1,10 @@ #!/bin/lua local candran = require("candran") -local cmdline = require("cmdline") +local cmdline = require("lib.cmdline") if #arg < 1 then print("Candran compiler version "..candran.VERSION.." by Reuh") - print("Usage: "..arg[0].." [target=] [dest=] [-print] [preprocessor arguments] filename...") + print("Usage: "..arg[0].." [target=] [dest=] [-print] [-preprocess] [-compile] [options] filename...") return end @@ -25,7 +25,20 @@ for _, file in ipairs(args) do local input = inputFile:read("*a") inputFile:close() - local out = candran.make(input, args) + if args.chunkname == nil then + args.chunkname = file + end + + local out = input + if args.preprocess then + out = candran.preprocess(out, args) + end + if args.compile then + out = candran.compile(out, args) + end + if args.compile == nil and args.preprocess == nil then + out = candran.make(input, args) + end if args.print then print(out) diff --git a/candran.can b/candran.can index 6e38e8c..5e9ef68 100644 --- a/candran.can +++ b/candran.can @@ -1,28 +1,45 @@ -#import("util") +#import("lib.util") +#import("lib.cmdline") + #import("compiler.lua53") #import("compiler.luajit") -#import("lua-parser.scope") -#import("lua-parser.validator") -#import("lua-parser.pp") -#import("lua-parser.parser") -#import("cmdline") -local util = require("util") +#import("lib.lua-parser.scope") +#import("lib.lua-parser.validator") +#import("lib.lua-parser.pp") +#import("lib.lua-parser.parser") local candran = { - VERSION = "0.2.0" + VERSION = "0.3.0" +} + +--- Default options. +local default = { + target = "lua53", + indentation = "", + newline = "\n", + requirePrefix = "CANDRAN_", + mapLines = true, + chunkname = "nil", + rewriteErrors = true } --- Run the preprocessor -- @tparam input string input code --- @tparam args table arguments for the preprocessor. They will be inserted into the preprocessor environement. +-- @tparam options table arguments for the preprocessor. They will be inserted into the preprocessor environement. -- @treturn output string output code -function candran.preprocess(input, args={}) +function candran.preprocess(input, options={}) + options = util.merge(default, options) + -- generate preprocessor code local preprocessor = "" + local i = 0 for line in (input.."\n"):gmatch("(.-\n)") do + i += 1 if line:match("^%s*#") and not line:match("^#!") then -- exclude shebang preprocessor ..= line:gsub("^%s*#", "") + elseif options.mapLines then + preprocessor ..= ("write(%q)"):format(line:sub(1, -2) .. " -- "..options.chunkname..":" .. i) .. "\n" else preprocessor ..= ("write(%q)"):format(line:sub(1, -2)) .. "\n" end @@ -30,13 +47,7 @@ function candran.preprocess(input, args={}) preprocessor ..= "return output" -- make preprocessor environement - local env = {} - for k,v in pairs(_G) do - env[k] = v - end - for k, v in pairs(args) do - env[k] = v - end + local env = util.merge(_G, options) --- Candran library table env.candran = candran --- Current preprocessor output @@ -45,15 +56,14 @@ function candran.preprocess(input, args={}) -- @tparam modpath string module path -- @tparam margs table preprocessor arguments to use when preprocessessing the module -- @tparam autoRequire[opt=true] boolean true to automatically load the module into a local variable - env.import = function(modpath, margs=args, autoRequire=true) + env.import = function(modpath, margs={}, autoRequire=true) local filepath = assert(util.search(modpath), "No module named \""..modpath.."\"") -- open module file local f = io.open(filepath) if not f then error("Can't open the module file to import") end - for k, v in pairs(args) do - if margs[k] == nil then margs[k] = v end - end + + margs = util.merge(options, { chunkname = filepath }, margs) local modcontent = candran.preprocess(f:read("*a"), margs) f:close() @@ -92,57 +102,143 @@ function candran.preprocess(input, args={}) end -- compile & load preprocessor - local preprocess, err = util.loadenv(candran.compile(preprocessor, args.target), "candran preprocessor", env) + local preprocess, err = util.load(candran.compile(preprocessor, args), "candran preprocessor", env) if not preprocess then error("Error while creating Candran preprocessor: " .. err) end -- execute preprocessor local success, output = pcall(preprocess) - if not success then error("Error while preprocessing file: " .. output .. "\nWith preprocessor : \n" .. preprocessor) end + if not success then error("Error while preprocessing file: " .. output) end return output end --- Compiler -function candran.compile(input, target="lua53") - local parse = require("lua-parser.parser").parse +--- Run the compiler +-- @tparam input string input code +-- @tparam options table options for the compiler +-- @treturn output string output code +function candran.compile(input, options={}) + options = util.merge(default, options) - local ast, errmsg = parse(input, "candran") + local ast, errmsg = parser.parse(input, "candran") if not ast then error("Compiler: error while parsing file: "..errmsg) end - return require("compiler."..target)(ast) + return require("compiler."..options.target)(input, ast, options) end --- Preprocess & compile -function candran.make(code, args={}) - return candran.compile(candran.preprocess(code, args), args.target) +--- Preprocess & compile code +-- @tparam code string input code +-- @tparam options table arguments for the preprocessor and compiler +-- @treturn output string output code +function candran.make(code, options) + return candran.compile(candran.preprocess(code, options), options) end -function candran.searcher(modpath) - -- get module filepath - local notfound = "" - local filepath - for path in package.path:gsub("%.lua", ".can"):gmatch("[^;]+") do - local path = path:gsub("%?", (modpath:gsub("%.", "/"))) - local f = io.open(path) - if f then - f:close() - filepath = path - else - notfound = notfound .. "\n\tno Candran file '"..path.."'" - end - end - if not filepath then return notfound end - - -- open module file - local f = io.open(filepath) - if not f then error("Can't open the module file to import") end - local modcontent = f:read("*a") +local errorRewritingActive = false +local codeCache = {} +--- Candran equivalent to the Lua 5.3's loadfile funtion. +-- Will rewrite errors by default. +function candran.loadfile(filepath, env, options) + local f, err = io.open(filepath) + if not f then error("can't open the file: "..err) end + local content = f:read("*a") f:close() - return load(candran.make(modcontent)) + return candran.load(content, filepath, env, options) +end + +--- Candran equivalent to the Lua 5.3's load funtion. +-- Will rewrite errors by default. +function candran.load(chunk, chunkname, env, options={}) + options = util.merge({ chunkname = tostring(chunkname or chunk) }, options) + + codeCache[options.chunkname] = candran.make(chunk, options) + local f = util.load(codeCache[options.chunkname], options.chunkname, env) + + if options.rewriteErrors == false then + return f + else + return function() + if not errorRewritingActive then + errorRewritingActive = true + local t = { xpcall(f, candran.messageHandler) } + errorRewritingActive = false + if t[1] == false then + error(t[2], 0) + end + return unpack(t, 2) + else + return f() + end + end + end +end + +--- Candran equivalent to the Lua 5.3's dofile funtion. +-- Will rewrite errors by default. +function candran.dofile(filename, options) + return candran.loadfile(filename, nil, options)() +end + +--- Candran error message handler. +-- Use it in xpcall to rewrite stacktraces to display Candran source file lines instead of compiled Lua lines. +function candran.messageHandler(message) + return debug.traceback(message, 2):gsub("(\n?%s*)([^\n]-)%:(%d+)%:", function(indentation, source, line) + line = tonumber(line) + + local originalFile + local strName = source:match("%[string \"(.-)\"%]") + if strName then + if codeCache[strName] then + originalFile = codeCache[strName] + source = strName + end + else + local fi = io.open(source, "r") + if fi then + originalFile = fi:read("*a") + end + fi:close() + end + + if originalFile then + local i = 0 + for l in originalFile:gmatch("([^\n]*)") do + i = i +1 + if i == line then + local extSource, lineMap = l:match("%-%- (.-)%:(%d+)$") + if lineMap then + if extSource ~= source then + return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):" + else + return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):" + end + end + break + end + end + end + end) +end + +--- Candran package searcher function. Use the existing package.path. +function candran.searcher(modpath) + local filepath = util.search(modpath) + if not filepath then + return "\n\tno candran file in package.path" + end + return candran.loadfile(filepath) +end + +--- Register the Candran package searcher. +function candran.setup() + if _VERSION == "Lua 5.1" then + table.insert(package.loaders, 2, candran.searcher) + else + table.insert(package.searchers, 2, candran.searcher) + end end return candran diff --git a/candran.lua b/candran.lua index 70eeeb0..7bf6777 100644 --- a/candran.lua +++ b/candran.lua @@ -1,1584 +1,1779 @@ local function _() - local util = {} - util["search"] = function(modpath, exts) - if exts == nil then exts = { - "can", "lua" - } end - for _, ext in ipairs(exts) do - for path in package["path"]:gmatch("[^;]+") do - local fpath = path:gsub("%.lua", "." .. ext):gsub("%?", (modpath:gsub("%.", "/"))) - local f = io["open"](fpath) - if f then - f:close() - return fpath - end - end - end - end - util["loadenv"] = function(str, name, env) - if _VERSION == "Lua 5.1" then - local fn, err = loadstring(str, name) - if not fn then - return fn, err - end - return env ~= nil and setfenv(fn, env) or fn - else - return load(str, name, nil, env) - end - end - return util +local util = {} +util["search"] = function(modpath, exts) +if exts == nil then exts = { +"can", "lua" +} end +for _, ext in ipairs(exts) do +for path in package["path"]:gmatch("[^;]+") do +local fpath = path:gsub("%.lua", "." .. ext):gsub("%?", (modpath:gsub("%.", "/"))) +local f = io["open"](fpath) +if f then +f:close() +return fpath +end +end +end +end +util["load"] = function(str, name, env) +if _VERSION == "Lua 5.1" then +local fn, err = loadstring(str, name) +if not fn then +return fn, err +end +return env ~= nil and setfenv(fn, env) or fn +else +if env then +return load(str, name, nil, env) +else +return load(str, name) +end +end +end +util["merge"] = function(...) +local r = {} +for _, t in ipairs({ ... }) do +for k, v in pairs(t) do +r[k] = v +end +end +return r +end +return util end local util = _() or util -package["loaded"]["util"] = util or true +package["loaded"]["lib.util"] = util or true local function _() - return function(ast, opts) - local options = { - ["indentation"] = "\9", ["newline"] = "\ -", ["requirePrefix"] = "CANDRAN_" - } - local indentLevel = 0 - local function newline() - return options["newline"] .. string["rep"](options["indentation"], indentLevel) - end - local function indent() - indentLevel = indentLevel + 1 - return newline() - end - local function unindent() - indentLevel = indentLevel - 1 - return newline() - end - local required = {} - local requireStr = "" - local function addRequire(str, name, field) - if not required[str] then - requireStr = requireStr .. "local " .. options["requirePrefix"] .. name .. (" = require(%q)"):format(str) .. (field and "." .. field or "") .. options["newline"] - required[str] = true - end - end - local function getRequire(name) - return options["requirePrefix"] .. name - end - local tags - local function lua(ast, forceTag, ...) - return tags[forceTag or ast["tag"]](ast, ...) - end - tags = setmetatable({ - ["Block"] = function(t) - local r = "" - for i = 1, # t - 1, 1 do - r = r .. lua(t[i]) .. newline() - end - if t[# t] then - r = r .. lua(t[# t]) - end - return r - end, ["Do"] = function(t) - return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end" - end, ["Set"] = function(t) - if # t == 2 then - return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs") - else - local r = lua(t[1], "_lhs") .. " = " .. lua({ - t[2], t[1][1], t[3][1] - }, "Op") - for i = 2, math["min"](# t[3], # t[1]), 1 do - r = r .. ", " .. lua({ - t[2], t[1][i], t[3][i] - }, "Op") - end - return r - end - end, ["While"] = function(t) - return "while " .. lua(t[1]) .. " do" .. indent() .. lua(t[2]) .. unindent() .. "end" - end, ["Repeat"] = function(t) - return "repeat" .. indent() .. lua(t[1]) .. unindent() .. "until " .. lua(t[2]) - end, ["If"] = function(t) - local r = "if " .. lua(t[1]) .. " then" .. indent() .. lua(t[2]) .. unindent() - for i = 3, # t - 1, 2 do - r = r .. "elseif " .. lua(t[i]) .. " then" .. indent() .. lua(t[i + 1]) .. unindent() - end - if # t % 2 == 1 then - r = r .. "else" .. indent() .. lua(t[# t]) .. unindent() - end - return r .. "end" - end, ["Fornum"] = function(t) - local r = "for " .. lua(t[1]) .. " = " .. lua(t[2]) .. ", " .. lua(t[3]) - if # t == 5 then - return r .. ", " .. lua(t[4]) .. " do" .. indent() .. lua(t[5]) .. unindent() .. "end" - else - return r .. " do" .. indent() .. lua(t[4]) .. unindent() .. "end" - end - end, ["Forin"] = function(t) - return "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent() .. lua(t[3]) .. unindent() .. "end" - end, ["Local"] = function(t) - local r = "local " .. lua(t[1], "_lhs") - if t[2][1] then - r = r .. " = " .. lua(t[2], "_lhs") - end - return r - end, ["Localrec"] = function(t) - return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword") - end, ["Goto"] = function(t) - return "goto " .. lua(t[1], "Id") - end, ["Label"] = function(t) - return "::" .. lua(t[1], "Id") .. "::" - end, ["Return"] = function(t) - return "return " .. lua(t, "_lhs") - end, ["Break"] = function() - return "break" - end, ["Nil"] = function() - return "nil" - end, ["Dots"] = function() - return "..." - end, ["Boolean"] = function(t) - return tostring(t[1]) - end, ["Number"] = function(t) - return tostring(t[1]) - end, ["String"] = function(t) - return ("%q"):format(t[1]) - end, ["_functionWithoutKeyword"] = function(t) - local r = "(" - local decl = {} - if t[1][1] then - if t[1][1]["tag"] == "ParPair" then - local id = lua(t[1][1][1]) - indentLevel = indentLevel + 1 - table["insert"](decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) - indentLevel = indentLevel - 1 - r = r .. id - else - r = r .. lua(t[1][1]) - end - for i = 2, # t[1], 1 do - if t[1][i]["tag"] == "ParPair" then - local id = lua(t[1][i][1]) - indentLevel = indentLevel + 1 - table["insert"](decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][i][2]) .. " end") - indentLevel = indentLevel - 1 - r = r .. ", " .. id - else - r = r .. ", " .. lua(t[1][i]) - end - end - end - r = r .. ")" .. indent() - for _, d in ipairs(decl) do - r = r .. d .. newline() - end - return r .. lua(t[2]) .. unindent() .. "end" - end, ["Function"] = function(t) - return "function" .. lua(t, "_functionWithoutKeyword") - end, ["Pair"] = function(t) - return "[" .. lua(t[1]) .. "] = " .. lua(t[2]) - end, ["Table"] = function(t) - if # t == 0 then - return "{}" - elseif # t == 1 then - return "{ " .. lua(t, "_lhs") .. " }" - else - return "{" .. indent() .. lua(t, "_lhs") .. unindent() .. "}" - end - end, ["Op"] = function(t) - local r - if # t == 2 then - if type(tags["_opid"][t[1]]) == "string" then - r = tags["_opid"][t[1]] .. " " .. lua(t[2]) - else - r = tags["_opid"][t[1]](t[2]) - end - else - if type(tags["_opid"][t[1]]) == "string" then - r = lua(t[2]) .. " " .. tags["_opid"][t[1]] .. " " .. lua(t[3]) - else - r = tags["_opid"][t[1]](t[2], t[3]) - end - end - return r - end, ["Paren"] = function(t) - return "(" .. lua(t[1]) .. ")" - end, ["Call"] = function(t) - return lua(t[1]) .. "(" .. lua(t, "_lhs", 2) .. ")" - end, ["Invoke"] = function(t) - return lua(t[1]) .. ":" .. lua(t[2], "Id") .. "(" .. lua(t, "_lhs", 3) .. ")" - end, ["_lhs"] = function(t, start) - start = start or 1 - local r - if t[start] then - r = lua(t[start]) - for i = start + 1, # t, 1 do - r = r .. ", " .. lua(t[i]) - end - else - r = "" - end - return r - end, ["Id"] = function(t) - return t[1] - end, ["Index"] = function(t) - return lua(t[1]) .. "[" .. lua(t[2]) .. "]" - end, ["_opid"] = { - ["add"] = "+", ["sub"] = "-", ["mul"] = "*", ["div"] = "/", ["idiv"] = "//", ["mod"] = "%", ["pow"] = "^", ["concat"] = "..", ["band"] = "&", ["bor"] = "|", ["bxor"] = "~", ["shl"] = "<<", ["shr"] = ">>", ["eq"] = "==", ["ne"] = "~=", ["lt"] = "<", ["gt"] = ">", ["le"] = "<=", ["ge"] = ">=", ["and"] = "and", ["or"] = "or", ["unm"] = "-", ["len"] = "#", ["bnot"] = "~", ["not"] = "not" - } - }, { ["__index"] = function(self, key) - error("don't know how to compile a " .. tostring(key) .. " to Lua 5.3") - end }) - if opts then - for k, v in pairs(opts) do - options[k] = v - end - end - local r = lua(ast) - return requireStr .. r - end +local ipairs, pairs, setfenv, tonumber, loadstring, type = ipairs, pairs, setfenv, tonumber, loadstring, type +local tinsert, tconcat = table["insert"], table["concat"] +local function commonerror(msg) +return nil, ("[cmdline]: " .. msg) +end +local function argerror(msg, numarg) +msg = msg and (": " .. msg) or "" +return nil, ("[cmdline]: bad argument #" .. numarg .. msg) +end +local function iderror(numarg) +return argerror("ID not valid", numarg) +end +local function idcheck(id) +return id:match("^[%a_][%w_]*$") and true +end +return function(t_in, options, params) +local t_out = {} +for i, v in ipairs(t_in) do +local prefix, command = v:sub(1, 1), v:sub(2) +if prefix == "$" then +tinsert(t_out, command) +elseif prefix == "-" then +for id in command:gmatch("[^,;]+") do +if not idcheck(id) then +return iderror(i) +end +t_out[id] = true +end +elseif prefix == "!" then +local f, err = loadstring(command) +if not f then +return argerror(err, i) +end +setfenv(f, t_out)() +elseif v:find("=") then +local ids, val = v:match("^([^=]+)%=(.*)") +if not ids then +return argerror("invalid assignment syntax", i) +end +if val == "false" then +val = false +elseif val == "true" then +val = true +else +val = val:sub(1, 1) == "$" and val:sub(2) or tonumber(val) or val +end +for id in ids:gmatch("[^,;]+") do +if not idcheck(id) then +return iderror(i) +end +t_out[id] = val +end +else +tinsert(t_out, v) +end +end +if options then +local lookup, unknown = {}, {} +for _, v in ipairs(options) do +lookup[v] = true +end +for k, _ in pairs(t_out) do +if lookup[k] == nil and type(k) == "string" then +tinsert(unknown, k) +end +end +if # unknown > 0 then +return commonerror("unknown options: " .. tconcat(unknown, ", ")) +end +end +if params then +local missing = {} +for _, v in ipairs(params) do +if t_out[v] == nil then +tinsert(missing, v) +end +end +if # missing > 0 then +return commonerror("missing parameters: " .. tconcat(missing, ", ")) +end +end +return t_out +end +end +local cmdline = _() or cmdline +package["loaded"]["lib.cmdline"] = cmdline or true +local function _() +return function(code, ast, options) +local lastInputPos = 1 +local prevLinePos = 1 +local lastSource = "nil" +local lastLine = 1 +local indentLevel = 0 +local function newline() +local r = options["newline"] .. string["rep"](options["indentation"], indentLevel) +if options["mapLines"] then +local sub = code:sub(lastInputPos) +local source, line = sub:sub(1, sub:find("\ +")):match("%-%- (.-)%:(%d+)\ +") +if source and line then +lastSource = source +lastLine = tonumber(line) +else +for _ in code:sub(prevLinePos, lastInputPos):gmatch("\ +") do +lastLine = lastLine + 1 +end +end +prevLinePos = lastInputPos +r = " -- " .. lastSource .. ":" .. lastLine .. r +end +return r +end +local function indent() +indentLevel = indentLevel + 1 +return newline() +end +local function unindent() +indentLevel = indentLevel - 1 +return newline() +end +local required = {} +local requireStr = "" +local function addRequire(str, name, field) +if not required[str] then +requireStr = requireStr .. "local " .. options["requirePrefix"] .. name .. (" = require(%q)"):format(str) .. (field and "." .. field or "") .. options["newline"] +required[str] = true +end +end +local function getRequire(name) +return options["requirePrefix"] .. name +end +local tags +local function lua(ast, forceTag, ...) +if options["mapLines"] and ast["pos"] then +lastInputPos = ast["pos"] +end +return tags[forceTag or ast["tag"]](ast, ...) +end +tags = setmetatable({ +["Block"] = function(t) +local r = "" +for i = 1, # t - 1, 1 do +r = r .. lua(t[i]) .. newline() +end +if t[# t] then +r = r .. lua(t[# t]) +end +return r +end, ["Do"] = function(t) +return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end" +end, ["Set"] = function(t) +if # t == 2 then +return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs") +elseif # t == 3 then +return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs") +elseif # t == 4 then +if t[3] == "=" then +local r = lua(t[1], "_lhs") .. " = " .. lua({ +t[2], t[1][1], t[4][1] +}, "Op") +for i = 2, math["min"](# t[4], # t[1]), 1 do +r = r .. ", " .. lua({ +t[2], t[1][i], t[4][i] +}, "Op") +end +return r +else +local r = lua(t[1], "_lhs") .. " = " .. lua({ +t[3], t[4][1], t[1][1] +}, "Op") +for i = 2, math["min"](# t[4], # t[1]), 1 do +r = r .. ", " .. lua({ +t[3], t[4][i], t[1][i] +}, "Op") +end +return r +end +else +local r = lua(t[1], "_lhs") .. " = " .. lua({ +t[2], t[1][1], { +["tag"] = "Op", t[4], t[5][1], t[1][1] +} +}, "Op") +for i = 2, math["min"](# t[5], # t[1]), 1 do +r = r .. ", " .. lua({ +t[2], t[1][i], { +["tag"] = "Op", t[4], t[5][i], t[1][i] +} +}, "Op") +end +return r +end +end, ["While"] = function(t) +return "while " .. lua(t[1]) .. " do" .. indent() .. lua(t[2]) .. unindent() .. "end" +end, ["Repeat"] = function(t) +return "repeat" .. indent() .. lua(t[1]) .. unindent() .. "until " .. lua(t[2]) +end, ["If"] = function(t) +local r = "if " .. lua(t[1]) .. " then" .. indent() .. lua(t[2]) .. unindent() +for i = 3, # t - 1, 2 do +r = r .. "elseif " .. lua(t[i]) .. " then" .. indent() .. lua(t[i + 1]) .. unindent() +end +if # t % 2 == 1 then +r = r .. "else" .. indent() .. lua(t[# t]) .. unindent() +end +return r .. "end" +end, ["Fornum"] = function(t) +local r = "for " .. lua(t[1]) .. " = " .. lua(t[2]) .. ", " .. lua(t[3]) +if # t == 5 then +return r .. ", " .. lua(t[4]) .. " do" .. indent() .. lua(t[5]) .. unindent() .. "end" +else +return r .. " do" .. indent() .. lua(t[4]) .. unindent() .. "end" +end +end, ["Forin"] = function(t) +return "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent() .. lua(t[3]) .. unindent() .. "end" +end, ["Local"] = function(t) +local r = "local " .. lua(t[1], "_lhs") +if t[2][1] then +r = r .. " = " .. lua(t[2], "_lhs") +end +return r +end, ["Localrec"] = function(t) +return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword") +end, ["Goto"] = function(t) +return "goto " .. lua(t[1], "Id") +end, ["Label"] = function(t) +return "::" .. lua(t[1], "Id") .. "::" +end, ["Return"] = function(t) +return "return " .. lua(t, "_lhs") +end, ["Break"] = function() +return "break" +end, ["Nil"] = function() +return "nil" +end, ["Dots"] = function() +return "..." +end, ["Boolean"] = function(t) +return tostring(t[1]) +end, ["Number"] = function(t) +return tostring(t[1]) +end, ["String"] = function(t) +return ("%q"):format(t[1]) +end, ["_functionWithoutKeyword"] = function(t) +local r = "(" +local decl = {} +if t[1][1] then +if t[1][1]["tag"] == "ParPair" then +local id = lua(t[1][1][1]) +indentLevel = indentLevel + 1 +table["insert"](decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) +indentLevel = indentLevel - 1 +r = r .. id +else +r = r .. lua(t[1][1]) +end +for i = 2, # t[1], 1 do +if t[1][i]["tag"] == "ParPair" then +local id = lua(t[1][i][1]) +indentLevel = indentLevel + 1 +table["insert"](decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][i][2]) .. " end") +indentLevel = indentLevel - 1 +r = r .. ", " .. id +else +r = r .. ", " .. lua(t[1][i]) +end +end +end +r = r .. ")" .. indent() +for _, d in ipairs(decl) do +r = r .. d .. newline() +end +return r .. lua(t[2]) .. unindent() .. "end" +end, ["Function"] = function(t) +return "function" .. lua(t, "_functionWithoutKeyword") +end, ["Pair"] = function(t) +return "[" .. lua(t[1]) .. "] = " .. lua(t[2]) +end, ["Table"] = function(t) +if # t == 0 then +return "{}" +elseif # t == 1 then +return "{ " .. lua(t, "_lhs") .. " }" +else +return "{" .. indent() .. lua(t, "_lhs") .. unindent() .. "}" +end +end, ["Op"] = function(t) +local r +if # t == 2 then +if type(tags["_opid"][t[1]]) == "string" then +r = tags["_opid"][t[1]] .. " " .. lua(t[2]) +else +r = tags["_opid"][t[1]](t[2]) +end +else +if type(tags["_opid"][t[1]]) == "string" then +r = lua(t[2]) .. " " .. tags["_opid"][t[1]] .. " " .. lua(t[3]) +else +r = tags["_opid"][t[1]](t[2], t[3]) +end +end +return r +end, ["Paren"] = function(t) +return "(" .. lua(t[1]) .. ")" +end, ["Call"] = function(t) +return lua(t[1]) .. "(" .. lua(t, "_lhs", 2) .. ")" +end, ["Invoke"] = function(t) +return lua(t[1]) .. ":" .. lua(t[2], "Id") .. "(" .. lua(t, "_lhs", 3) .. ")" +end, ["_lhs"] = function(t, start) +start = start or 1 +local r +if t[start] then +r = lua(t[start]) +for i = start + 1, # t, 1 do +r = r .. ", " .. lua(t[i]) +end +else +r = "" +end +return r +end, ["Id"] = function(t) +return t[1] +end, ["Index"] = function(t) +return lua(t[1]) .. "[" .. lua(t[2]) .. "]" +end, ["_opid"] = { +["add"] = "+", ["sub"] = "-", ["mul"] = "*", ["div"] = "/", ["idiv"] = "//", ["mod"] = "%", ["pow"] = "^", ["concat"] = "..", ["band"] = "&", ["bor"] = "|", ["bxor"] = "~", ["shl"] = "<<", ["shr"] = ">>", ["eq"] = "==", ["ne"] = "~=", ["lt"] = "<", ["gt"] = ">", ["le"] = "<=", ["ge"] = ">=", ["and"] = "and", ["or"] = "or", ["unm"] = "-", ["len"] = "#", ["bnot"] = "~", ["not"] = "not" +} +}, { ["__index"] = function(self, key) +error("don't know how to compile a " .. tostring(key) .. " to Lua 5.3") +end }) +return requireStr .. lua(ast) .. newline() +end end local lua53 = _() or lua53 package["loaded"]["compiler.lua53"] = lua53 or true local function _() - local function _() - return function(ast, opts) - local options = { - ["indentation"] = "\9", ["newline"] = "\ -", ["requirePrefix"] = "CANDRAN_" - } - local indentLevel = 0 - local function newline() - return options["newline"] .. string["rep"](options["indentation"], indentLevel) - end - local function indent() - indentLevel = indentLevel + 1 - return newline() - end - local function unindent() - indentLevel = indentLevel - 1 - return newline() - end - local required = {} - local requireStr = "" - local function addRequire(str, name, field) - if not required[str] then - requireStr = requireStr .. "local " .. options["requirePrefix"] .. name .. (" = require(%q)"):format(str) .. (field and "." .. field or "") .. options["newline"] - required[str] = true - end - end - local function getRequire(name) - return options["requirePrefix"] .. name - end - local tags - local function lua(ast, forceTag, ...) - return tags[forceTag or ast["tag"]](ast, ...) - end - tags = setmetatable({ - ["Block"] = function(t) - local r = "" - for i = 1, # t - 1, 1 do - r = r .. lua(t[i]) .. newline() - end - if t[# t] then - r = r .. lua(t[# t]) - end - return r - end, ["Do"] = function(t) - return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end" - end, ["Set"] = function(t) - if # t == 2 then - return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs") - else - local r = lua(t[1], "_lhs") .. " = " .. lua({ - t[2], t[1][1], t[3][1] - }, "Op") - for i = 2, math["min"](# t[3], # t[1]), 1 do - r = r .. ", " .. lua({ - t[2], t[1][i], t[3][i] - }, "Op") - end - return r - end - end, ["While"] = function(t) - return "while " .. lua(t[1]) .. " do" .. indent() .. lua(t[2]) .. unindent() .. "end" - end, ["Repeat"] = function(t) - return "repeat" .. indent() .. lua(t[1]) .. unindent() .. "until " .. lua(t[2]) - end, ["If"] = function(t) - local r = "if " .. lua(t[1]) .. " then" .. indent() .. lua(t[2]) .. unindent() - for i = 3, # t - 1, 2 do - r = r .. "elseif " .. lua(t[i]) .. " then" .. indent() .. lua(t[i + 1]) .. unindent() - end - if # t % 2 == 1 then - r = r .. "else" .. indent() .. lua(t[# t]) .. unindent() - end - return r .. "end" - end, ["Fornum"] = function(t) - local r = "for " .. lua(t[1]) .. " = " .. lua(t[2]) .. ", " .. lua(t[3]) - if # t == 5 then - return r .. ", " .. lua(t[4]) .. " do" .. indent() .. lua(t[5]) .. unindent() .. "end" - else - return r .. " do" .. indent() .. lua(t[4]) .. unindent() .. "end" - end - end, ["Forin"] = function(t) - return "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent() .. lua(t[3]) .. unindent() .. "end" - end, ["Local"] = function(t) - local r = "local " .. lua(t[1], "_lhs") - if t[2][1] then - r = r .. " = " .. lua(t[2], "_lhs") - end - return r - end, ["Localrec"] = function(t) - return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword") - end, ["Goto"] = function(t) - return "goto " .. lua(t[1], "Id") - end, ["Label"] = function(t) - return "::" .. lua(t[1], "Id") .. "::" - end, ["Return"] = function(t) - return "return " .. lua(t, "_lhs") - end, ["Break"] = function() - return "break" - end, ["Nil"] = function() - return "nil" - end, ["Dots"] = function() - return "..." - end, ["Boolean"] = function(t) - return tostring(t[1]) - end, ["Number"] = function(t) - return tostring(t[1]) - end, ["String"] = function(t) - return ("%q"):format(t[1]) - end, ["_functionWithoutKeyword"] = function(t) - local r = "(" - local decl = {} - if t[1][1] then - if t[1][1]["tag"] == "ParPair" then - local id = lua(t[1][1][1]) - indentLevel = indentLevel + 1 - table["insert"](decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) - indentLevel = indentLevel - 1 - r = r .. id - else - r = r .. lua(t[1][1]) - end - for i = 2, # t[1], 1 do - if t[1][i]["tag"] == "ParPair" then - local id = lua(t[1][i][1]) - indentLevel = indentLevel + 1 - table["insert"](decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][i][2]) .. " end") - indentLevel = indentLevel - 1 - r = r .. ", " .. id - else - r = r .. ", " .. lua(t[1][i]) - end - end - end - r = r .. ")" .. indent() - for _, d in ipairs(decl) do - r = r .. d .. newline() - end - return r .. lua(t[2]) .. unindent() .. "end" - end, ["Function"] = function(t) - return "function" .. lua(t, "_functionWithoutKeyword") - end, ["Pair"] = function(t) - return "[" .. lua(t[1]) .. "] = " .. lua(t[2]) - end, ["Table"] = function(t) - if # t == 0 then - return "{}" - elseif # t == 1 then - return "{ " .. lua(t, "_lhs") .. " }" - else - return "{" .. indent() .. lua(t, "_lhs") .. unindent() .. "}" - end - end, ["Op"] = function(t) - local r - if # t == 2 then - if type(tags["_opid"][t[1]]) == "string" then - r = tags["_opid"][t[1]] .. " " .. lua(t[2]) - else - r = tags["_opid"][t[1]](t[2]) - end - else - if type(tags["_opid"][t[1]]) == "string" then - r = lua(t[2]) .. " " .. tags["_opid"][t[1]] .. " " .. lua(t[3]) - else - r = tags["_opid"][t[1]](t[2], t[3]) - end - end - return r - end, ["Paren"] = function(t) - return "(" .. lua(t[1]) .. ")" - end, ["Call"] = function(t) - return lua(t[1]) .. "(" .. lua(t, "_lhs", 2) .. ")" - end, ["Invoke"] = function(t) - return lua(t[1]) .. ":" .. lua(t[2], "Id") .. "(" .. lua(t, "_lhs", 3) .. ")" - end, ["_lhs"] = function(t, start) - start = start or 1 - local r - if t[start] then - r = lua(t[start]) - for i = start + 1, # t, 1 do - r = r .. ", " .. lua(t[i]) - end - else - r = "" - end - return r - end, ["Id"] = function(t) - return t[1] - end, ["Index"] = function(t) - return lua(t[1]) .. "[" .. lua(t[2]) .. "]" - end, ["_opid"] = { - ["add"] = "+", ["sub"] = "-", ["mul"] = "*", ["div"] = "/", ["idiv"] = "//", ["mod"] = "%", ["pow"] = "^", ["concat"] = "..", ["band"] = "&", ["bor"] = "|", ["bxor"] = "~", ["shl"] = "<<", ["shr"] = ">>", ["eq"] = "==", ["ne"] = "~=", ["lt"] = "<", ["gt"] = ">", ["le"] = "<=", ["ge"] = ">=", ["and"] = "and", ["or"] = "or", ["unm"] = "-", ["len"] = "#", ["bnot"] = "~", ["not"] = "not" - } - }, { ["__index"] = function(self, key) - error("don't know how to compile a " .. tostring(key) .. " to Lua 5.3") - end }) - tags["_opid"]["idiv"] = function(left, right) - return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")" - end - tags["_opid"]["band"] = function(left, right) - addRequire("bit", "band", "band") - return getRequire("band") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" - end - tags["_opid"]["bor"] = function(left, right) - addRequire("bit", "bor", "bor") - return getRequire("bor") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" - end - tags["_opid"]["bxor"] = function(left, right) - addRequire("bit", "bxor", "bxor") - return getRequire("bxor") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" - end - tags["_opid"]["shl"] = function(left, right) - addRequire("bit", "lshift", "lshift") - return getRequire("lshift") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" - end - tags["_opid"]["shr"] = function(left, right) - addRequire("bit", "rshift", "rshift") - return getRequire("rshift") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" - end - tags["_opid"]["bnot"] = function(right) - addRequire("bit", "bnot", "bnot") - return getRequire("bnot") .. "(" .. lua(right) .. ")" - end - if opts then - for k, v in pairs(opts) do - options[k] = v - end - end - local r = lua(ast) - return requireStr .. r - end - end - local lua53 = _() or lua53 - package["loaded"]["compiler.lua53"] = lua53 or true - return lua53 +local function _() +return function(code, ast, options) +local lastInputPos = 1 +local prevLinePos = 1 +local lastSource = "nil" +local lastLine = 1 +local indentLevel = 0 +local function newline() +local r = options["newline"] .. string["rep"](options["indentation"], indentLevel) +if options["mapLines"] then +local sub = code:sub(lastInputPos) +local source, line = sub:sub(1, sub:find("\ +")):match("%-%- (.-)%:(%d+)\ +") +if source and line then +lastSource = source +lastLine = tonumber(line) +else +for _ in code:sub(prevLinePos, lastInputPos):gmatch("\ +") do +lastLine = lastLine + 1 +end +end +prevLinePos = lastInputPos +r = " -- " .. lastSource .. ":" .. lastLine .. r +end +return r +end +local function indent() +indentLevel = indentLevel + 1 +return newline() +end +local function unindent() +indentLevel = indentLevel - 1 +return newline() +end +local required = {} +local requireStr = "" +local function addRequire(str, name, field) +if not required[str] then +requireStr = requireStr .. "local " .. options["requirePrefix"] .. name .. (" = require(%q)"):format(str) .. (field and "." .. field or "") .. options["newline"] +required[str] = true +end +end +local function getRequire(name) +return options["requirePrefix"] .. name +end +local tags +local function lua(ast, forceTag, ...) +if options["mapLines"] and ast["pos"] then +lastInputPos = ast["pos"] +end +return tags[forceTag or ast["tag"]](ast, ...) +end +tags = setmetatable({ +["Block"] = function(t) +local r = "" +for i = 1, # t - 1, 1 do +r = r .. lua(t[i]) .. newline() +end +if t[# t] then +r = r .. lua(t[# t]) +end +return r +end, ["Do"] = function(t) +return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end" +end, ["Set"] = function(t) +if # t == 2 then +return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs") +elseif # t == 3 then +return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs") +elseif # t == 4 then +if t[3] == "=" then +local r = lua(t[1], "_lhs") .. " = " .. lua({ +t[2], t[1][1], t[4][1] +}, "Op") +for i = 2, math["min"](# t[4], # t[1]), 1 do +r = r .. ", " .. lua({ +t[2], t[1][i], t[4][i] +}, "Op") +end +return r +else +local r = lua(t[1], "_lhs") .. " = " .. lua({ +t[3], t[4][1], t[1][1] +}, "Op") +for i = 2, math["min"](# t[4], # t[1]), 1 do +r = r .. ", " .. lua({ +t[3], t[4][i], t[1][i] +}, "Op") +end +return r +end +else +local r = lua(t[1], "_lhs") .. " = " .. lua({ +t[2], t[1][1], { +["tag"] = "Op", t[4], t[5][1], t[1][1] +} +}, "Op") +for i = 2, math["min"](# t[5], # t[1]), 1 do +r = r .. ", " .. lua({ +t[2], t[1][i], { +["tag"] = "Op", t[4], t[5][i], t[1][i] +} +}, "Op") +end +return r +end +end, ["While"] = function(t) +return "while " .. lua(t[1]) .. " do" .. indent() .. lua(t[2]) .. unindent() .. "end" +end, ["Repeat"] = function(t) +return "repeat" .. indent() .. lua(t[1]) .. unindent() .. "until " .. lua(t[2]) +end, ["If"] = function(t) +local r = "if " .. lua(t[1]) .. " then" .. indent() .. lua(t[2]) .. unindent() +for i = 3, # t - 1, 2 do +r = r .. "elseif " .. lua(t[i]) .. " then" .. indent() .. lua(t[i + 1]) .. unindent() +end +if # t % 2 == 1 then +r = r .. "else" .. indent() .. lua(t[# t]) .. unindent() +end +return r .. "end" +end, ["Fornum"] = function(t) +local r = "for " .. lua(t[1]) .. " = " .. lua(t[2]) .. ", " .. lua(t[3]) +if # t == 5 then +return r .. ", " .. lua(t[4]) .. " do" .. indent() .. lua(t[5]) .. unindent() .. "end" +else +return r .. " do" .. indent() .. lua(t[4]) .. unindent() .. "end" +end +end, ["Forin"] = function(t) +return "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent() .. lua(t[3]) .. unindent() .. "end" +end, ["Local"] = function(t) +local r = "local " .. lua(t[1], "_lhs") +if t[2][1] then +r = r .. " = " .. lua(t[2], "_lhs") +end +return r +end, ["Localrec"] = function(t) +return "local function " .. lua(t[1][1]) .. lua(t[2][1], "_functionWithoutKeyword") +end, ["Goto"] = function(t) +return "goto " .. lua(t[1], "Id") +end, ["Label"] = function(t) +return "::" .. lua(t[1], "Id") .. "::" +end, ["Return"] = function(t) +return "return " .. lua(t, "_lhs") +end, ["Break"] = function() +return "break" +end, ["Nil"] = function() +return "nil" +end, ["Dots"] = function() +return "..." +end, ["Boolean"] = function(t) +return tostring(t[1]) +end, ["Number"] = function(t) +return tostring(t[1]) +end, ["String"] = function(t) +return ("%q"):format(t[1]) +end, ["_functionWithoutKeyword"] = function(t) +local r = "(" +local decl = {} +if t[1][1] then +if t[1][1]["tag"] == "ParPair" then +local id = lua(t[1][1][1]) +indentLevel = indentLevel + 1 +table["insert"](decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id) +indentLevel = indentLevel - 1 +r = r .. id +else +r = r .. lua(t[1][1]) +end +for i = 2, # t[1], 1 do +if t[1][i]["tag"] == "ParPair" then +local id = lua(t[1][i][1]) +indentLevel = indentLevel + 1 +table["insert"](decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][i][2]) .. " end") +indentLevel = indentLevel - 1 +r = r .. ", " .. id +else +r = r .. ", " .. lua(t[1][i]) +end +end +end +r = r .. ")" .. indent() +for _, d in ipairs(decl) do +r = r .. d .. newline() +end +return r .. lua(t[2]) .. unindent() .. "end" +end, ["Function"] = function(t) +return "function" .. lua(t, "_functionWithoutKeyword") +end, ["Pair"] = function(t) +return "[" .. lua(t[1]) .. "] = " .. lua(t[2]) +end, ["Table"] = function(t) +if # t == 0 then +return "{}" +elseif # t == 1 then +return "{ " .. lua(t, "_lhs") .. " }" +else +return "{" .. indent() .. lua(t, "_lhs") .. unindent() .. "}" +end +end, ["Op"] = function(t) +local r +if # t == 2 then +if type(tags["_opid"][t[1]]) == "string" then +r = tags["_opid"][t[1]] .. " " .. lua(t[2]) +else +r = tags["_opid"][t[1]](t[2]) +end +else +if type(tags["_opid"][t[1]]) == "string" then +r = lua(t[2]) .. " " .. tags["_opid"][t[1]] .. " " .. lua(t[3]) +else +r = tags["_opid"][t[1]](t[2], t[3]) +end +end +return r +end, ["Paren"] = function(t) +return "(" .. lua(t[1]) .. ")" +end, ["Call"] = function(t) +return lua(t[1]) .. "(" .. lua(t, "_lhs", 2) .. ")" +end, ["Invoke"] = function(t) +return lua(t[1]) .. ":" .. lua(t[2], "Id") .. "(" .. lua(t, "_lhs", 3) .. ")" +end, ["_lhs"] = function(t, start) +start = start or 1 +local r +if t[start] then +r = lua(t[start]) +for i = start + 1, # t, 1 do +r = r .. ", " .. lua(t[i]) +end +else +r = "" +end +return r +end, ["Id"] = function(t) +return t[1] +end, ["Index"] = function(t) +return lua(t[1]) .. "[" .. lua(t[2]) .. "]" +end, ["_opid"] = { +["add"] = "+", ["sub"] = "-", ["mul"] = "*", ["div"] = "/", ["idiv"] = "//", ["mod"] = "%", ["pow"] = "^", ["concat"] = "..", ["band"] = "&", ["bor"] = "|", ["bxor"] = "~", ["shl"] = "<<", ["shr"] = ">>", ["eq"] = "==", ["ne"] = "~=", ["lt"] = "<", ["gt"] = ">", ["le"] = "<=", ["ge"] = ">=", ["and"] = "and", ["or"] = "or", ["unm"] = "-", ["len"] = "#", ["bnot"] = "~", ["not"] = "not" +} +}, { ["__index"] = function(self, key) +error("don't know how to compile a " .. tostring(key) .. " to Lua 5.3") +end }) +tags["_opid"]["idiv"] = function(left, right) +return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")" +end +tags["_opid"]["band"] = function(left, right) +addRequire("bit", "band", "band") +return getRequire("band") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" +end +tags["_opid"]["bor"] = function(left, right) +addRequire("bit", "bor", "bor") +return getRequire("bor") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" +end +tags["_opid"]["bxor"] = function(left, right) +addRequire("bit", "bxor", "bxor") +return getRequire("bxor") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" +end +tags["_opid"]["shl"] = function(left, right) +addRequire("bit", "lshift", "lshift") +return getRequire("lshift") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" +end +tags["_opid"]["shr"] = function(left, right) +addRequire("bit", "rshift", "rshift") +return getRequire("rshift") .. "(" .. lua(left) .. ", " .. lua(right) .. ")" +end +tags["_opid"]["bnot"] = function(right) +addRequire("bit", "bnot", "bnot") +return getRequire("bnot") .. "(" .. lua(right) .. ")" +end +return requireStr .. lua(ast) .. newline() +end +end +local lua53 = _() or lua53 +package["loaded"]["compiler.lua53"] = lua53 or true +return lua53 end local luajit = _() or luajit package["loaded"]["compiler.luajit"] = luajit or true local function _() - local scope = {} - scope["lineno"] = function(s, i) - if i == 1 then - return 1, 1 - end - local l, lastline = 0, "" - s = s:sub(1, i) .. "\ +local scope = {} +scope["lineno"] = function(s, i) +if i == 1 then +return 1, 1 +end +local l, lastline = 0, "" +s = s:sub(1, i) .. "\ " - for line in s:gmatch("[^\ +for line in s:gmatch("[^\ ]*[\ ]") do - l = l + 1 - lastline = line - end - local c = lastline:len() - 1 - return l, c ~= 0 and c or 1 - end - scope["new_scope"] = function(env) - if not env["scope"] then - env["scope"] = 0 - else - env["scope"] = env["scope"] + 1 - end - local scope = env["scope"] - env["maxscope"] = scope - env[scope] = {} - env[scope]["label"] = {} - env[scope]["local"] = {} - env[scope]["goto"] = {} - end - scope["begin_scope"] = function(env) - env["scope"] = env["scope"] + 1 - end - scope["end_scope"] = function(env) - env["scope"] = env["scope"] - 1 - end - scope["new_function"] = function(env) - if not env["fscope"] then - env["fscope"] = 0 - else - env["fscope"] = env["fscope"] + 1 - end - local fscope = env["fscope"] - env["function"][fscope] = {} - end - scope["begin_function"] = function(env) - env["fscope"] = env["fscope"] + 1 - end - scope["end_function"] = function(env) - env["fscope"] = env["fscope"] - 1 - end - scope["begin_loop"] = function(env) - if not env["loop"] then - env["loop"] = 1 - else - env["loop"] = env["loop"] + 1 - end - end - scope["end_loop"] = function(env) - env["loop"] = env["loop"] - 1 - end - scope["insideloop"] = function(env) - return env["loop"] and env["loop"] > 0 - end - return scope +l = l + 1 +lastline = line +end +local c = lastline:len() - 1 +return l, c ~= 0 and c or 1 +end +scope["new_scope"] = function(env) +if not env["scope"] then +env["scope"] = 0 +else +env["scope"] = env["scope"] + 1 +end +local scope = env["scope"] +env["maxscope"] = scope +env[scope] = {} +env[scope]["label"] = {} +env[scope]["local"] = {} +env[scope]["goto"] = {} +end +scope["begin_scope"] = function(env) +env["scope"] = env["scope"] + 1 +end +scope["end_scope"] = function(env) +env["scope"] = env["scope"] - 1 +end +scope["new_function"] = function(env) +if not env["fscope"] then +env["fscope"] = 0 +else +env["fscope"] = env["fscope"] + 1 +end +local fscope = env["fscope"] +env["function"][fscope] = {} +end +scope["begin_function"] = function(env) +env["fscope"] = env["fscope"] + 1 +end +scope["end_function"] = function(env) +env["fscope"] = env["fscope"] - 1 +end +scope["begin_loop"] = function(env) +if not env["loop"] then +env["loop"] = 1 +else +env["loop"] = env["loop"] + 1 +end +end +scope["end_loop"] = function(env) +env["loop"] = env["loop"] - 1 +end +scope["insideloop"] = function(env) +return env["loop"] and env["loop"] > 0 +end +return scope end local scope = _() or scope -package["loaded"]["lua-parser.scope"] = scope or true +package["loaded"]["lib.lua-parser.scope"] = scope or true local function _() - local scope = require("lua-parser.scope") - local lineno = scope["lineno"] - local new_scope, end_scope = scope["new_scope"], scope["end_scope"] - local new_function, end_function = scope["new_function"], scope["end_function"] - local begin_loop, end_loop = scope["begin_loop"], scope["end_loop"] - local insideloop = scope["insideloop"] - local function syntaxerror(errorinfo, pos, msg) - local l, c = lineno(errorinfo["subject"], pos) - local error_msg = "%s:%d:%d: syntax error, %s" - return string["format"](error_msg, errorinfo["filename"], l, c, msg) - end - local function exist_label(env, scope, stm) - local l = stm[1] - for s = scope, 0, - 1 do - if env[s]["label"][l] then - return true - end - end - return false - end - local function set_label(env, label, pos) - local scope = env["scope"] - local l = env[scope]["label"][label] - if not l then - env[scope]["label"][label] = { - ["name"] = label, ["pos"] = pos - } - return true - else - local msg = "label '%s' already defined at line %d" - local line = lineno(env["errorinfo"]["subject"], l["pos"]) - msg = string["format"](msg, label, line) - return nil, syntaxerror(env["errorinfo"], pos, msg) - end - end - local function set_pending_goto(env, stm) - local scope = env["scope"] - table["insert"](env[scope]["goto"], stm) - return true - end - local function verify_pending_gotos(env) - for s = env["maxscope"], 0, - 1 do - for k, v in ipairs(env[s]["goto"]) do - if not exist_label(env, s, v) then - local msg = "no visible label '%s' for " - msg = string["format"](msg, v[1]) - return nil, syntaxerror(env["errorinfo"], v["pos"], msg) - end - end - end - return true - end - local function set_vararg(env, is_vararg) - env["function"][env["fscope"]]["is_vararg"] = is_vararg - end - local traverse_stm, traverse_exp, traverse_var - local traverse_block, traverse_explist, traverse_varlist, traverse_parlist - traverse_parlist = function(env, parlist) - local len = # parlist - local is_vararg = false - if len > 0 and parlist[len]["tag"] == "Dots" then - is_vararg = true - end - set_vararg(env, is_vararg) - return true - end - local function traverse_function(env, exp) - new_function(env) - new_scope(env) - local status, msg = traverse_parlist(env, exp[1]) - if not status then - return status, msg - end - status, msg = traverse_block(env, exp[2]) - if not status then - return status, msg - end - end_scope(env) - end_function(env) - return true - end - local function traverse_op(env, exp) - local status, msg = traverse_exp(env, exp[2]) - if not status then - return status, msg - end - if exp[3] then - status, msg = traverse_exp(env, exp[3]) - if not status then - return status, msg - end - end - return true - end - local function traverse_paren(env, exp) - local status, msg = traverse_exp(env, exp[1]) - if not status then - return status, msg - end - return true - end - local function traverse_table(env, fieldlist) - for k, v in ipairs(fieldlist) do - local tag = v["tag"] - if tag == "Pair" then - local status, msg = traverse_exp(env, v[1]) - if not status then - return status, msg - end - status, msg = traverse_exp(env, v[2]) - if not status then - return status, msg - end - else - local status, msg = traverse_exp(env, v) - if not status then - return status, msg - end - end - end - return true - end - local function traverse_vararg(env, exp) - if not env["function"][env["fscope"]]["is_vararg"] then - local msg = "cannot use '...' outside a vararg function" - return nil, syntaxerror(env["errorinfo"], exp["pos"], msg) - end - return true - end - local function traverse_call(env, call) - local status, msg = traverse_exp(env, call[1]) - if not status then - return status, msg - end - for i = 2, # call do - status, msg = traverse_exp(env, call[i]) - if not status then - return status, msg - end - end - return true - end - local function traverse_invoke(env, invoke) - local status, msg = traverse_exp(env, invoke[1]) - if not status then - return status, msg - end - for i = 3, # invoke do - status, msg = traverse_exp(env, invoke[i]) - if not status then - return status, msg - end - end - return true - end - local function traverse_assignment(env, stm) - local status, msg = traverse_varlist(env, stm[1]) - if not status then - return status, msg - end - status, msg = traverse_explist(env, stm[2]) - if not status then - return status, msg - end - return true - end - local function traverse_break(env, stm) - if not insideloop(env) then - local msg = " not inside a loop" - return nil, syntaxerror(env["errorinfo"], stm["pos"], msg) - end - return true - end - local function traverse_forin(env, stm) - begin_loop(env) - new_scope(env) - local status, msg = traverse_explist(env, stm[2]) - if not status then - return status, msg - end - status, msg = traverse_block(env, stm[3]) - if not status then - return status, msg - end - end_scope(env) - end_loop(env) - return true - end - local function traverse_fornum(env, stm) - local status, msg - begin_loop(env) - new_scope(env) - status, msg = traverse_exp(env, stm[2]) - if not status then - return status, msg - end - status, msg = traverse_exp(env, stm[3]) - if not status then - return status, msg - end - if stm[5] then - status, msg = traverse_exp(env, stm[4]) - if not status then - return status, msg - end - status, msg = traverse_block(env, stm[5]) - if not status then - return status, msg - end - else - status, msg = traverse_block(env, stm[4]) - if not status then - return status, msg - end - end - end_scope(env) - end_loop(env) - return true - end - local function traverse_goto(env, stm) - local status, msg = set_pending_goto(env, stm) - if not status then - return status, msg - end - return true - end - local function traverse_if(env, stm) - local len = # stm - if len % 2 == 0 then - for i = 1, len, 2 do - local status, msg = traverse_exp(env, stm[i]) - if not status then - return status, msg - end - status, msg = traverse_block(env, stm[i + 1]) - if not status then - return status, msg - end - end - else - for i = 1, len - 1, 2 do - local status, msg = traverse_exp(env, stm[i]) - if not status then - return status, msg - end - status, msg = traverse_block(env, stm[i + 1]) - if not status then - return status, msg - end - end - local status, msg = traverse_block(env, stm[len]) - if not status then - return status, msg - end - end - return true - end - local function traverse_label(env, stm) - local status, msg = set_label(env, stm[1], stm["pos"]) - if not status then - return status, msg - end - return true - end - local function traverse_let(env, stm) - local status, msg = traverse_explist(env, stm[2]) - if not status then - return status, msg - end - return true - end - local function traverse_letrec(env, stm) - local status, msg = traverse_exp(env, stm[2][1]) - if not status then - return status, msg - end - return true - end - local function traverse_repeat(env, stm) - begin_loop(env) - local status, msg = traverse_block(env, stm[1]) - if not status then - return status, msg - end - status, msg = traverse_exp(env, stm[2]) - if not status then - return status, msg - end - end_loop(env) - return true - end - local function traverse_return(env, stm) - local status, msg = traverse_explist(env, stm) - if not status then - return status, msg - end - return true - end - local function traverse_while(env, stm) - begin_loop(env) - local status, msg = traverse_exp(env, stm[1]) - if not status then - return status, msg - end - status, msg = traverse_block(env, stm[2]) - if not status then - return status, msg - end - end_loop(env) - return true - end - traverse_var = function(env, var) - local tag = var["tag"] - if tag == "Id" then - return true - elseif tag == "Index" then - local status, msg = traverse_exp(env, var[1]) - if not status then - return status, msg - end - status, msg = traverse_exp(env, var[2]) - if not status then - return status, msg - end - return true - else - error("expecting a variable, but got a " .. tag) - end - end - traverse_varlist = function(env, varlist) - for k, v in ipairs(varlist) do - local status, msg = traverse_var(env, v) - if not status then - return status, msg - end - end - return true - end - traverse_exp = function(env, exp) - local tag = exp["tag"] - if tag == "Nil" or tag == "Boolean" or tag == "Number" or tag == "String" then - return true - elseif tag == "Dots" then - return traverse_vararg(env, exp) - elseif tag == "Function" then - return traverse_function(env, exp) - elseif tag == "Table" then - return traverse_table(env, exp) - elseif tag == "Op" then - return traverse_op(env, exp) - elseif tag == "Paren" then - return traverse_paren(env, exp) - elseif tag == "Call" then - return traverse_call(env, exp) - elseif tag == "Invoke" then - return traverse_invoke(env, exp) - elseif tag == "Id" or tag == "Index" then - return traverse_var(env, exp) - else - error("expecting an expression, but got a " .. tag) - end - end - traverse_explist = function(env, explist) - for k, v in ipairs(explist) do - local status, msg = traverse_exp(env, v) - if not status then - return status, msg - end - end - return true - end - traverse_stm = function(env, stm) - local tag = stm["tag"] - if tag == "Do" then - return traverse_block(env, stm) - elseif tag == "Set" then - return traverse_assignment(env, stm) - elseif tag == "While" then - return traverse_while(env, stm) - elseif tag == "Repeat" then - return traverse_repeat(env, stm) - elseif tag == "If" then - return traverse_if(env, stm) - elseif tag == "Fornum" then - return traverse_fornum(env, stm) - elseif tag == "Forin" then - return traverse_forin(env, stm) - elseif tag == "Local" then - return traverse_let(env, stm) - elseif tag == "Localrec" then - return traverse_letrec(env, stm) - elseif tag == "Goto" then - return traverse_goto(env, stm) - elseif tag == "Label" then - return traverse_label(env, stm) - elseif tag == "Return" then - return traverse_return(env, stm) - elseif tag == "Break" then - return traverse_break(env, stm) - elseif tag == "Call" then - return traverse_call(env, stm) - elseif tag == "Invoke" then - return traverse_invoke(env, stm) - else - error("expecting a statement, but got a " .. tag) - end - end - traverse_block = function(env, block) - local l = {} - new_scope(env) - for k, v in ipairs(block) do - local status, msg = traverse_stm(env, v) - if not status then - return status, msg - end - end - end_scope(env) - return true - end - local function traverse(ast, errorinfo) - assert(type(ast) == "table") - assert(type(errorinfo) == "table") - local env = { - ["errorinfo"] = errorinfo, ["function"] = {} - } - new_function(env) - set_vararg(env, true) - local status, msg = traverse_block(env, ast) - if not status then - return status, msg - end - end_function(env) - status, msg = verify_pending_gotos(env) - if not status then - return status, msg - end - return ast - end - return { - ["validate"] = traverse, ["syntaxerror"] = syntaxerror - } +local scope = require("lib.lua-parser.scope") +local lineno = scope["lineno"] +local new_scope, end_scope = scope["new_scope"], scope["end_scope"] +local new_function, end_function = scope["new_function"], scope["end_function"] +local begin_loop, end_loop = scope["begin_loop"], scope["end_loop"] +local insideloop = scope["insideloop"] +local function syntaxerror(errorinfo, pos, msg) +local l, c = lineno(errorinfo["subject"], pos) +local error_msg = "%s:%d:%d: syntax error, %s" +return string["format"](error_msg, errorinfo["filename"], l, c, msg) +end +local function exist_label(env, scope, stm) +local l = stm[1] +for s = scope, 0, - 1 do +if env[s]["label"][l] then +return true +end +end +return false +end +local function set_label(env, label, pos) +local scope = env["scope"] +local l = env[scope]["label"][label] +if not l then +env[scope]["label"][label] = { +["name"] = label, ["pos"] = pos +} +return true +else +local msg = "label '%s' already defined at line %d" +local line = lineno(env["errorinfo"]["subject"], l["pos"]) +msg = string["format"](msg, label, line) +return nil, syntaxerror(env["errorinfo"], pos, msg) +end +end +local function set_pending_goto(env, stm) +local scope = env["scope"] +table["insert"](env[scope]["goto"], stm) +return true +end +local function verify_pending_gotos(env) +for s = env["maxscope"], 0, - 1 do +for k, v in ipairs(env[s]["goto"]) do +if not exist_label(env, s, v) then +local msg = "no visible label '%s' for " +msg = string["format"](msg, v[1]) +return nil, syntaxerror(env["errorinfo"], v["pos"], msg) +end +end +end +return true +end +local function set_vararg(env, is_vararg) +env["function"][env["fscope"]]["is_vararg"] = is_vararg +end +local traverse_stm, traverse_exp, traverse_var +local traverse_block, traverse_explist, traverse_varlist, traverse_parlist +traverse_parlist = function(env, parlist) +local len = # parlist +local is_vararg = false +if len > 0 and parlist[len]["tag"] == "Dots" then +is_vararg = true +end +set_vararg(env, is_vararg) +return true +end +local function traverse_function(env, exp) +new_function(env) +new_scope(env) +local status, msg = traverse_parlist(env, exp[1]) +if not status then +return status, msg +end +status, msg = traverse_block(env, exp[2]) +if not status then +return status, msg +end +end_scope(env) +end_function(env) +return true +end +local function traverse_op(env, exp) +local status, msg = traverse_exp(env, exp[2]) +if not status then +return status, msg +end +if exp[3] then +status, msg = traverse_exp(env, exp[3]) +if not status then +return status, msg +end +end +return true +end +local function traverse_paren(env, exp) +local status, msg = traverse_exp(env, exp[1]) +if not status then +return status, msg +end +return true +end +local function traverse_table(env, fieldlist) +for k, v in ipairs(fieldlist) do +local tag = v["tag"] +if tag == "Pair" then +local status, msg = traverse_exp(env, v[1]) +if not status then +return status, msg +end +status, msg = traverse_exp(env, v[2]) +if not status then +return status, msg +end +else +local status, msg = traverse_exp(env, v) +if not status then +return status, msg +end +end +end +return true +end +local function traverse_vararg(env, exp) +if not env["function"][env["fscope"]]["is_vararg"] then +local msg = "cannot use '...' outside a vararg function" +return nil, syntaxerror(env["errorinfo"], exp["pos"], msg) +end +return true +end +local function traverse_call(env, call) +local status, msg = traverse_exp(env, call[1]) +if not status then +return status, msg +end +for i = 2, # call do +status, msg = traverse_exp(env, call[i]) +if not status then +return status, msg +end +end +return true +end +local function traverse_invoke(env, invoke) +local status, msg = traverse_exp(env, invoke[1]) +if not status then +return status, msg +end +for i = 3, # invoke do +status, msg = traverse_exp(env, invoke[i]) +if not status then +return status, msg +end +end +return true +end +local function traverse_assignment(env, stm) +local status, msg = traverse_varlist(env, stm[1]) +if not status then +return status, msg +end +status, msg = traverse_explist(env, stm[2]) +if not status then +return status, msg +end +return true +end +local function traverse_break(env, stm) +if not insideloop(env) then +local msg = " not inside a loop" +return nil, syntaxerror(env["errorinfo"], stm["pos"], msg) +end +return true +end +local function traverse_forin(env, stm) +begin_loop(env) +new_scope(env) +local status, msg = traverse_explist(env, stm[2]) +if not status then +return status, msg +end +status, msg = traverse_block(env, stm[3]) +if not status then +return status, msg +end +end_scope(env) +end_loop(env) +return true +end +local function traverse_fornum(env, stm) +local status, msg +begin_loop(env) +new_scope(env) +status, msg = traverse_exp(env, stm[2]) +if not status then +return status, msg +end +status, msg = traverse_exp(env, stm[3]) +if not status then +return status, msg +end +if stm[5] then +status, msg = traverse_exp(env, stm[4]) +if not status then +return status, msg +end +status, msg = traverse_block(env, stm[5]) +if not status then +return status, msg +end +else +status, msg = traverse_block(env, stm[4]) +if not status then +return status, msg +end +end +end_scope(env) +end_loop(env) +return true +end +local function traverse_goto(env, stm) +local status, msg = set_pending_goto(env, stm) +if not status then +return status, msg +end +return true +end +local function traverse_if(env, stm) +local len = # stm +if len % 2 == 0 then +for i = 1, len, 2 do +local status, msg = traverse_exp(env, stm[i]) +if not status then +return status, msg +end +status, msg = traverse_block(env, stm[i + 1]) +if not status then +return status, msg +end +end +else +for i = 1, len - 1, 2 do +local status, msg = traverse_exp(env, stm[i]) +if not status then +return status, msg +end +status, msg = traverse_block(env, stm[i + 1]) +if not status then +return status, msg +end +end +local status, msg = traverse_block(env, stm[len]) +if not status then +return status, msg +end +end +return true +end +local function traverse_label(env, stm) +local status, msg = set_label(env, stm[1], stm["pos"]) +if not status then +return status, msg +end +return true +end +local function traverse_let(env, stm) +local status, msg = traverse_explist(env, stm[2]) +if not status then +return status, msg +end +return true +end +local function traverse_letrec(env, stm) +local status, msg = traverse_exp(env, stm[2][1]) +if not status then +return status, msg +end +return true +end +local function traverse_repeat(env, stm) +begin_loop(env) +local status, msg = traverse_block(env, stm[1]) +if not status then +return status, msg +end +status, msg = traverse_exp(env, stm[2]) +if not status then +return status, msg +end +end_loop(env) +return true +end +local function traverse_return(env, stm) +local status, msg = traverse_explist(env, stm) +if not status then +return status, msg +end +return true +end +local function traverse_while(env, stm) +begin_loop(env) +local status, msg = traverse_exp(env, stm[1]) +if not status then +return status, msg +end +status, msg = traverse_block(env, stm[2]) +if not status then +return status, msg +end +end_loop(env) +return true +end +traverse_var = function(env, var) +local tag = var["tag"] +if tag == "Id" then +return true +elseif tag == "Index" then +local status, msg = traverse_exp(env, var[1]) +if not status then +return status, msg +end +status, msg = traverse_exp(env, var[2]) +if not status then +return status, msg +end +return true +else +error("expecting a variable, but got a " .. tag) +end +end +traverse_varlist = function(env, varlist) +for k, v in ipairs(varlist) do +local status, msg = traverse_var(env, v) +if not status then +return status, msg +end +end +return true +end +traverse_exp = function(env, exp) +local tag = exp["tag"] +if tag == "Nil" or tag == "Boolean" or tag == "Number" or tag == "String" then +return true +elseif tag == "Dots" then +return traverse_vararg(env, exp) +elseif tag == "Function" then +return traverse_function(env, exp) +elseif tag == "Table" then +return traverse_table(env, exp) +elseif tag == "Op" then +return traverse_op(env, exp) +elseif tag == "Paren" then +return traverse_paren(env, exp) +elseif tag == "Call" then +return traverse_call(env, exp) +elseif tag == "Invoke" then +return traverse_invoke(env, exp) +elseif tag == "Id" or tag == "Index" then +return traverse_var(env, exp) +else +error("expecting an expression, but got a " .. tag) +end +end +traverse_explist = function(env, explist) +for k, v in ipairs(explist) do +local status, msg = traverse_exp(env, v) +if not status then +return status, msg +end +end +return true +end +traverse_stm = function(env, stm) +local tag = stm["tag"] +if tag == "Do" then +return traverse_block(env, stm) +elseif tag == "Set" then +return traverse_assignment(env, stm) +elseif tag == "While" then +return traverse_while(env, stm) +elseif tag == "Repeat" then +return traverse_repeat(env, stm) +elseif tag == "If" then +return traverse_if(env, stm) +elseif tag == "Fornum" then +return traverse_fornum(env, stm) +elseif tag == "Forin" then +return traverse_forin(env, stm) +elseif tag == "Local" then +return traverse_let(env, stm) +elseif tag == "Localrec" then +return traverse_letrec(env, stm) +elseif tag == "Goto" then +return traverse_goto(env, stm) +elseif tag == "Label" then +return traverse_label(env, stm) +elseif tag == "Return" then +return traverse_return(env, stm) +elseif tag == "Break" then +return traverse_break(env, stm) +elseif tag == "Call" then +return traverse_call(env, stm) +elseif tag == "Invoke" then +return traverse_invoke(env, stm) +else +error("expecting a statement, but got a " .. tag) +end +end +traverse_block = function(env, block) +local l = {} +new_scope(env) +for k, v in ipairs(block) do +local status, msg = traverse_stm(env, v) +if not status then +return status, msg +end +end +end_scope(env) +return true +end +local function traverse(ast, errorinfo) +assert(type(ast) == "table") +assert(type(errorinfo) == "table") +local env = { +["errorinfo"] = errorinfo, ["function"] = {} +} +new_function(env) +set_vararg(env, true) +local status, msg = traverse_block(env, ast) +if not status then +return status, msg +end +end_function(env) +status, msg = verify_pending_gotos(env) +if not status then +return status, msg +end +return ast +end +return { +["validate"] = traverse, ["syntaxerror"] = syntaxerror +} end local validator = _() or validator -package["loaded"]["lua-parser.validator"] = validator or true +package["loaded"]["lib.lua-parser.validator"] = validator or true local function _() - local pp = {} - local block2str, stm2str, exp2str, var2str - local explist2str, varlist2str, parlist2str, fieldlist2str - local function iscntrl(x) - if (x >= 0 and x <= 31) or (x == 127) then - return true - end - return false - end - local function isprint(x) - return not iscntrl(x) - end - local function fixed_string(str) - local new_str = "" - for i = 1, string["len"](str) do - char = string["byte"](str, i) - if char == 34 then - new_str = new_str .. string["format"]("\\\"") - elseif char == 92 then - new_str = new_str .. string["format"]("\\\\") - elseif char == 7 then - new_str = new_str .. string["format"]("\\a") - elseif char == 8 then - new_str = new_str .. string["format"]("\\b") - elseif char == 12 then - new_str = new_str .. string["format"]("\\f") - elseif char == 10 then - new_str = new_str .. string["format"]("\\n") - elseif char == 13 then - new_str = new_str .. string["format"]("\\r") - elseif char == 9 then - new_str = new_str .. string["format"]("\\t") - elseif char == 11 then - new_str = new_str .. string["format"]("\\v") - else - if isprint(char) then - new_str = new_str .. string["format"]("%c", char) - else - new_str = new_str .. string["format"]("\\%03d", char) - end - end - end - return new_str - end - local function name2str(name) - return string["format"]("\"%s\"", name) - end - local function boolean2str(b) - return string["format"]("\"%s\"", tostring(b)) - end - local function number2str(n) - return string["format"]("\"%s\"", tostring(n)) - end - local function string2str(s) - return string["format"]("\"%s\"", fixed_string(s)) - end - var2str = function(var) - local tag = var["tag"] - local str = "`" .. tag - if tag == "Id" then - str = str .. " " .. name2str(var[1]) - elseif tag == "Index" then - str = str .. "{ " - str = str .. exp2str(var[1]) .. ", " - str = str .. exp2str(var[2]) - str = str .. " }" - else - error("expecting a variable, but got a " .. tag) - end - return str - end - varlist2str = function(varlist) - local l = {} - for k, v in ipairs(varlist) do - l[k] = var2str(v) - end - return "{ " .. table["concat"](l, ", ") .. " }" - end - parlist2str = function(parlist) - local l = {} - local len = # parlist - local is_vararg = false - if len > 0 and parlist[len]["tag"] == "Dots" then - is_vararg = true - len = len - 1 - end - local i = 1 - while i <= len do - l[i] = var2str(parlist[i]) - i = i + 1 - end - if is_vararg then - l[i] = "`" .. parlist[i]["tag"] - end - return "{ " .. table["concat"](l, ", ") .. " }" - end - fieldlist2str = function(fieldlist) - local l = {} - for k, v in ipairs(fieldlist) do - local tag = v["tag"] - if tag == "Pair" then - l[k] = "`" .. tag .. "{ " - l[k] = l[k] .. exp2str(v[1]) .. ", " .. exp2str(v[2]) - l[k] = l[k] .. " }" - else - l[k] = exp2str(v) - end - end - if # l > 0 then - return "{ " .. table["concat"](l, ", ") .. " }" - else - return "" - end - end - exp2str = function(exp) - local tag = exp["tag"] - local str = "`" .. tag - if tag == "Nil" or tag == "Dots" then - - elseif tag == "Boolean" then - str = str .. " " .. boolean2str(exp[1]) - elseif tag == "Number" then - str = str .. " " .. number2str(exp[1]) - elseif tag == "String" then - str = str .. " " .. string2str(exp[1]) - elseif tag == "Function" then - str = str .. "{ " - str = str .. parlist2str(exp[1]) .. ", " - str = str .. block2str(exp[2]) - str = str .. " }" - elseif tag == "Table" then - str = str .. fieldlist2str(exp) - elseif tag == "Op" then - str = str .. "{ " - str = str .. name2str(exp[1]) .. ", " - str = str .. exp2str(exp[2]) - if exp[3] then - str = str .. ", " .. exp2str(exp[3]) - end - str = str .. " }" - elseif tag == "Paren" then - str = str .. "{ " .. exp2str(exp[1]) .. " }" - elseif tag == "Call" then - str = str .. "{ " - str = str .. exp2str(exp[1]) - if exp[2] then - for i = 2, # exp do - str = str .. ", " .. exp2str(exp[i]) - end - end - str = str .. " }" - elseif tag == "Invoke" then - str = str .. "{ " - str = str .. exp2str(exp[1]) .. ", " - str = str .. exp2str(exp[2]) - if exp[3] then - for i = 3, # exp do - str = str .. ", " .. exp2str(exp[i]) - end - end - str = str .. " }" - elseif tag == "Id" or tag == "Index" then - str = var2str(exp) - else - error("expecting an expression, but got a " .. tag) - end - return str - end - explist2str = function(explist) - local l = {} - for k, v in ipairs(explist) do - l[k] = exp2str(v) - end - if # l > 0 then - return "{ " .. table["concat"](l, ", ") .. " }" - else - return "" - end - end - stm2str = function(stm) - local tag = stm["tag"] - local str = "`" .. tag - if tag == "Do" then - local l = {} - for k, v in ipairs(stm) do - l[k] = stm2str(v) - end - str = str .. "{ " .. table["concat"](l, ", ") .. " }" - elseif tag == "Set" then - str = str .. "{ " - str = str .. varlist2str(stm[1]) .. ", " - str = str .. explist2str(stm[2]) - str = str .. " }" - elseif tag == "While" then - str = str .. "{ " - str = str .. exp2str(stm[1]) .. ", " - str = str .. block2str(stm[2]) - str = str .. " }" - elseif tag == "Repeat" then - str = str .. "{ " - str = str .. block2str(stm[1]) .. ", " - str = str .. exp2str(stm[2]) - str = str .. " }" - elseif tag == "If" then - str = str .. "{ " - local len = # stm - if len % 2 == 0 then - local l = {} - for i = 1, len - 2, 2 do - str = str .. exp2str(stm[i]) .. ", " .. block2str(stm[i + 1]) .. ", " - end - str = str .. exp2str(stm[len - 1]) .. ", " .. block2str(stm[len]) - else - local l = {} - for i = 1, len - 3, 2 do - str = str .. exp2str(stm[i]) .. ", " .. block2str(stm[i + 1]) .. ", " - end - str = str .. exp2str(stm[len - 2]) .. ", " .. block2str(stm[len - 1]) .. ", " - str = str .. block2str(stm[len]) - end - str = str .. " }" - elseif tag == "Fornum" then - str = str .. "{ " - str = str .. var2str(stm[1]) .. ", " - str = str .. exp2str(stm[2]) .. ", " - str = str .. exp2str(stm[3]) .. ", " - if stm[5] then - str = str .. exp2str(stm[4]) .. ", " - str = str .. block2str(stm[5]) - else - str = str .. block2str(stm[4]) - end - str = str .. " }" - elseif tag == "Forin" then - str = str .. "{ " - str = str .. varlist2str(stm[1]) .. ", " - str = str .. explist2str(stm[2]) .. ", " - str = str .. block2str(stm[3]) - str = str .. " }" - elseif tag == "Local" then - str = str .. "{ " - str = str .. varlist2str(stm[1]) - if # stm[2] > 0 then - str = str .. ", " .. explist2str(stm[2]) - else - str = str .. ", " .. "{ }" - end - str = str .. " }" - elseif tag == "Localrec" then - str = str .. "{ " - str = str .. "{ " .. var2str(stm[1][1]) .. " }, " - str = str .. "{ " .. exp2str(stm[2][1]) .. " }" - str = str .. " }" - elseif tag == "Goto" or tag == "Label" then - str = str .. "{ " .. name2str(stm[1]) .. " }" - elseif tag == "Return" then - str = str .. explist2str(stm) - elseif tag == "Break" then - - elseif tag == "Call" then - str = str .. "{ " - str = str .. exp2str(stm[1]) - if stm[2] then - for i = 2, # stm do - str = str .. ", " .. exp2str(stm[i]) - end - end - str = str .. " }" - elseif tag == "Invoke" then - str = str .. "{ " - str = str .. exp2str(stm[1]) .. ", " - str = str .. exp2str(stm[2]) - if stm[3] then - for i = 3, # stm do - str = str .. ", " .. exp2str(stm[i]) - end - end - str = str .. " }" - else - error("expecting a statement, but got a " .. tag) - end - return str - end - block2str = function(block) - local l = {} - for k, v in ipairs(block) do - l[k] = stm2str(v) - end - return "{ " .. table["concat"](l, ", ") .. " }" - end - pp["tostring"] = function(t) - assert(type(t) == "table") - return block2str(t) - end - pp["print"] = function(t) - assert(type(t) == "table") - print(pp["tostring"](t)) - end - pp["dump"] = function(t, i) - if i == nil then - i = 0 - end - io["write"](string["format"]("{\ +local pp = {} +local block2str, stm2str, exp2str, var2str +local explist2str, varlist2str, parlist2str, fieldlist2str +local function iscntrl(x) +if (x >= 0 and x <= 31) or (x == 127) then +return true +end +return false +end +local function isprint(x) +return not iscntrl(x) +end +local function fixed_string(str) +local new_str = "" +for i = 1, string["len"](str) do +char = string["byte"](str, i) +if char == 34 then +new_str = new_str .. string["format"]("\\\"") +elseif char == 92 then +new_str = new_str .. string["format"]("\\\\") +elseif char == 7 then +new_str = new_str .. string["format"]("\\a") +elseif char == 8 then +new_str = new_str .. string["format"]("\\b") +elseif char == 12 then +new_str = new_str .. string["format"]("\\f") +elseif char == 10 then +new_str = new_str .. string["format"]("\\n") +elseif char == 13 then +new_str = new_str .. string["format"]("\\r") +elseif char == 9 then +new_str = new_str .. string["format"]("\\t") +elseif char == 11 then +new_str = new_str .. string["format"]("\\v") +else +if isprint(char) then +new_str = new_str .. string["format"]("%c", char) +else +new_str = new_str .. string["format"]("\\%03d", char) +end +end +end +return new_str +end +local function name2str(name) +return string["format"]("\"%s\"", name) +end +local function boolean2str(b) +return string["format"]("\"%s\"", tostring(b)) +end +local function number2str(n) +return string["format"]("\"%s\"", tostring(n)) +end +local function string2str(s) +return string["format"]("\"%s\"", fixed_string(s)) +end +var2str = function(var) +local tag = var["tag"] +local str = "`" .. tag +if tag == "Id" then +str = str .. " " .. name2str(var[1]) +elseif tag == "Index" then +str = str .. "{ " +str = str .. exp2str(var[1]) .. ", " +str = str .. exp2str(var[2]) +str = str .. " }" +else +error("expecting a variable, but got a " .. tag) +end +return str +end +varlist2str = function(varlist) +local l = {} +for k, v in ipairs(varlist) do +l[k] = var2str(v) +end +return "{ " .. table["concat"](l, ", ") .. " }" +end +parlist2str = function(parlist) +local l = {} +local len = # parlist +local is_vararg = false +if len > 0 and parlist[len]["tag"] == "Dots" then +is_vararg = true +len = len - 1 +end +local i = 1 +while i <= len do +l[i] = var2str(parlist[i]) +i = i + 1 +end +if is_vararg then +l[i] = "`" .. parlist[i]["tag"] +end +return "{ " .. table["concat"](l, ", ") .. " }" +end +fieldlist2str = function(fieldlist) +local l = {} +for k, v in ipairs(fieldlist) do +local tag = v["tag"] +if tag == "Pair" then +l[k] = "`" .. tag .. "{ " +l[k] = l[k] .. exp2str(v[1]) .. ", " .. exp2str(v[2]) +l[k] = l[k] .. " }" +else +l[k] = exp2str(v) +end +end +if # l > 0 then +return "{ " .. table["concat"](l, ", ") .. " }" +else +return "" +end +end +exp2str = function(exp) +local tag = exp["tag"] +local str = "`" .. tag +if tag == "Nil" or tag == "Dots" then + +elseif tag == "Boolean" then +str = str .. " " .. boolean2str(exp[1]) +elseif tag == "Number" then +str = str .. " " .. number2str(exp[1]) +elseif tag == "String" then +str = str .. " " .. string2str(exp[1]) +elseif tag == "Function" then +str = str .. "{ " +str = str .. parlist2str(exp[1]) .. ", " +str = str .. block2str(exp[2]) +str = str .. " }" +elseif tag == "Table" then +str = str .. fieldlist2str(exp) +elseif tag == "Op" then +str = str .. "{ " +str = str .. name2str(exp[1]) .. ", " +str = str .. exp2str(exp[2]) +if exp[3] then +str = str .. ", " .. exp2str(exp[3]) +end +str = str .. " }" +elseif tag == "Paren" then +str = str .. "{ " .. exp2str(exp[1]) .. " }" +elseif tag == "Call" then +str = str .. "{ " +str = str .. exp2str(exp[1]) +if exp[2] then +for i = 2, # exp do +str = str .. ", " .. exp2str(exp[i]) +end +end +str = str .. " }" +elseif tag == "Invoke" then +str = str .. "{ " +str = str .. exp2str(exp[1]) .. ", " +str = str .. exp2str(exp[2]) +if exp[3] then +for i = 3, # exp do +str = str .. ", " .. exp2str(exp[i]) +end +end +str = str .. " }" +elseif tag == "Id" or tag == "Index" then +str = var2str(exp) +else +error("expecting an expression, but got a " .. tag) +end +return str +end +explist2str = function(explist) +local l = {} +for k, v in ipairs(explist) do +l[k] = exp2str(v) +end +if # l > 0 then +return "{ " .. table["concat"](l, ", ") .. " }" +else +return "" +end +end +stm2str = function(stm) +local tag = stm["tag"] +local str = "`" .. tag +if tag == "Do" then +local l = {} +for k, v in ipairs(stm) do +l[k] = stm2str(v) +end +str = str .. "{ " .. table["concat"](l, ", ") .. " }" +elseif tag == "Set" then +str = str .. "{ " +str = str .. varlist2str(stm[1]) .. ", " +str = str .. explist2str(stm[2]) +str = str .. " }" +elseif tag == "While" then +str = str .. "{ " +str = str .. exp2str(stm[1]) .. ", " +str = str .. block2str(stm[2]) +str = str .. " }" +elseif tag == "Repeat" then +str = str .. "{ " +str = str .. block2str(stm[1]) .. ", " +str = str .. exp2str(stm[2]) +str = str .. " }" +elseif tag == "If" then +str = str .. "{ " +local len = # stm +if len % 2 == 0 then +local l = {} +for i = 1, len - 2, 2 do +str = str .. exp2str(stm[i]) .. ", " .. block2str(stm[i + 1]) .. ", " +end +str = str .. exp2str(stm[len - 1]) .. ", " .. block2str(stm[len]) +else +local l = {} +for i = 1, len - 3, 2 do +str = str .. exp2str(stm[i]) .. ", " .. block2str(stm[i + 1]) .. ", " +end +str = str .. exp2str(stm[len - 2]) .. ", " .. block2str(stm[len - 1]) .. ", " +str = str .. block2str(stm[len]) +end +str = str .. " }" +elseif tag == "Fornum" then +str = str .. "{ " +str = str .. var2str(stm[1]) .. ", " +str = str .. exp2str(stm[2]) .. ", " +str = str .. exp2str(stm[3]) .. ", " +if stm[5] then +str = str .. exp2str(stm[4]) .. ", " +str = str .. block2str(stm[5]) +else +str = str .. block2str(stm[4]) +end +str = str .. " }" +elseif tag == "Forin" then +str = str .. "{ " +str = str .. varlist2str(stm[1]) .. ", " +str = str .. explist2str(stm[2]) .. ", " +str = str .. block2str(stm[3]) +str = str .. " }" +elseif tag == "Local" then +str = str .. "{ " +str = str .. varlist2str(stm[1]) +if # stm[2] > 0 then +str = str .. ", " .. explist2str(stm[2]) +else +str = str .. ", " .. "{ }" +end +str = str .. " }" +elseif tag == "Localrec" then +str = str .. "{ " +str = str .. "{ " .. var2str(stm[1][1]) .. " }, " +str = str .. "{ " .. exp2str(stm[2][1]) .. " }" +str = str .. " }" +elseif tag == "Goto" or tag == "Label" then +str = str .. "{ " .. name2str(stm[1]) .. " }" +elseif tag == "Return" then +str = str .. explist2str(stm) +elseif tag == "Break" then + +elseif tag == "Call" then +str = str .. "{ " +str = str .. exp2str(stm[1]) +if stm[2] then +for i = 2, # stm do +str = str .. ", " .. exp2str(stm[i]) +end +end +str = str .. " }" +elseif tag == "Invoke" then +str = str .. "{ " +str = str .. exp2str(stm[1]) .. ", " +str = str .. exp2str(stm[2]) +if stm[3] then +for i = 3, # stm do +str = str .. ", " .. exp2str(stm[i]) +end +end +str = str .. " }" +else +error("expecting a statement, but got a " .. tag) +end +return str +end +block2str = function(block) +local l = {} +for k, v in ipairs(block) do +l[k] = stm2str(v) +end +return "{ " .. table["concat"](l, ", ") .. " }" +end +pp["tostring"] = function(t) +assert(type(t) == "table") +return block2str(t) +end +pp["print"] = function(t) +assert(type(t) == "table") +print(pp["tostring"](t)) +end +pp["dump"] = function(t, i) +if i == nil then +i = 0 +end +io["write"](string["format"]("{\ ")) - io["write"](string["format"]("%s[tag] = %s\ +io["write"](string["format"]("%s[tag] = %s\ ", string["rep"](" ", i + 2), t["tag"] or "nil")) - io["write"](string["format"]("%s[pos] = %s\ +io["write"](string["format"]("%s[pos] = %s\ ", string["rep"](" ", i + 2), t["pos"] or "nil")) - for k, v in ipairs(t) do - io["write"](string["format"]("%s[%s] = ", string["rep"](" ", i + 2), tostring(k))) - if type(v) == "table" then - pp["dump"](v, i + 2) - else - io["write"](string["format"]("%s\ +for k, v in ipairs(t) do +io["write"](string["format"]("%s[%s] = ", string["rep"](" ", i + 2), tostring(k))) +if type(v) == "table" then +pp["dump"](v, i + 2) +else +io["write"](string["format"]("%s\ ", tostring(v))) - end - end - io["write"](string["format"]("%s}\ +end +end +io["write"](string["format"]("%s}\ ", string["rep"](" ", i))) - end - return pp +end +return pp end local pp = _() or pp -package["loaded"]["lua-parser.pp"] = pp or true +package["loaded"]["lib.lua-parser.pp"] = pp or true local function _() - local lpeg = require("lpeglabel") - lpeg["locale"](lpeg) - local P, S, V = lpeg["P"], lpeg["S"], lpeg["V"] - local C, Carg, Cb, Cc = lpeg["C"], lpeg["Carg"], lpeg["Cb"], lpeg["Cc"] - local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg["Cf"], lpeg["Cg"], lpeg["Cmt"], lpeg["Cp"], lpeg["Cs"], lpeg["Ct"] - local Lc, T = lpeg["Lc"], lpeg["T"] - local alpha, digit, alnum = lpeg["alpha"], lpeg["digit"], lpeg["alnum"] - local xdigit = lpeg["xdigit"] - local space = lpeg["space"] - local labels = { - { - "ErrExtra", "unexpected character(s), expected EOF" - }, { - "ErrInvalidStat", "unexpected token, invalid start of statement" - }, { - "ErrEndIf", "expected 'end' to close the if statement" - }, { - "ErrExprIf", "expected a condition after 'if'" - }, { - "ErrThenIf", "expected 'then' after the condition" - }, { - "ErrExprEIf", "expected a condition after 'elseif'" - }, { - "ErrThenEIf", "expected 'then' after the condition" - }, { - "ErrEndDo", "expected 'end' to close the do block" - }, { - "ErrExprWhile", "expected a condition after 'while'" - }, { - "ErrDoWhile", "expected 'do' after the condition" - }, { - "ErrEndWhile", "expected 'end' to close the while loop" - }, { - "ErrUntilRep", "expected 'until' at the end of the repeat loop" - }, { - "ErrExprRep", "expected a conditions after 'until'" - }, { - "ErrForRange", "expected a numeric or generic range after 'for'" - }, { - "ErrEndFor", "expected 'end' to close the for loop" - }, { - "ErrExprFor1", "expected a starting expression for the numeric range" - }, { - "ErrCommaFor", "expected ',' to split the start and end of the range" - }, { - "ErrExprFor2", "expected an ending expression for the numeric range" - }, { - "ErrExprFor3", "expected a step expression for the numeric range after ','" - }, { - "ErrInFor", "expected '=' or 'in' after the variable(s)" - }, { - "ErrEListFor", "expected one or more expressions after 'in'" - }, { - "ErrDoFor", "expected 'do' after the range of the for loop" - }, { - "ErrDefLocal", "expected a function definition or assignment after local" - }, { - "ErrNameLFunc", "expected a function name after 'function'" - }, { - "ErrEListLAssign", "expected one or more expressions after '='" - }, { - "ErrEListAssign", "expected one or more expressions after '='" - }, { - "ErrFuncName", "expected a function name after 'function'" - }, { - "ErrNameFunc1", "expected a function name after '.'" - }, { - "ErrNameFunc2", "expected a method name after ':'" - }, { - "ErrOParenPList", "expected '(' for the parameter list" - }, { - "ErrCParenPList", "expected ')' to close the parameter list" - }, { - "ErrEndFunc", "expected 'end' to close the function body" - }, { - "ErrParList", "expected a variable name or '...' after ','" - }, { - "ErrLabel", "expected a label name after '::'" - }, { - "ErrCloseLabel", "expected '::' after the label" - }, { - "ErrGoto", "expected a label after 'goto'" - }, { - "ErrRetList", "expected an expression after ',' in the return statement" - }, { - "ErrVarList", "expected a variable name after ','" - }, { - "ErrExprList", "expected an expression after ','" - }, { - "ErrOrExpr", "expected an expression after 'or'" - }, { - "ErrAndExpr", "expected an expression after 'and'" - }, { - "ErrRelExpr", "expected an expression after the relational operator" - }, { - "ErrBOrExpr", "expected an expression after '|'" - }, { - "ErrBXorExpr", "expected an expression after '~'" - }, { - "ErrBAndExpr", "expected an expression after '&'" - }, { - "ErrShiftExpr", "expected an expression after the bit shift" - }, { - "ErrConcatExpr", "expected an expression after '..'" - }, { - "ErrAddExpr", "expected an expression after the additive operator" - }, { - "ErrMulExpr", "expected an expression after the multiplicative operator" - }, { - "ErrUnaryExpr", "expected an expression after the unary operator" - }, { - "ErrPowExpr", "expected an expression after '^'" - }, { - "ErrExprParen", "expected an expression after '('" - }, { - "ErrCParenExpr", "expected ')' to close the expression" - }, { - "ErrNameIndex", "expected a field name after '.'" - }, { - "ErrExprIndex", "expected an expression after '['" - }, { - "ErrCBracketIndex", "expected ']' to close the indexing expression" - }, { - "ErrNameMeth", "expected a method name after ':'" - }, { - "ErrMethArgs", "expected some arguments for the method call (or '()')" - }, { - "ErrArgList", "expected an expression after ',' in the argument list" - }, { - "ErrCParenArgs", "expected ')' to close the argument list" - }, { - "ErrCBraceTable", "expected '}' to close the table constructor" - }, { - "ErrEqField", "expected '=' after the table key" - }, { - "ErrExprField", "expected an expression after '='" - }, { - "ErrExprFKey", "expected an expression after '[' for the table key" - }, { - "ErrCBracketFKey", "expected ']' to close the table key" - }, { - "ErrDigitHex", "expected one or more hexadecimal digits after '0x'" - }, { - "ErrDigitDeci", "expected one or more digits after the decimal point" - }, { - "ErrDigitExpo", "expected one or more digits for the exponent" - }, { - "ErrQuote", "unclosed string" - }, { - "ErrHexEsc", "expected exactly two hexadecimal digits after '\\x'" - }, { - "ErrOBraceUEsc", "expected '{' after '\\u'" - }, { - "ErrDigitUEsc", "expected one or more hexadecimal digits for the UTF-8 code point" - }, { - "ErrCBraceUEsc", "expected '}' after the code point" - }, { - "ErrEscSeq", "invalid escape sequence" - }, { - "ErrCloseLStr", "unclosed long string" - } - } - local function throw(label) - label = "Err" .. label - for i, labelinfo in ipairs(labels) do - if labelinfo[1] == label then - return T(i) - end - end - error("Label not found: " .. label) - end - local function expect(patt, label) - return patt + throw(label) - end - local function token(patt) - return patt * V("Skip") - end - local function sym(str) - return token(P(str)) - end - local function kw(str) - return token(P(str) * - V("IdRest")) - end - local function tagC(tag, patt) - return Ct(Cg(Cp(), "pos") * Cg(Cc(tag), "tag") * patt) - end - local function unaryOp(op, e) - return { - ["tag"] = "Op", ["pos"] = e["pos"], [1] = op, [2] = e - } - end - local function binaryOp(e1, op, e2) - if not op then - return e1 - else - return { - ["tag"] = "Op", ["pos"] = e1["pos"], [1] = op, [2] = e1, [3] = e2 - } - end - end - local function sepBy(patt, sep, label) - if label then - return patt * Cg(sep * expect(patt, label)) ^ 0 - else - return patt * Cg(sep * patt) ^ 0 - end - end - local function chainOp(patt, sep, label) - return Cf(sepBy(patt, sep, label), binaryOp) - end - local function commaSep(patt, label) - return sepBy(patt, sym(","), label) - end - local function tagDo(block) - block["tag"] = "Do" - return block - end - local function fixFuncStat(func) - if func[1]["is_method"] then - table["insert"](func[2][1], 1, { - ["tag"] = "Id", [1] = "self" - }) - end - func[1] = { func[1] } - func[2] = { func[2] } - return func - end - local function addDots(params, dots) - if dots then - table["insert"](params, dots) - end - return params - end - local function insertIndex(t, index) - return { - ["tag"] = "Index", ["pos"] = t["pos"], [1] = t, [2] = index - } - end - local function markMethod(t, method) - if method then - return { - ["tag"] = "Index", ["pos"] = t["pos"], ["is_method"] = true, [1] = t, [2] = method - } - end - return t - end - local function makeIndexOrCall(t1, t2) - if t2["tag"] == "Call" or t2["tag"] == "Invoke" then - local t = { - ["tag"] = t2["tag"], ["pos"] = t1["pos"], [1] = t1 - } - for k, v in ipairs(t2) do - table["insert"](t, v) - end - return t - end - return { - ["tag"] = "Index", ["pos"] = t1["pos"], [1] = t1, [2] = t2[1] - } - end - local G = { - V("Lua"), ["Lua"] = V("Shebang") ^ - 1 * V("Skip") * V("Block") * expect(P(- 1), "Extra"), ["Shebang"] = P("#!") * (P(1) - P("\ -")) ^ 0, ["Block"] = tagC("Block", V("Stat") ^ 0 * V("RetStat") ^ - 1), ["Stat"] = V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat") + V("LocalStat") + V("FuncStat") + V("BreakStat") + V("LabelStat") + V("GoToStat") + V("FuncCall") + V("Assignment") + sym(";") + - V("BlockEnd") * throw("InvalidStat"), ["BlockEnd"] = P("return") + "end" + "elseif" + "else" + "until" + - 1, ["IfStat"] = tagC("If", V("IfPart") * V("ElseIfPart") ^ 0 * V("ElsePart") ^ - 1 * expect(kw("end"), "EndIf")), ["IfPart"] = kw("if") * expect(V("Expr"), "ExprIf") * expect(kw("then"), "ThenIf") * V("Block"), ["ElseIfPart"] = kw("elseif") * expect(V("Expr"), "ExprEIf") * expect(kw("then"), "ThenEIf") * V("Block"), ["ElsePart"] = kw("else") * V("Block"), ["DoStat"] = kw("do") * V("Block") * expect(kw("end"), "EndDo") / tagDo, ["WhileStat"] = tagC("While", kw("while") * expect(V("Expr"), "ExprWhile") * V("WhileBody")), ["WhileBody"] = expect(kw("do"), "DoWhile") * V("Block") * expect(kw("end"), "EndWhile"), ["RepeatStat"] = tagC("Repeat", kw("repeat") * V("Block") * expect(kw("until"), "UntilRep") * expect(V("Expr"), "ExprRep")), ["ForStat"] = kw("for") * expect(V("ForNum") + V("ForIn"), "ForRange") * expect(kw("end"), "EndFor"), ["ForNum"] = tagC("Fornum", V("Id") * sym("=") * V("NumRange") * V("ForBody")), ["NumRange"] = expect(V("Expr"), "ExprFor1") * expect(sym(","), "CommaFor") * expect(V("Expr"), "ExprFor2") * (sym(",") * expect(V("Expr"), "ExprFor3")) ^ - 1, ["ForIn"] = tagC("Forin", V("NameList") * expect(kw("in"), "InFor") * expect(V("ExprList"), "EListFor") * V("ForBody")), ["ForBody"] = expect(kw("do"), "DoFor") * V("Block"), ["LocalStat"] = kw("local") * expect(V("LocalFunc") + V("LocalAssign"), "DefLocal"), ["LocalFunc"] = tagC("Localrec", kw("function") * expect(V("Id"), "NameLFunc") * V("FuncBody")) / fixFuncStat, ["LocalAssign"] = tagC("Local", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))), ["Assignment"] = tagC("Set", V("VarList") * V("AssignmentOp") * expect(V("ExprList"), "EListAssign")), ["FuncStat"] = tagC("Set", kw("function") * expect(V("FuncName"), "FuncName") * V("FuncBody")) / fixFuncStat, ["FuncName"] = Cf(V("Id") * (sym(".") * expect(V("StrId"), "NameFunc1")) ^ 0, insertIndex) * (sym(":") * expect(V("StrId"), "NameFunc2")) ^ - 1 / markMethod, ["FuncBody"] = tagC("Function", V("FuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["FuncParams"] = expect(sym("("), "OParenPList") * V("ParList") * expect(sym(")"), "CParenPList"), ["ParList"] = V("NamedParList") * (sym(",") * expect(tagC("Dots", sym("...")), "ParList")) ^ - 1 / addDots + Ct(tagC("Dots", sym("..."))) + Ct(Cc()), ["NamedParList"] = tagC("NamedParList", commaSep(V("NamedPar"))), ["NamedPar"] = tagC("ParPair", V("ParKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Id"), ["ParKey"] = V("Id") * # ("=" * - P("=")), ["LabelStat"] = tagC("Label", sym("::") * expect(V("Name"), "Label") * expect(sym("::"), "CloseLabel")), ["GoToStat"] = tagC("Goto", kw("goto") * expect(V("Name"), "Goto")), ["BreakStat"] = tagC("Break", kw("break")), ["RetStat"] = tagC("Return", kw("return") * commaSep(V("Expr"), "RetList") ^ - 1 * sym(";") ^ - 1), ["NameList"] = tagC("NameList", commaSep(V("Id"))), ["VarList"] = tagC("VarList", commaSep(V("VarExpr"), "VarList")), ["ExprList"] = tagC("ExpList", commaSep(V("Expr"), "ExprList")), ["Expr"] = V("OrExpr"), ["OrExpr"] = chainOp(V("AndExpr"), V("OrOp"), "OrExpr"), ["AndExpr"] = chainOp(V("RelExpr"), V("AndOp"), "AndExpr"), ["RelExpr"] = chainOp(V("BOrExpr"), V("RelOp"), "RelExpr"), ["BOrExpr"] = chainOp(V("BXorExpr"), V("BOrOp"), "BOrExpr"), ["BXorExpr"] = chainOp(V("BAndExpr"), V("BXorOp"), "BXorExpr"), ["BAndExpr"] = chainOp(V("ShiftExpr"), V("BAndOp"), "BAndExpr"), ["ShiftExpr"] = chainOp(V("ConcatExpr"), V("ShiftOp"), "ShiftExpr"), ["ConcatExpr"] = V("AddExpr") * (V("ConcatOp") * expect(V("ConcatExpr"), "ConcatExpr")) ^ - 1 / binaryOp, ["AddExpr"] = chainOp(V("MulExpr"), V("AddOp"), "AddExpr"), ["MulExpr"] = chainOp(V("UnaryExpr"), V("MulOp"), "MulExpr"), ["UnaryExpr"] = V("UnaryOp") * expect(V("UnaryExpr"), "UnaryExpr") / unaryOp + V("PowExpr"), ["PowExpr"] = V("SimpleExpr") * (V("PowOp") * expect(V("UnaryExpr"), "PowExpr")) ^ - 1 / binaryOp, ["SimpleExpr"] = tagC("Number", V("Number")) + tagC("String", V("String")) + tagC("Nil", kw("nil")) + tagC("Boolean", kw("false") * Cc(false)) + tagC("Boolean", kw("true") * Cc(true)) + tagC("Dots", sym("...")) + V("FuncDef") + V("Table") + V("SuffixedExpr"), ["FuncCall"] = Cmt(V("SuffixedExpr"), function(s, i, exp) - return exp["tag"] == "Call" or exp["tag"] == "Invoke", exp - end), ["VarExpr"] = Cmt(V("SuffixedExpr"), function(s, i, exp) - return exp["tag"] == "Id" or exp["tag"] == "Index", exp - end), ["SuffixedExpr"] = Cf(V("PrimaryExpr") * (V("Index") + V("Call")) ^ 0, makeIndexOrCall), ["PrimaryExpr"] = V("Id") + tagC("Paren", sym("(") * expect(V("Expr"), "ExprParen") * expect(sym(")"), "CParenExpr")), ["Index"] = tagC("DotIndex", sym("." * - P(".")) * expect(V("StrId"), "NameIndex")) + tagC("ArrayIndex", sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprIndex") * expect(sym("]"), "CBracketIndex")), ["Call"] = tagC("Invoke", Cg(sym(":" * - P(":")) * expect(V("StrId"), "NameMeth") * expect(V("FuncArgs"), "MethArgs"))) + tagC("Call", V("FuncArgs")), ["FuncDef"] = kw("function") * V("FuncBody"), ["FuncArgs"] = sym("(") * commaSep(V("Expr"), "ArgList") ^ - 1 * expect(sym(")"), "CParenArgs") + V("Table") + tagC("String", V("String")), ["Table"] = tagC("Table", sym("{") * V("FieldList") ^ - 1 * expect(sym("}"), "CBraceTable")), ["FieldList"] = sepBy(V("Field"), V("FieldSep")) * V("FieldSep") ^ - 1, ["Field"] = tagC("Pair", V("FieldKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Expr"), ["FieldKey"] = sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprFKey") * expect(sym("]"), "CBracketFKey") + V("StrId") * # ("=" * - P("=")), ["FieldSep"] = sym(",") + sym(";"), ["Id"] = tagC("Id", V("Name")), ["StrId"] = tagC("String", V("Name")), ["Skip"] = (V("Space") + V("Comment")) ^ 0, ["Space"] = space ^ 1, ["Comment"] = P("--") * V("LongStr") / function() - return - end + P("--") * (P(1) - P("\ +local lpeg = require("lpeglabel") +lpeg["locale"](lpeg) +local P, S, V = lpeg["P"], lpeg["S"], lpeg["V"] +local C, Carg, Cb, Cc = lpeg["C"], lpeg["Carg"], lpeg["Cb"], lpeg["Cc"] +local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg["Cf"], lpeg["Cg"], lpeg["Cmt"], lpeg["Cp"], lpeg["Cs"], lpeg["Ct"] +local Lc, T = lpeg["Lc"], lpeg["T"] +local alpha, digit, alnum = lpeg["alpha"], lpeg["digit"], lpeg["alnum"] +local xdigit = lpeg["xdigit"] +local space = lpeg["space"] +local labels = { +{ +"ErrExtra", "unexpected character(s), expected EOF" +}, { +"ErrInvalidStat", "unexpected token, invalid start of statement" +}, { +"ErrEndIf", "expected 'end' to close the if statement" +}, { +"ErrExprIf", "expected a condition after 'if'" +}, { +"ErrThenIf", "expected 'then' after the condition" +}, { +"ErrExprEIf", "expected a condition after 'elseif'" +}, { +"ErrThenEIf", "expected 'then' after the condition" +}, { +"ErrEndDo", "expected 'end' to close the do block" +}, { +"ErrExprWhile", "expected a condition after 'while'" +}, { +"ErrDoWhile", "expected 'do' after the condition" +}, { +"ErrEndWhile", "expected 'end' to close the while loop" +}, { +"ErrUntilRep", "expected 'until' at the end of the repeat loop" +}, { +"ErrExprRep", "expected a conditions after 'until'" +}, { +"ErrForRange", "expected a numeric or generic range after 'for'" +}, { +"ErrEndFor", "expected 'end' to close the for loop" +}, { +"ErrExprFor1", "expected a starting expression for the numeric range" +}, { +"ErrCommaFor", "expected ',' to split the start and end of the range" +}, { +"ErrExprFor2", "expected an ending expression for the numeric range" +}, { +"ErrExprFor3", "expected a step expression for the numeric range after ','" +}, { +"ErrInFor", "expected '=' or 'in' after the variable(s)" +}, { +"ErrEListFor", "expected one or more expressions after 'in'" +}, { +"ErrDoFor", "expected 'do' after the range of the for loop" +}, { +"ErrDefLocal", "expected a function definition or assignment after local" +}, { +"ErrNameLFunc", "expected a function name after 'function'" +}, { +"ErrEListLAssign", "expected one or more expressions after '='" +}, { +"ErrEListAssign", "expected one or more expressions after '='" +}, { +"ErrFuncName", "expected a function name after 'function'" +}, { +"ErrNameFunc1", "expected a function name after '.'" +}, { +"ErrNameFunc2", "expected a method name after ':'" +}, { +"ErrOParenPList", "expected '(' for the parameter list" +}, { +"ErrCParenPList", "expected ')' to close the parameter list" +}, { +"ErrEndFunc", "expected 'end' to close the function body" +}, { +"ErrParList", "expected a variable name or '...' after ','" +}, { +"ErrLabel", "expected a label name after '::'" +}, { +"ErrCloseLabel", "expected '::' after the label" +}, { +"ErrGoto", "expected a label after 'goto'" +}, { +"ErrRetList", "expected an expression after ',' in the return statement" +}, { +"ErrVarList", "expected a variable name after ','" +}, { +"ErrExprList", "expected an expression after ','" +}, { +"ErrOrExpr", "expected an expression after 'or'" +}, { +"ErrAndExpr", "expected an expression after 'and'" +}, { +"ErrRelExpr", "expected an expression after the relational operator" +}, { +"ErrBOrExpr", "expected an expression after '|'" +}, { +"ErrBXorExpr", "expected an expression after '~'" +}, { +"ErrBAndExpr", "expected an expression after '&'" +}, { +"ErrShiftExpr", "expected an expression after the bit shift" +}, { +"ErrConcatExpr", "expected an expression after '..'" +}, { +"ErrAddExpr", "expected an expression after the additive operator" +}, { +"ErrMulExpr", "expected an expression after the multiplicative operator" +}, { +"ErrUnaryExpr", "expected an expression after the unary operator" +}, { +"ErrPowExpr", "expected an expression after '^'" +}, { +"ErrExprParen", "expected an expression after '('" +}, { +"ErrCParenExpr", "expected ')' to close the expression" +}, { +"ErrNameIndex", "expected a field name after '.'" +}, { +"ErrExprIndex", "expected an expression after '['" +}, { +"ErrCBracketIndex", "expected ']' to close the indexing expression" +}, { +"ErrNameMeth", "expected a method name after ':'" +}, { +"ErrMethArgs", "expected some arguments for the method call (or '()')" +}, { +"ErrArgList", "expected an expression after ',' in the argument list" +}, { +"ErrCParenArgs", "expected ')' to close the argument list" +}, { +"ErrCBraceTable", "expected '}' to close the table constructor" +}, { +"ErrEqField", "expected '=' after the table key" +}, { +"ErrExprField", "expected an expression after '='" +}, { +"ErrExprFKey", "expected an expression after '[' for the table key" +}, { +"ErrCBracketFKey", "expected ']' to close the table key" +}, { +"ErrDigitHex", "expected one or more hexadecimal digits after '0x'" +}, { +"ErrDigitDeci", "expected one or more digits after the decimal point" +}, { +"ErrDigitExpo", "expected one or more digits for the exponent" +}, { +"ErrQuote", "unclosed string" +}, { +"ErrHexEsc", "expected exactly two hexadecimal digits after '\\x'" +}, { +"ErrOBraceUEsc", "expected '{' after '\\u'" +}, { +"ErrDigitUEsc", "expected one or more hexadecimal digits for the UTF-8 code point" +}, { +"ErrCBraceUEsc", "expected '}' after the code point" +}, { +"ErrEscSeq", "invalid escape sequence" +}, { +"ErrCloseLStr", "unclosed long string" +} +} +local function throw(label) +label = "Err" .. label +for i, labelinfo in ipairs(labels) do +if labelinfo[1] == label then +return T(i) +end +end +error("Label not found: " .. label) +end +local function expect(patt, label) +return patt + throw(label) +end +local function token(patt) +return patt * V("Skip") +end +local function sym(str) +return token(P(str)) +end +local function kw(str) +return token(P(str) * - V("IdRest")) +end +local function tagC(tag, patt) +return Ct(Cg(Cp(), "pos") * Cg(Cc(tag), "tag") * patt) +end +local function unaryOp(op, e) +return { +["tag"] = "Op", ["pos"] = e["pos"], [1] = op, [2] = e +} +end +local function binaryOp(e1, op, e2) +if not op then +return e1 +else +return { +["tag"] = "Op", ["pos"] = e1["pos"], [1] = op, [2] = e1, [3] = e2 +} +end +end +local function sepBy(patt, sep, label) +if label then +return patt * Cg(sep * expect(patt, label)) ^ 0 +else +return patt * Cg(sep * patt) ^ 0 +end +end +local function chainOp(patt, sep, label) +return Cf(sepBy(patt, sep, label), binaryOp) +end +local function commaSep(patt, label) +return sepBy(patt, sym(","), label) +end +local function tagDo(block) +block["tag"] = "Do" +return block +end +local function fixFuncStat(func) +if func[1]["is_method"] then +table["insert"](func[2][1], 1, { +["tag"] = "Id", [1] = "self" +}) +end +func[1] = { func[1] } +func[2] = { func[2] } +return func +end +local function addDots(params, dots) +if dots then +table["insert"](params, dots) +end +return params +end +local function insertIndex(t, index) +return { +["tag"] = "Index", ["pos"] = t["pos"], [1] = t, [2] = index +} +end +local function markMethod(t, method) +if method then +return { +["tag"] = "Index", ["pos"] = t["pos"], ["is_method"] = true, [1] = t, [2] = method +} +end +return t +end +local function makeIndexOrCall(t1, t2) +if t2["tag"] == "Call" or t2["tag"] == "Invoke" then +local t = { +["tag"] = t2["tag"], ["pos"] = t1["pos"], [1] = t1 +} +for k, v in ipairs(t2) do +table["insert"](t, v) +end +return t +end +return { +["tag"] = "Index", ["pos"] = t1["pos"], [1] = t1, [2] = t2[1] +} +end +local function fixAnonymousMethodParams(t1, t2) +if t1 == ":" then +t1 = t2 +table["insert"](t1, 1, { +["tag"] = "Id", "self" +}) +end +return t1 +end +local G = { +V("Lua"), ["Lua"] = V("Shebang") ^ - 1 * V("Skip") * V("Block") * expect(P(- 1), "Extra"), ["Shebang"] = P("#!") * (P(1) - P("\ +")) ^ 0, ["Block"] = tagC("Block", V("Stat") ^ 0 * V("RetStat") ^ - 1), ["Stat"] = V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat") + V("LocalStat") + V("FuncStat") + V("BreakStat") + V("LabelStat") + V("GoToStat") + V("FuncCall") + V("Assignment") + sym(";") + - V("BlockEnd") * throw("InvalidStat"), ["BlockEnd"] = P("return") + "end" + "elseif" + "else" + "until" + - 1, ["IfStat"] = tagC("If", V("IfPart") * V("ElseIfPart") ^ 0 * V("ElsePart") ^ - 1 * expect(kw("end"), "EndIf")), ["IfPart"] = kw("if") * expect(V("Expr"), "ExprIf") * expect(kw("then"), "ThenIf") * V("Block"), ["ElseIfPart"] = kw("elseif") * expect(V("Expr"), "ExprEIf") * expect(kw("then"), "ThenEIf") * V("Block"), ["ElsePart"] = kw("else") * V("Block"), ["DoStat"] = kw("do") * V("Block") * expect(kw("end"), "EndDo") / tagDo, ["WhileStat"] = tagC("While", kw("while") * expect(V("Expr"), "ExprWhile") * V("WhileBody")), ["WhileBody"] = expect(kw("do"), "DoWhile") * V("Block") * expect(kw("end"), "EndWhile"), ["RepeatStat"] = tagC("Repeat", kw("repeat") * V("Block") * expect(kw("until"), "UntilRep") * expect(V("Expr"), "ExprRep")), ["ForStat"] = kw("for") * expect(V("ForNum") + V("ForIn"), "ForRange") * expect(kw("end"), "EndFor"), ["ForNum"] = tagC("Fornum", V("Id") * sym("=") * V("NumRange") * V("ForBody")), ["NumRange"] = expect(V("Expr"), "ExprFor1") * expect(sym(","), "CommaFor") * expect(V("Expr"), "ExprFor2") * (sym(",") * expect(V("Expr"), "ExprFor3")) ^ - 1, ["ForIn"] = tagC("Forin", V("NameList") * expect(kw("in"), "InFor") * expect(V("ExprList"), "EListFor") * V("ForBody")), ["ForBody"] = expect(kw("do"), "DoFor") * V("Block"), ["LocalStat"] = kw("local") * expect(V("LocalFunc") + V("LocalAssign"), "DefLocal"), ["LocalFunc"] = tagC("Localrec", kw("function") * expect(V("Id"), "NameLFunc") * V("FuncBody")) / fixFuncStat, ["LocalAssign"] = tagC("Local", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))), ["Assignment"] = tagC("Set", V("VarList") * V("BinOp") ^ - 1 * (sym("=") / "=") * V("BinOp") ^ - 1 * expect(V("ExprList"), "EListAssign")), ["FuncStat"] = tagC("Set", kw("function") * expect(V("FuncName"), "FuncName") * V("FuncBody")) / fixFuncStat, ["FuncName"] = Cf(V("Id") * (sym(".") * expect(V("StrId"), "NameFunc1")) ^ 0, insertIndex) * (sym(":") * expect(V("StrId"), "NameFunc2")) ^ - 1 / markMethod, ["FuncBody"] = tagC("Function", V("FuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["FuncParams"] = expect(sym("("), "OParenPList") * V("ParList") * expect(sym(")"), "CParenPList"), ["ParList"] = V("NamedParList") * (sym(",") * expect(tagC("Dots", sym("...")), "ParList")) ^ - 1 / addDots + Ct(tagC("Dots", sym("..."))) + Ct(Cc()), ["NamedParList"] = tagC("NamedParList", commaSep(V("NamedPar"))), ["NamedPar"] = tagC("ParPair", V("ParKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Id"), ["ParKey"] = V("Id") * # ("=" * - P("=")), ["LabelStat"] = tagC("Label", sym("::") * expect(V("Name"), "Label") * expect(sym("::"), "CloseLabel")), ["GoToStat"] = tagC("Goto", kw("goto") * expect(V("Name"), "Goto")), ["BreakStat"] = tagC("Break", kw("break")), ["RetStat"] = tagC("Return", kw("return") * commaSep(V("Expr"), "RetList") ^ - 1 * sym(";") ^ - 1), ["NameList"] = tagC("NameList", commaSep(V("Id"))), ["VarList"] = tagC("VarList", commaSep(V("VarExpr"), "VarList")), ["ExprList"] = tagC("ExpList", commaSep(V("Expr"), "ExprList")), ["Expr"] = V("OrExpr"), ["OrExpr"] = chainOp(V("AndExpr"), V("OrOp"), "OrExpr"), ["AndExpr"] = chainOp(V("RelExpr"), V("AndOp"), "AndExpr"), ["RelExpr"] = chainOp(V("BOrExpr"), V("RelOp"), "RelExpr"), ["BOrExpr"] = chainOp(V("BXorExpr"), V("BOrOp"), "BOrExpr"), ["BXorExpr"] = chainOp(V("BAndExpr"), V("BXorOp"), "BXorExpr"), ["BAndExpr"] = chainOp(V("ShiftExpr"), V("BAndOp"), "BAndExpr"), ["ShiftExpr"] = chainOp(V("ConcatExpr"), V("ShiftOp"), "ShiftExpr"), ["ConcatExpr"] = V("AddExpr") * (V("ConcatOp") * expect(V("ConcatExpr"), "ConcatExpr")) ^ - 1 / binaryOp, ["AddExpr"] = chainOp(V("MulExpr"), V("AddOp"), "AddExpr"), ["MulExpr"] = chainOp(V("UnaryExpr"), V("MulOp"), "MulExpr"), ["UnaryExpr"] = V("UnaryOp") * expect(V("UnaryExpr"), "UnaryExpr") / unaryOp + V("PowExpr"), ["PowExpr"] = V("SimpleExpr") * (V("PowOp") * expect(V("UnaryExpr"), "PowExpr")) ^ - 1 / binaryOp, ["SimpleExpr"] = tagC("Number", V("Number")) + tagC("String", V("String")) + tagC("Nil", kw("nil")) + tagC("Boolean", kw("false") * Cc(false)) + tagC("Boolean", kw("true") * Cc(true)) + tagC("Dots", sym("...")) + V("FuncDef") + V("Table") + V("SuffixedExpr"), ["FuncCall"] = Cmt(V("SuffixedExpr"), function(s, i, exp) +return exp["tag"] == "Call" or exp["tag"] == "Invoke", exp +end), ["VarExpr"] = Cmt(V("SuffixedExpr"), function(s, i, exp) +return exp["tag"] == "Id" or exp["tag"] == "Index", exp +end), ["SuffixedExpr"] = Cf(V("PrimaryExpr") * (V("Index") + V("Call")) ^ 0, makeIndexOrCall), ["PrimaryExpr"] = V("SelfId") * (V("SelfCall") + V("SelfIndex")) + V("Id") + tagC("Paren", sym("(") * expect(V("Expr"), "ExprParen") * expect(sym(")"), "CParenExpr")), ["Index"] = tagC("DotIndex", sym("." * - P(".")) * expect(V("StrId"), "NameIndex")) + tagC("ArrayIndex", sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprIndex") * expect(sym("]"), "CBracketIndex")), ["Call"] = tagC("Invoke", Cg(sym(":" * - P(":")) * expect(V("StrId"), "NameMeth") * expect(V("FuncArgs"), "MethArgs"))) + tagC("Call", V("FuncArgs")), ["SelfIndex"] = tagC("DotIndex", V("StrId")), ["SelfCall"] = tagC("Invoke", Cg(V("StrId") * V("FuncArgs"))), ["ShortFuncDef"] = tagC("Function", V("ShortFuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["ShortFuncParams"] = (sym(":") / ":") ^ - 1 * sym("(") * V("ParList") * sym(")") / fixAnonymousMethodParams, ["FuncDef"] = (kw("function") * V("FuncBody")) + V("ShortFuncDef"), ["FuncArgs"] = sym("(") * commaSep(V("Expr"), "ArgList") ^ - 1 * expect(sym(")"), "CParenArgs") + V("Table") + tagC("String", V("String")), ["Table"] = tagC("Table", sym("{") * V("FieldList") ^ - 1 * expect(sym("}"), "CBraceTable")), ["FieldList"] = sepBy(V("Field"), V("FieldSep")) * V("FieldSep") ^ - 1, ["Field"] = tagC("Pair", V("FieldKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Expr"), ["FieldKey"] = sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprFKey") * expect(sym("]"), "CBracketFKey") + V("StrId") * # ("=" * - P("=")), ["FieldSep"] = sym(",") + sym(";"), ["SelfId"] = tagC("Id", sym("@") / "self"), ["Id"] = tagC("Id", V("Name")) + V("SelfId"), ["StrId"] = tagC("String", V("Name")), ["Skip"] = (V("Space") + V("Comment")) ^ 0, ["Space"] = space ^ 1, ["Comment"] = P("--") * V("LongStr") / function() +return +end + P("--") * (P(1) - P("\ ")) ^ 0, ["Name"] = token(- V("Reserved") * C(V("Ident"))), ["Reserved"] = V("Keywords") * - V("IdRest"), ["Keywords"] = P("and") + "break" + "do" + "elseif" + "else" + "end" + "false" + "for" + "function" + "goto" + "if" + "in" + "local" + "nil" + "not" + "or" + "repeat" + "return" + "then" + "true" + "until" + "while", ["Ident"] = V("IdStart") * V("IdRest") ^ 0, ["IdStart"] = alpha + P("_"), ["IdRest"] = alnum + P("_"), ["Number"] = token((V("Hex") + V("Float") + V("Int")) / tonumber), ["Hex"] = (P("0x") + "0X") * expect(xdigit ^ 1, "DigitHex"), ["Float"] = V("Decimal") * V("Expo") ^ - 1 + V("Int") * V("Expo"), ["Decimal"] = digit ^ 1 * "." * digit ^ 0 + P(".") * - P(".") * expect(digit ^ 1, "DigitDeci"), ["Expo"] = S("eE") * S("+-") ^ - 1 * expect(digit ^ 1, "DigitExpo"), ["Int"] = digit ^ 1, ["String"] = token(V("ShortStr") + V("LongStr")), ["ShortStr"] = P("\"") * Cs((V("EscSeq") + (P(1) - S("\"\ "))) ^ 0) * expect(P("\""), "Quote") + P("'") * Cs((V("EscSeq") + (P(1) - S("'\ "))) ^ 0) * expect(P("'"), "Quote"), ["EscSeq"] = P("\\") / "" * (P("a") / "\7" + P("b") / "\8" + P("f") / "\12" + P("n") / "\ @@ -1586,228 +1781,209 @@ local function _() ") / "\ " + P("\13") / "\ " + P("\\") / "\\" + P("\"") / "\"" + P("'") / "'" + P("z") * space ^ 0 / "" + digit * digit ^ - 2 / tonumber / string["char"] + P("x") * expect(C(xdigit * xdigit), "HexEsc") * Cc(16) / tonumber / string["char"] + P("u") * expect("{", "OBraceUEsc") * expect(C(xdigit ^ 1), "DigitUEsc") * Cc(16) * expect("}", "CBraceUEsc") / tonumber / (utf8 and utf8["char"] or string["char"]) + throw("EscSeq")), ["LongStr"] = V("Open") * C((P(1) - V("CloseEq")) ^ 0) * expect(V("Close"), "CloseLStr") / function(s, eqs) - return s - end, ["Open"] = "[" * Cg(V("Equals"), "openEq") * "[" * P("\ +return s +end, ["Open"] = "[" * Cg(V("Equals"), "openEq") * "[" * P("\ ") ^ - 1, ["Close"] = "]" * C(V("Equals")) * "]", ["Equals"] = P("=") ^ 0, ["CloseEq"] = Cmt(V("Close") * Cb("openEq"), function(s, i, closeEq, openEq) - return # openEq == # closeEq - end), ["OrOp"] = kw("or") / "or", ["AndOp"] = kw("and") / "and", ["RelOp"] = sym("~=") / "ne" + sym("==") / "eq" + sym("<=") / "le" + sym(">=") / "ge" + sym("<") / "lt" + sym(">") / "gt", ["BOrOp"] = sym("|") / "bor", ["BXorOp"] = sym("~" * - P("=")) / "bxor", ["BAndOp"] = sym("&") / "band", ["ShiftOp"] = sym("<<") / "shl" + sym(">>") / "shr", ["ConcatOp"] = sym("..") / "concat", ["AddOp"] = sym("+") / "add" + sym("-") / "sub", ["MulOp"] = sym("*") / "mul" + sym("//") / "idiv" + sym("/") / "div" + sym("%") / "mod", ["UnaryOp"] = kw("not") / "not" + sym("-") / "unm" + sym("#") / "len" + sym("~") / "bnot", ["PowOp"] = sym("^") / "pow", ["AssignmentOp"] = (V("OrOp") + V("AndOp") + V("BOrOp") + V("BXorOp") + V("BAndOp") + V("ShiftOp") + V("ConcatOp") + V("AddOp") + V("MulOp") + V("PowOp")) ^ - 1 * sym("=") - } - local parser = {} - local validator = require("lua-parser.validator") - local validate = validator["validate"] - local syntaxerror = validator["syntaxerror"] - parser["parse"] = function(subject, filename) - local errorinfo = { - ["subject"] = subject, ["filename"] = filename - } - lpeg["setmaxstack"](1000) - local ast, label, sfail = lpeg["match"](G, subject, nil, errorinfo) - if not ast then - local errpos = # subject - # sfail + 1 - local errmsg = labels[label][2] - return ast, syntaxerror(errorinfo, errpos, errmsg) - end - return validate(ast, errorinfo) - end - return parser +return # openEq == # closeEq +end), ["OrOp"] = kw("or") / "or", ["AndOp"] = kw("and") / "and", ["RelOp"] = sym("~=") / "ne" + sym("==") / "eq" + sym("<=") / "le" + sym(">=") / "ge" + sym("<") / "lt" + sym(">") / "gt", ["BOrOp"] = sym("|") / "bor", ["BXorOp"] = sym("~" * - P("=")) / "bxor", ["BAndOp"] = sym("&") / "band", ["ShiftOp"] = sym("<<") / "shl" + sym(">>") / "shr", ["ConcatOp"] = sym("..") / "concat", ["AddOp"] = sym("+") / "add" + sym("-") / "sub", ["MulOp"] = sym("*") / "mul" + sym("//") / "idiv" + sym("/") / "div" + sym("%") / "mod", ["UnaryOp"] = kw("not") / "not" + sym("-") / "unm" + sym("#") / "len" + sym("~") / "bnot", ["PowOp"] = sym("^") / "pow", ["BinOp"] = V("OrOp") + V("AndOp") + V("BOrOp") + V("BXorOp") + V("BAndOp") + V("ShiftOp") + V("ConcatOp") + V("AddOp") + V("MulOp") + V("PowOp") +} +local parser = {} +local validator = require("lib.lua-parser.validator") +local validate = validator["validate"] +local syntaxerror = validator["syntaxerror"] +parser["parse"] = function(subject, filename) +local errorinfo = { +["subject"] = subject, ["filename"] = filename +} +lpeg["setmaxstack"](1000) +local ast, label, sfail = lpeg["match"](G, subject, nil, errorinfo) +if not ast then +local errpos = # subject - # sfail + 1 +local errmsg = labels[label][2] +return ast, syntaxerror(errorinfo, errpos, errmsg) +end +return validate(ast, errorinfo) +end +return parser end local parser = _() or parser -package["loaded"]["lua-parser.parser"] = parser or true -local function _() - local ipairs, pairs, setfenv, tonumber, loadstring, type = ipairs, pairs, setfenv, tonumber, loadstring, type - local tinsert, tconcat = table["insert"], table["concat"] - local function commonerror(msg) - return nil, ("[cmdline]: " .. msg) - end - local function argerror(msg, numarg) - msg = msg and (": " .. msg) or "" - return nil, ("[cmdline]: bad argument #" .. numarg .. msg) - end - local function iderror(numarg) - return argerror("ID not valid", numarg) - end - local function idcheck(id) - return id:match("^[%a_][%w_]*$") and true - end - return function(t_in, options, params) - local t_out = {} - for i, v in ipairs(t_in) do - local prefix, command = v:sub(1, 1), v:sub(2) - if prefix == "$" then - tinsert(t_out, command) - elseif prefix == "-" then - for id in command:gmatch("[^,;]+") do - if not idcheck(id) then - return iderror(i) - end - t_out[id] = true - end - elseif prefix == "!" then - local f, err = loadstring(command) - if not f then - return argerror(err, i) - end - setfenv(f, t_out)() - elseif v:find("=") then - local ids, val = v:match("^([^=]+)%=(.*)") - if not ids then - return argerror("invalid assignment syntax", i) - end - val = val:sub(1, 1) == "$" and val:sub(2) or tonumber(val) or val - for id in ids:gmatch("[^,;]+") do - if not idcheck(id) then - return iderror(i) - end - t_out[id] = val - end - else - tinsert(t_out, v) - end - end - if options then - local lookup, unknown = {}, {} - for _, v in ipairs(options) do - lookup[v] = true - end - for k, _ in pairs(t_out) do - if lookup[k] == nil and type(k) == "string" then - tinsert(unknown, k) - end - end - if # unknown > 0 then - return commonerror("unknown options: " .. tconcat(unknown, ", ")) - end - end - if params then - local missing = {} - for _, v in ipairs(params) do - if t_out[v] == nil then - tinsert(missing, v) - end - end - if # missing > 0 then - return commonerror("missing parameters: " .. tconcat(missing, ", ")) - end - end - return t_out - end -end -local cmdline = _() or cmdline -package["loaded"]["cmdline"] = cmdline or true -local util = require("util") -local candran = { ["VERSION"] = "0.2.0" } -candran["preprocess"] = function(input, args) - if args == nil then args = {} end - local preprocessor = "" - for line in (input .. "\ +package["loaded"]["lib.lua-parser.parser"] = parser or true +local candran = { ["VERSION"] = "0.3.0" } +local default = { +["target"] = "lua53", ["indentation"] = "", ["newline"] = "\ +", ["requirePrefix"] = "CANDRAN_", ["mapLines"] = true, ["chunkname"] = "nil", ["rewriteErrors"] = true +} +candran["preprocess"] = function(input, options) +if options == nil then options = {} end +options = util["merge"](default, options) +local preprocessor = "" +local i = 0 +for line in (input .. "\ "):gmatch("(.-\ )") do - if line:match("^%s*#") and not line:match("^#!") then - preprocessor = preprocessor .. line:gsub("^%s*#", "") - else - preprocessor = preprocessor .. ("write(%q)"):format(line:sub(1, - 2)) .. "\ +i = i + 1 +if line:match("^%s*#") and not line:match("^#!") then +preprocessor = preprocessor .. line:gsub("^%s*#", "") +elseif options["mapLines"] then +preprocessor = preprocessor .. ("write(%q)"):format(line:sub(1, - 2) .. " -- " .. options["chunkname"] .. ":" .. i) .. "\ " - end - end - preprocessor = preprocessor .. "return output" - local env = {} - for k, v in pairs(_G) do - env[k] = v - end - for k, v in pairs(args) do - env[k] = v - end - env["candran"] = candran - env["output"] = "" - env["import"] = function(modpath, margs, autoRequire) - if margs == nil then margs = args end - if autoRequire == nil then autoRequire = true end - local filepath = assert(util["search"](modpath), "No module named \"" .. modpath .. "\"") - local f = io["open"](filepath) - if not f then - error("Can't open the module file to import") - end - for k, v in pairs(args) do - if margs[k] == nil then - margs[k] = v - end - end - local modcontent = candran["preprocess"](f:read("*a"), margs) - f:close() - local modname = modpath:match("[^%.]+$") - env["write"]("-- MODULE \"" .. modpath .. "\" --\ +else +preprocessor = preprocessor .. ("write(%q)"):format(line:sub(1, - 2)) .. "\ +" +end +end +preprocessor = preprocessor .. "return output" +local env = util["merge"](_G, options) +env["candran"] = candran +env["output"] = "" +env["import"] = function(modpath, margs, autoRequire) +if margs == nil then margs = {} end +if autoRequire == nil then autoRequire = true end +local filepath = assert(util["search"](modpath), "No module named \"" .. modpath .. "\"") +local f = io["open"](filepath) +if not f then +error("Can't open the module file to import") +end +margs = util["merge"](options, { ["chunkname"] = filepath }, margs) +local modcontent = candran["preprocess"](f:read("*a"), margs) +f:close() +local modname = modpath:match("[^%.]+$") +env["write"]("-- MODULE \"" .. modpath .. "\" --\ " .. "local function _()\ " .. modcontent .. "\ " .. "end\ " .. (autoRequire and "local " .. modname .. " = _() or " .. modname .. "\ " or "") .. "package.loaded[\"" .. modpath .. "\"] = " .. (autoRequire and modname or "_()") .. " or true\ " .. "-- END OF MODULE \"" .. modpath .. "\" --") - end - env["include"] = function(file) - local f = io["open"](file) - if not f then - error("Can't open the file " .. file .. " to include") - end - env["write"](f:read("*a")) - f:close() - end - env["write"] = function(...) - env["output"] = env["output"] .. table["concat"]({ ... }, "\9") .. "\ +end +env["include"] = function(file) +local f = io["open"](file) +if not f then +error("Can't open the file " .. file .. " to include") +end +env["write"](f:read("*a")) +f:close() +end +env["write"] = function(...) +env["output"] = env["output"] .. table["concat"]({ ... }, "\9") .. "\ " - end - env["placeholder"] = function(name) - if env[name] then - env["write"](env[name]) - end - end - local preprocess, err = util["loadenv"](candran["compile"](preprocessor, args["target"]), "candran preprocessor", env) - if not preprocess then - error("Error while creating Candran preprocessor: " .. err) - end - local success, output = pcall(preprocess) - if not success then - error("Error while preprocessing file: " .. output .. "\ -With preprocessor : \ -" .. preprocessor) - end - return output end -candran["compile"] = function(input, target) - if target == nil then target = "lua53" end - local parse = require("lua-parser.parser")["parse"] - local ast, errmsg = parse(input, "candran") - if not ast then - error("Compiler: error while parsing file: " .. errmsg) - end - return require("compiler." .. target)(ast) +env["placeholder"] = function(name) +if env[name] then +env["write"](env[name]) end -candran["make"] = function(code, args) - if args == nil then args = {} end - return candran["compile"](candran["preprocess"](code, args), args["target"]) +end +local preprocess, err = util["load"](candran["compile"](preprocessor, args), "candran preprocessor", env) +if not preprocess then +error("Error while creating Candran preprocessor: " .. err) +end +local success, output = pcall(preprocess) +if not success then +error("Error while preprocessing file: " .. output) +end +return output +end +candran["compile"] = function(input, options) +if options == nil then options = {} end +options = util["merge"](default, options) +local ast, errmsg = parser["parse"](input, "candran") +if not ast then +error("Compiler: error while parsing file: " .. errmsg) +end +return require("compiler." .. options["target"])(input, ast, options) +end +candran["make"] = function(code, options) +return candran["compile"](candran["preprocess"](code, options), options) +end +local errorRewritingActive = false +local codeCache = {} +candran["loadfile"] = function(filepath, env, options) +local f, err = io["open"](filepath) +if not f then +error("can't open the file: " .. err) +end +local content = f:read("*a") +f:close() +return candran["load"](content, filepath, env, options) +end +candran["load"] = function(chunk, chunkname, env, options) +if options == nil then options = {} end +options = util["merge"]({ ["chunkname"] = tostring(chunkname or chunk) }, options) +codeCache[options["chunkname"]] = candran["make"](chunk, options) +local f = util["load"](codeCache[options["chunkname"]], options["chunkname"], env) +if options["rewriteErrors"] == false then +return f +else +return function() +if not errorRewritingActive then +errorRewritingActive = true +local t = { xpcall(f, candran["messageHandler"]) } +errorRewritingActive = false +if t[1] == false then +error(t[2], 0) +end +return unpack(t, 2) +else +return f() +end +end +end +end +candran["dofile"] = function(filename, options) +return candran["loadfile"](filename, nil, options)() +end +candran["messageHandler"] = function(message) +return debug["traceback"](message, 2):gsub("(\ +?%s*)([^\ +]-)%:(%d+)%:", function(indentation, source, line) +line = tonumber(line) +local originalFile +local strName = source:match("%[string \"(.-)\"%]") +if strName then +if codeCache[strName] then +originalFile = codeCache[strName] +source = strName +end +else +local fi = io["open"](source, "r") +if fi then +originalFile = fi:read("*a") +end +fi:close() +end +if originalFile then +local i = 0 +for l in originalFile:gmatch("([^\ +]*)") do +i = i + 1 +if i == line then +local extSource, lineMap = l:match("%-%- (.-)%:(%d+)$") +if lineMap then +if extSource ~= source then +return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):" +else +return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):" +end +end +break +end +end +end +end) end candran["searcher"] = function(modpath) - local notfound = "" - local filepath - for path in package["path"]:gsub("%.lua", ".can"):gmatch("[^;]+") do - local path = path:gsub("%?", (modpath:gsub("%.", "/"))) - local f = io["open"](path) - if f then - f:close() - filepath = path - else - notfound = notfound .. "\ -\9no Candran file '" .. path .. "'" - end - end - if not filepath then - return notfound - end - local f = io["open"](filepath) - if not f then - error("Can't open the module file to import") - end - local modcontent = f:read("*a") - f:close() - return load(candran["make"](modcontent)) +local filepath = util["search"](modpath) +if not filepath then +return "\ +\9no candran file in package.path" end -return candran \ No newline at end of file +return candran["loadfile"](filepath) +end +candran["setup"] = function() +if _VERSION == "Lua 5.1" then +table["insert"](package["loaders"], 2, candran["searcher"]) +else +table["insert"](package["searchers"], 2, candran["searcher"]) +end +end +return candran diff --git a/compiler/lua53.can b/compiler/lua53.can index 7fea215..11621e3 100644 --- a/compiler/lua53.can +++ b/compiler/lua53.can @@ -1,13 +1,30 @@ -return function(ast, opts) - local options = { - indentation = "\t", - newline = "\n", - requirePrefix = "CANDRAN_" - } +return function(code, ast, options) + local lastInputPos = 1 + local prevLinePos = 1 + local lastSource = "nil" + local lastLine = 1 local indentLevel = 0 local function newline() - return options.newline .. string.rep(options.indentation, indentLevel) + local r = options.newline .. string.rep(options.indentation, indentLevel) + if options.mapLines then + local sub = code:sub(lastInputPos) + local source, line = sub:sub(1, sub:find("\n")):match("%-%- (.-)%:(%d+)\n") + + if source and line then + lastSource = source + lastLine = tonumber(line) + else + for _ in code:sub(prevLinePos, lastInputPos):gmatch("\n") do + lastLine += 1 + end + end + + prevLinePos = lastInputPos + + r = " -- " .. lastSource .. ":" .. lastLine .. r + end + return r end local function indent() indentLevel += 1 @@ -32,6 +49,9 @@ return function(ast, opts) local tags local function lua(ast, forceTag, ...) + if options.mapLines and ast.pos then + lastInputPos = ast.pos + end return tags[forceTag or ast.tag](ast, ...) end @@ -54,14 +74,30 @@ return function(ast, opts) Do = function(t) return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end" end, - -- Set{ {lhs+} opid? {expr+} } + -- Set{ {lhs+} (opid? = opid?)? {expr+} } Set = function(t) if #t == 2 then return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs") - else - local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], t[3][1] }, "Op") - for i=2, math.min(#t[3], #t[1]), 1 do - r = r .. ", " .. lua({ t[2], t[1][i], t[3][i] }, "Op") + elseif #t == 3 then + return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs") + elseif #t == 4 then + if t[3] == "=" then + local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], t[4][1] }, "Op") + for i=2, math.min(#t[4], #t[1]), 1 do + r = r .. ", " .. lua({ t[2], t[1][i], t[4][i] }, "Op") + end + return r + else + local r = lua(t[1], "_lhs") .. " = " .. lua({ t[3], t[4][1], t[1][1] }, "Op") + for i=2, math.min(#t[4], #t[1]), 1 do + r = r .. ", " .. lua({ t[3], t[4][i], t[1][i] }, "Op") + end + return r + end + else -- You are mad. + local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], { tag = "Op", t[4], t[5][1], t[1][1] } }, "Op") + for i=2, math.min(#t[5], #t[1]), 1 do + r = r .. ", " .. lua({ t[2], t[1][i], { tag = "Op", t[4], t[5][i], t[1][i] } }, "Op") end return r end @@ -274,12 +310,5 @@ return function(ast, opts) #placeholder("patch") - if opts then - for k, v in pairs(opts) do - options[k] = v - end - end - - local r = lua(ast) - return requireStr .. r + return requireStr .. lua(ast) .. newline() end diff --git a/cmdline.lua b/lib/cmdline.lua similarity index 93% rename from cmdline.lua rename to lib/cmdline.lua index f6ed67d..c27e0f9 100644 --- a/cmdline.lua +++ b/lib/cmdline.lua @@ -48,6 +48,8 @@ Parameters: example: var1,var2=$$125 --> var1,var2 = "$125","$125" * if value is convertible to number, it is a number example: var1,var2=125 --> var1,var2 = 125,125 + * if value is true of false, it is a boolean + example: var1=false --> var1 = false * otherwise it is a string example: name=John --> name = "John" @@ -86,7 +88,13 @@ return function(t_in, options, params) elseif v:find("=") then local ids, val = v:match("^([^=]+)%=(.*)") -- no space around = if not ids then return argerror("invalid assignment syntax", i) end - val = val:sub(1,1)=="$" and val:sub(2) or tonumber(val) or val + if val == "false" then + val = false + elseif val == "true" then + val = true + else + val = val:sub(1,1)=="$" and val:sub(2) or tonumber(val) or val + end for id in ids:gmatch"[^,;]+" do if not idcheck(id) then return iderror(i) end t_out[id] = val diff --git a/lua-parser/LICENSE b/lib/lua-parser/LICENSE similarity index 100% rename from lua-parser/LICENSE rename to lib/lua-parser/LICENSE diff --git a/lua-parser/README.md b/lib/lua-parser/README.md similarity index 100% rename from lua-parser/README.md rename to lib/lua-parser/README.md diff --git a/lua-parser/parser.lua b/lib/lua-parser/parser.lua similarity index 93% rename from lua-parser/parser.lua rename to lib/lua-parser/parser.lua index 6688bc1..55d36a8 100644 --- a/lua-parser/parser.lua +++ b/lib/lua-parser/parser.lua @@ -8,7 +8,7 @@ block: { stat* } stat: `Do{ stat* } - | `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2... + | `Set{ {lhs+} (opid? = opid?)? {expr+} } -- lhs1, lhs2... op=op e1, e2... | `While{ expr block } -- while e do b end | `Repeat{ block expr } -- repeat b until e | `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end @@ -28,7 +28,7 @@ expr: | `Boolean{ } | `Number{ } | `String{ } - | `Function{ { `Id{ }* `Dots? } block } + | `Function{ { ( `ParPair{ Id expr } | `Id{ } )* `Dots? } block } | `Table{ ( `Pair{ expr expr } | expr )* } | `Op{ opid expr expr? } | `Paren{ expr } -- significant to cut multiple values returns @@ -257,6 +257,14 @@ local function makeIndexOrCall (t1, t2) return { tag = "Index", pos = t1.pos, [1] = t1, [2] = t2[1] } end +local function fixAnonymousMethodParams(t1, t2) + if t1 == ":" then + t1 = t2 + table.insert(t1, 1, { tag = "Id", "self" }) + end + return t1 +end + -- grammar local G = { V"Lua", Lua = V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra"); @@ -288,7 +296,7 @@ local G = { V"Lua", LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal"); LocalFunc = tagC("Localrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat; LocalAssign = tagC("Local", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc()))); - Assignment = tagC("Set", V"VarList" * V"AssignmentOp" * expect(V"ExprList", "EListAssign")); + Assignment = tagC("Set", V"VarList" * V"BinOp"^-1 * (sym("=") / "=") * V"BinOp"^-1 * expect(V"ExprList", "EListAssign")); FuncStat = tagC("Set", kw("function") * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat; FuncName = Cf(V"Id" * (sym(".") * expect(V"StrId", "NameFunc1"))^0, insertIndex) @@ -342,13 +350,20 @@ local G = { V"Lua", VarExpr = Cmt(V"SuffixedExpr", function(s, i, exp) return exp.tag == "Id" or exp.tag == "Index", exp end); SuffixedExpr = Cf(V"PrimaryExpr" * (V"Index" + V"Call")^0, makeIndexOrCall); - PrimaryExpr = V"Id" + tagC("Paren", sym("(") * expect(V"Expr", "ExprParen") * expect(sym(")"), "CParenExpr")); + PrimaryExpr = V"SelfId" * (V"SelfCall" + V"SelfIndex") + + V"Id" + + tagC("Paren", sym("(") * expect(V"Expr", "ExprParen") * expect(sym(")"), "CParenExpr")); Index = tagC("DotIndex", sym("." * -P".") * expect(V"StrId", "NameIndex")) + tagC("ArrayIndex", sym("[" * -P(S"=[")) * expect(V"Expr", "ExprIndex") * expect(sym("]"), "CBracketIndex")); Call = tagC("Invoke", Cg(sym(":" * -P":") * expect(V"StrId", "NameMeth") * expect(V"FuncArgs", "MethArgs"))) + tagC("Call", V"FuncArgs"); + SelfIndex = tagC("DotIndex", V"StrId"); + SelfCall = tagC("Invoke", Cg(V"StrId" * V"FuncArgs")); - FuncDef = kw("function") * V"FuncBody"; + ShortFuncDef = tagC("Function", V"ShortFuncParams" * V"Block" * expect(kw("end"), "EndFunc")); + ShortFuncParams = (sym(":") / ":")^-1 * sym("(") * V"ParList" * sym(")") / fixAnonymousMethodParams; + + FuncDef = (kw("function") * V"FuncBody") + V"ShortFuncDef"; FuncArgs = sym("(") * commaSep(V"Expr", "ArgList")^-1 * expect(sym(")"), "CParenArgs") + V"Table" + tagC("String", V"String"); @@ -361,7 +376,8 @@ local G = { V"Lua", + V"StrId" * #("=" * -P"="); FieldSep = sym(",") + sym(";"); - Id = tagC("Id", V"Name"); + SelfId = tagC("Id", sym"@" / "self"); + Id = tagC("Id", V"Name") + V"SelfId"; StrId = tagC("String", V"Name"); -- lexer @@ -455,12 +471,12 @@ local G = { V"Lua", + sym("#") / "len" + sym("~") / "bnot"; PowOp = sym("^") / "pow"; - AssignmentOp = (V"OrOp" + V"AndOp" + V"BOrOp" + V"BXorOp" + V"BAndOp" + V"ShiftOp" + V"ConcatOp" + V"AddOp" + V"MulOp" + V"PowOp")^-1 * sym("=") + BinOp = V"OrOp" + V"AndOp" + V"BOrOp" + V"BXorOp" + V"BAndOp" + V"ShiftOp" + V"ConcatOp" + V"AddOp" + V"MulOp" + V"PowOp"; } local parser = {} -local validator = require("lua-parser.validator") +local validator = require("lib.lua-parser.validator") local validate = validator.validate local syntaxerror = validator.syntaxerror diff --git a/lua-parser/pp.lua b/lib/lua-parser/pp.lua similarity index 100% rename from lua-parser/pp.lua rename to lib/lua-parser/pp.lua diff --git a/lua-parser/scope.lua b/lib/lua-parser/scope.lua similarity index 100% rename from lua-parser/scope.lua rename to lib/lua-parser/scope.lua diff --git a/lua-parser/validator.lua b/lib/lua-parser/validator.lua similarity index 99% rename from lua-parser/validator.lua rename to lib/lua-parser/validator.lua index faa3c4d..9ae9160 100644 --- a/lua-parser/validator.lua +++ b/lib/lua-parser/validator.lua @@ -1,7 +1,7 @@ --[[ This module impements a validator for the AST ]] -local scope = require "lua-parser.scope" +local scope = require "lib.lua-parser.scope" local lineno = scope.lineno local new_scope, end_scope = scope.new_scope, scope.end_scope diff --git a/util.can b/lib/util.can similarity index 66% rename from util.can rename to lib/util.can index f1cdc0b..c19c448 100644 --- a/util.can +++ b/lib/util.can @@ -13,14 +13,28 @@ function util.search(modpath, exts={"can", "lua"}) end end -function util.loadenv(str, name, env) +function util.load(str, name, env) if _VERSION == "Lua 5.1" then local fn, err = loadstring(str, name) if not fn then return fn, err end return env ~= nil and setfenv(fn, env) or fn else - return load(str, name, nil, env) + if env then + return load(str, name, nil, env) + else + return load(str, name) + end end end +function util.merge(...) + local r = {} + for _, t in ipairs({...}) do + for k, v in pairs(t) do + r[k] = v + end + end + return r +end + return util diff --git a/rockspec/candran-0.3.0-1.rockspec b/rockspec/candran-0.3.0-1.rockspec new file mode 100644 index 0000000..17492c3 --- /dev/null +++ b/rockspec/candran-0.3.0-1.rockspec @@ -0,0 +1,37 @@ +package = "Candran" + +version = "0.3.0-1" + +description = { + summary = "A simple Lua dialect and preprocessor.", + detailed = [[ + Candran is a dialect of the Lua 5.3 programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds a preprocessor and several useful syntax additions. + Unlike Moonscript, Candran tries to stay close to the Lua syntax. + ]], + license = "MIT", + homepage = "https://github.com/Reuh/Candran", + issues_url = "https://github.com/Reuh/Candran", + maintener = "Étienne 'Reuh' Fildadut ", + --labels = {} +} + +source = { + url = "git://github.com/Reuh/Candran", + tag = "v0.3.0" +} + +dependencies = { + "lua >= 5.1", + "lpeglabel >= 1.0.0" +} + +build = { + type = "builtin", + modules = { + candran = "candran.lua" + }, + install = { + bin = { "bin/can", "bin/canc" } + } + --copy_directories = { "doc", "test" } +} diff --git a/tests/test.lua b/test/test.lua similarity index 100% rename from tests/test.lua rename to test/test.lua diff --git a/tests/toInclude.lua b/test/toInclude.lua similarity index 100% rename from tests/toInclude.lua rename to test/toInclude.lua