mirror of
https://github.com/Reuh/candran.git
synced 2025-10-27 17:59:30 +00:00
Candran 0.2
Changed a LOT. Notable changes: * Removed decorators, as they're not that useful, unless rewriting most Lua libraries API. * Added functions parameters default values. * Added Lua 5.3 stuff and building to Lua 5.1. * Remplaced the LuaMinify parser by lua-parser. It now requires some non-Lua dependencies (LPeg) unfortunately, but it's waaaaaay easier to handle. Code should be adaptable to any Metalua-like AST generator anyway. * The generated code now look like shit, and comment are stripped, because the parser ignore them. Oh well. * Changed a few things in the preprocessor environment. * Nobody will read this commit message I guess. If you did, create an issue saying "I love pineapple flavored bread".
This commit is contained in:
parent
1875ea31de
commit
2a1e293aa5
45 changed files with 3899 additions and 11569 deletions
7
LICENSE
Normal file
7
LICENSE
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright 2017 Étienne "Reuh" Fildadut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
108
README.md
108
README.md
|
|
@ -1,34 +1,30 @@
|
|||
Candran
|
||||
=======
|
||||
Candran is a dialect of the [Lua](http://www.lua.org) programming language which compiles to Lua. It adds a preprocessor and several useful syntax additions.
|
||||
Candran is a dialect of the [Lua 5.3](http://www.lua.org) 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.
|
||||
|
||||
Candran code example :
|
||||
|
||||
````lua
|
||||
#import("lib.thing")
|
||||
#local debug = args.debug or false
|
||||
#local debug = debug or false
|
||||
|
||||
local function debugArgs(func)
|
||||
return function(...)
|
||||
#if debug then
|
||||
for _,arg in pairs({...}) do
|
||||
print(arg, type(arg))
|
||||
end
|
||||
#end
|
||||
return func(...)
|
||||
end
|
||||
end
|
||||
|
||||
@debugArgs
|
||||
local function calculate()
|
||||
local function calculate(toadd=25)
|
||||
local result = thing.do()
|
||||
result += 25
|
||||
result += toadd
|
||||
#if debug then
|
||||
print("Did something")
|
||||
#end
|
||||
return result
|
||||
end
|
||||
|
||||
print(calculate())
|
||||
````
|
||||
|
||||
##### Quick setup
|
||||
Install LPegLabel (```luarocks install LPegLabel```), download this repository and use Candran through ```canc.lua``` or ```candran.lua```.
|
||||
|
||||
The language
|
||||
------------
|
||||
### Preprocessor
|
||||
|
|
@ -36,7 +32,7 @@ Before compiling, Candran's preprocessor is run. It execute every line starting
|
|||
For example,
|
||||
|
||||
````lua
|
||||
#if args.lang == "fr" then
|
||||
#if lang == "fr" then
|
||||
print("Bonjour")
|
||||
#else
|
||||
print("Hello")
|
||||
|
|
@ -47,11 +43,12 @@ Will output ````print("Bonjour")```` or ````print("Hello")```` depending of the
|
|||
|
||||
The preprocessor has access to the following variables :
|
||||
* ````candran```` : the Candran library table.
|
||||
* ````output```` : the preprocessor output string.
|
||||
* ````import(module[, autoRequire])```` : a function which import a module. This is equivalent to use _require(module)_ in the Candran code, except the module will be embedded in the current file. _autoRequire_ (boolean, default true) indicate if the module should be automaticaly loaded in a local variable or not. If true, the local variable will have the name of the module.
|
||||
* ````output```` : the current preprocessor output string.
|
||||
* ````import(module[, [args, autoRequire]])```` : a function which import a module. This is equivalent to use _require(module)_ in the Candran code, except the module will be embedded in the current file. _args_ is an optional preprocessor arguments table for the imported module (current preprocessor arguments will be inherited). _autoRequire_ (boolean, default true) indicate if the module should be automaticaly loaded in a local variable or not. If true, the local variable will have the name of the module.
|
||||
* ````include(filename)```` : a function which copy the contents of the file _filename_ to the output.
|
||||
* ````print(...)```` : instead of writing to stdout, _print(...)_ will write to the preprocessor output. For example, ````#print("hello()")```` will output ````hello()````.
|
||||
* ````args```` : the arguments table passed to the compiler. Example use : ````withDebugTools = args["debug"]````.
|
||||
* ````write(...)```` : write to the preprocessor output. For example, ````#print("hello()")```` will output ````hello()```` in the final file.
|
||||
* ```placeholder(name)``` : if the variable _name_ is defined in the preprocessor environement, its content will be inserted here.
|
||||
* ````...```` : each arguments passed to the preprocessor is directly available.
|
||||
* and every standard Lua library.
|
||||
|
||||
### Syntax additions
|
||||
|
|
@ -61,56 +58,70 @@ After the preprocessor is run the Candran code is compiled to Lua. The Candran c
|
|||
* ````var -= nb````
|
||||
* ````var *= nb````
|
||||
* ````var /= nb````
|
||||
* ````var //= nb````
|
||||
* ````var ^= nb````
|
||||
* ````var %= nb````
|
||||
* ````var ..= str````
|
||||
* ````var and= str````
|
||||
* ````var or= str````
|
||||
* ````var &= nb````
|
||||
* ````var |= nb````
|
||||
* ````var <<= nb````
|
||||
* ````var >>= nb````
|
||||
|
||||
For example, a ````var += nb```` assignment will be compiled into ````var = var + nb````.
|
||||
|
||||
##### Decorators
|
||||
Candran supports function decorators similar to Python. A decorator is a function returning another function, and allows easy function modification with this syntax :
|
||||
````lua
|
||||
@decorator
|
||||
function name(...)
|
||||
...
|
||||
##### Default function parameters
|
||||
```lua
|
||||
function foo(bar = "default", other = thing.do())
|
||||
-- stuff
|
||||
end
|
||||
````
|
||||
This is equivalent to :
|
||||
````lua
|
||||
function name(...)
|
||||
...
|
||||
end
|
||||
name = decorator(name)
|
||||
````
|
||||
The decorators can be chained. Note that Candran allows this syntax for every variable, not only functions.
|
||||
```
|
||||
If an argument isn't provided or ```nil``` when the function is called, it will be automatically set to a default value.
|
||||
|
||||
It is equivalent to doing ```if arg == nil then arg = default end``` for each argument at the start of the function.
|
||||
|
||||
The default values can be complete Lua expressions, and will be evaluated each time the function is run.
|
||||
|
||||
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.
|
||||
|
||||
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 :
|
||||
The library can be used standalone through the ```canc``` utility:
|
||||
|
||||
* ````lua candran.lua````
|
||||
* ````lua canc.lua````
|
||||
|
||||
Display the information text (version and basic command-line usage).
|
||||
|
||||
* ````lua candran.lua <filename> [arguments]````
|
||||
* ````lua canc.lua [arguments] filename...````
|
||||
|
||||
Output to stdout the _filename_ Candran file, preprocessed (with _arguments_) and compiled to Lua.
|
||||
Preprocess and compile each _filename_ Candran files, and creates the assiociated ```.lua``` files in the same directories.
|
||||
|
||||
_arguments_ is of type ````--somearg value --anotherarg anothervalue ...````.
|
||||
_arguments_ 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.
|
||||
|
||||
* example uses :
|
||||
|
||||
````lua candran.lua foo.can > foo.lua````
|
||||
````lua canc.lua foo.can````
|
||||
|
||||
preprocess and compile _foo.can_ and write the result in _foo.lua_.
|
||||
|
||||
````lua candran.lua foo.can --verbose true | lua````
|
||||
````lua canc.lua foo.can -verbose -print | lua````
|
||||
|
||||
preprocess _foo.can_ with _verbose_ set to _true_, compile it and execute it.
|
||||
|
||||
### Library usage
|
||||
Candran can also be used as a normal Lua library. For example,
|
||||
Candran can also be used as a Lua library. For example,
|
||||
````lua
|
||||
local candran = require("candran")
|
||||
|
||||
|
|
@ -126,18 +137,19 @@ Will load Candran, read the file _foo.can_, compile its contents with the argume
|
|||
|
||||
The table returned by _require("candran")_ gives you access to :
|
||||
* ````candran.VERSION```` : Candran's version string.
|
||||
* ````candran.syntax```` : table containing all the syntax additions of Candran.
|
||||
* ````candran.preprocess(code[, args])```` : return the Candran code _code_, preprocessed with _args_ as argument table.
|
||||
* ````candran.compile(code)```` : return the Candran code compiled to Lua.
|
||||
* ````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.
|
||||
|
||||
### Compiling the library
|
||||
The Candran library itself is written is Candran, so you have to compile it with an already compiled Candran library.
|
||||
|
||||
This command will use the precompilled version of this repository (build/candran.lua) to compile _candran.can_ and write the result in _candran.lua_ :
|
||||
The compiled _candran.lua_ should include every Lua library needed to run it. You will still need to install LPegLabel.
|
||||
|
||||
This command will use the precompilled version of this repository (candran.lua) to compile _candran.can_ and write the result in _candran.lua_ :
|
||||
|
||||
````
|
||||
lua build/candran.lua candran.can > candran.lua
|
||||
lua canc.lua candran.can
|
||||
````
|
||||
|
||||
You can then run the tests on your build :
|
||||
|
|
|
|||
2764
build/candran.lua
2764
build/candran.lua
File diff suppressed because it is too large
Load diff
44
canc.lua
Normal file
44
canc.lua
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/lua
|
||||
local candran = require("candran")
|
||||
local cmdline = require("cmdline")
|
||||
|
||||
if #arg < 1 then
|
||||
print("Candran compiler version "..candran.VERSION.." by Reuh")
|
||||
print("Usage: "..arg[0].." [target=<target>] [dest=<destination directory>] [-print] [preprocessor arguments] filename...")
|
||||
return
|
||||
end
|
||||
|
||||
local args = cmdline(arg)
|
||||
|
||||
for _, file in ipairs(args) do
|
||||
local dest = file:gsub("%.can$", "")..".lua"
|
||||
if args.dest then
|
||||
dest = args.dest .. "/" .. dest
|
||||
end
|
||||
|
||||
if not args.print then
|
||||
print("Compiling "..file.." in "..dest)
|
||||
end
|
||||
|
||||
local inputFile, err = io.open(file, "r")
|
||||
if not inputFile then error("Error while opening input file: "..err) end
|
||||
local input = inputFile:read("*a")
|
||||
inputFile:close()
|
||||
|
||||
local out = candran.make(input, args)
|
||||
|
||||
if args.print then
|
||||
print(out)
|
||||
else
|
||||
local outFile = io.open(dest, "w")
|
||||
if not outFile then
|
||||
os.execute("mkdir -p "..dest:gsub("[^/]+%.lua$", ""))
|
||||
outFile, err = io.open(dest, "w")
|
||||
if not outFile then
|
||||
error("Error while writing output file: "..err)
|
||||
end
|
||||
end
|
||||
outFile:write(out)
|
||||
outFile:close()
|
||||
end
|
||||
end
|
||||
206
candran.can
206
candran.can
|
|
@ -1,170 +1,148 @@
|
|||
--[[
|
||||
Candran language, preprocessor and compiler by Thomas99.
|
||||
#import("util")
|
||||
#import("compiler.lua53")
|
||||
#import("compiler.luajit")
|
||||
#import("lua-parser.scope")
|
||||
#import("lua-parser.validator")
|
||||
#import("lua-parser.pp")
|
||||
#import("lua-parser.parser")
|
||||
#import("cmdline")
|
||||
|
||||
LICENSE :
|
||||
Copyright (c) 2015 Thomas99
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the
|
||||
use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including
|
||||
commercial applications, and to alter it and redistribute it freely, subject
|
||||
to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software in a
|
||||
product, an acknowledgment in the product documentation would be appreciated
|
||||
but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
]]
|
||||
local util = require("util")
|
||||
|
||||
local candran = {
|
||||
VERSION = "0.1.0",
|
||||
syntax = {
|
||||
assignment = { "+=", "-=", "*=", "/=", "^=", "%=", "..=" },
|
||||
decorator = "@"
|
||||
}
|
||||
VERSION = "0.2.0"
|
||||
}
|
||||
package.loaded["candran"] = candran
|
||||
|
||||
#import("lib.table")
|
||||
#import("lib.LuaMinify.Util")
|
||||
#import("lib.LuaMinify.Scope")
|
||||
#import("lib.LuaMinify.ParseCandran")
|
||||
#import("lib.LuaMinify.FormatIdentityCandran")
|
||||
|
||||
-- Preprocessor
|
||||
function candran.preprocess(input, args)
|
||||
-- generate preprocessor
|
||||
local preprocessor = "return function()\n"
|
||||
|
||||
local lines = {}
|
||||
for line in (input.."\n"):gmatch("(.-)\n") do
|
||||
table.insert(lines, line)
|
||||
-- preprocessor instructions (exclude shebang)
|
||||
if line:match("^%s*#") and not (line:match("^#!") and #lines == 1) then
|
||||
preprocessor ..= line:gsub("^%s*#", "") .. "\n"
|
||||
--- Run the preprocessor
|
||||
-- @tparam input string input code
|
||||
-- @tparam args table arguments for the preprocessor. They will be inserted into the preprocessor environement.
|
||||
-- @treturn output string output code
|
||||
function candran.preprocess(input, args={})
|
||||
-- generate preprocessor code
|
||||
local preprocessor = ""
|
||||
for line in (input.."\n"):gmatch("(.-\n)") do
|
||||
if line:match("^%s*#") and not line:match("^#!") then -- exclude shebang
|
||||
preprocessor ..= line:gsub("^%s*#", "")
|
||||
else
|
||||
preprocessor ..= "output ..= lines[" .. #lines .. "] .. \"\\n\"\n"
|
||||
preprocessor ..= ("write(%q)"):format(line:sub(1, -2)) .. "\n"
|
||||
end
|
||||
end
|
||||
preprocessor ..= "return output\nend"
|
||||
preprocessor ..= "return output"
|
||||
|
||||
-- make preprocessor environement
|
||||
local env = table.copy(_G)
|
||||
local env = {}
|
||||
for k,v in pairs(_G) do
|
||||
env[k] = v
|
||||
end
|
||||
for k, v in pairs(args) do
|
||||
env[k] = v
|
||||
end
|
||||
--- Candran library table
|
||||
env.candran = candran
|
||||
--- Current preprocessor output
|
||||
env.output = ""
|
||||
env.import = function(modpath, autoRequire)
|
||||
local autoRequire = (autoRequire == nil) or autoRequire
|
||||
|
||||
-- get module filepath
|
||||
local filepath
|
||||
for _,search in ipairs(package.searchers) do
|
||||
local loader, path = search(modpath)
|
||||
if type(loader) == "function" and type(path) == "string" then
|
||||
filepath = path
|
||||
break
|
||||
end
|
||||
end
|
||||
if not filepath then error("No module named \""..modpath.."\"") end
|
||||
--- Import an external Candran/Lua module into the generated file
|
||||
-- @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)
|
||||
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
|
||||
local modcontent = f:read("*a")
|
||||
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()
|
||||
|
||||
-- get module name (ex: module name of path.to.module is module)
|
||||
local modname = modpath:match("[^%.]+$")
|
||||
|
||||
--
|
||||
env.output ..=
|
||||
"-- IMPORT OF MODULE \""..modpath.."\" --\n"..
|
||||
env.write(
|
||||
"-- MODULE \""..modpath.."\" --\n"..
|
||||
"local function _()\n"..
|
||||
modcontent.."\n"..
|
||||
"end\n"..
|
||||
(autoRequire and "local "..modname.." = _() or "..modname.."\n" or "").. -- auto require
|
||||
"package.loaded[\""..modpath.."\"] = "..(autoRequire and modname or "_()").." or true\n".. -- add to package.loaded
|
||||
"-- END OF IMPORT OF MODULE \""..modpath.."\" --\n"
|
||||
"-- END OF MODULE \""..modpath.."\" --"
|
||||
)
|
||||
end
|
||||
--- Include another file content in the preprocessor output.
|
||||
-- @tparam file string filepath
|
||||
env.include = function(file)
|
||||
local f = io.open(file)
|
||||
if not f then error("Can't open the file to include") end
|
||||
env.output ..= f:read("*a").."\n"
|
||||
if not f then error("Can't open the file "..file.." to include") end
|
||||
env.write(f:read("*a"))
|
||||
f:close()
|
||||
end
|
||||
env.print = function(...)
|
||||
--- Write a line in the preprocessor output.
|
||||
-- @tparam ... string strings to write (similar to print)
|
||||
env.write = function(...)
|
||||
env.output ..= table.concat({...}, "\t") .. "\n"
|
||||
end
|
||||
env.args = args or {}
|
||||
env.lines = lines
|
||||
--- Will be replaced with the content of the variable with the given name, if it exists.
|
||||
-- @tparam name string variable name
|
||||
env.placeholder = function(name)
|
||||
if env[name] then
|
||||
env.write(env[name])
|
||||
end
|
||||
end
|
||||
|
||||
-- load preprocessor
|
||||
local preprocess, err = load(candran.compile(preprocessor), "Preprocessor", nil, env)
|
||||
if not preprocess then error("Error while creating preprocessor :\n" .. err) end
|
||||
-- compile & load preprocessor
|
||||
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
|
||||
|
||||
-- execute preprocessor
|
||||
local success, output = pcall(preprocess())
|
||||
if not success then error("Error while preprocessing file :\n" .. output .. "\nWith preprocessor : \n" .. preprocessor) end
|
||||
local success, output = pcall(preprocess)
|
||||
if not success then error("Error while preprocessing file: " .. output .. "\nWith preprocessor : \n" .. preprocessor) end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
-- Compiler
|
||||
function candran.compile(input)
|
||||
local parse = require("lib.LuaMinify.ParseCandran")
|
||||
local format = require("lib.LuaMinify.FormatIdentityCandran")
|
||||
function candran.compile(input, target="lua53")
|
||||
local parse = require("lua-parser.parser").parse
|
||||
|
||||
local success, ast = parse.ParseLua(input)
|
||||
if not success then error("Error while parsing the file :\n"..tostring(ast)) end
|
||||
local ast, errmsg = parse(input, "candran")
|
||||
|
||||
local success, output = format(ast)
|
||||
if not success then error("Error while formating the file :\n"..tostring(output)) end
|
||||
if not ast then
|
||||
error("Compiler: error while parsing file: "..errmsg)
|
||||
end
|
||||
|
||||
return output
|
||||
return require("compiler."..target)(ast)
|
||||
end
|
||||
|
||||
-- Preprocess & compile
|
||||
function candran.make(code, args)
|
||||
local preprocessed = candran.preprocess(code, args or {})
|
||||
local output = candran.compile(preprocessed)
|
||||
|
||||
return output
|
||||
function candran.make(code, args={})
|
||||
return candran.compile(candran.preprocess(code, args), args.target)
|
||||
end
|
||||
|
||||
-- Standalone mode
|
||||
if debug.getinfo(3) == nil and arg then
|
||||
-- Check args
|
||||
if #arg < 1 then
|
||||
print("Candran version "..candran.VERSION.." by Thomas99")
|
||||
print("Command-line usage :")
|
||||
print("lua candran.lua <filename> [preprocessor arguments]")
|
||||
return candran
|
||||
end
|
||||
|
||||
-- Parse args
|
||||
local inputFilepath = arg[1]
|
||||
local args = {}
|
||||
for i=2, #arg, 1 do
|
||||
if arg[i]:sub(1,2) == "--" then
|
||||
args[arg[i]:sub(3)] = arg[i+1]
|
||||
i = i + 1 -- skip argument value
|
||||
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 & read input file
|
||||
local inputFile, err = io.open(inputFilepath, "r")
|
||||
if not inputFile then error("Error while opening input file : "..err) end
|
||||
local input = inputFile:read("*a")
|
||||
inputFile:close()
|
||||
-- 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")
|
||||
f:close()
|
||||
|
||||
-- Make
|
||||
print(candran.make(input, args))
|
||||
return load(candran.make(modcontent))
|
||||
end
|
||||
|
||||
return candran
|
||||
1813
candran.lua
Normal file
1813
candran.lua
Normal file
File diff suppressed because it is too large
Load diff
118
cmdline.lua
Normal file
118
cmdline.lua
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
-- started: 2008-04-12 by Shmuel Zeigerman
|
||||
-- license: public domain
|
||||
|
||||
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
|
||||
|
||||
--[[------------------------------------------------------------------------
|
||||
Syntax:
|
||||
t_out = getparam(t_in [,options] [,params])
|
||||
|
||||
Parameters:
|
||||
t_in: table - list of string arguments to be processed in order
|
||||
(usually it is the `arg' table created by the Lua interpreter).
|
||||
|
||||
* if an argument begins with $, the $ is skipped and the rest is inserted
|
||||
into the array part of the output table.
|
||||
|
||||
* if an argument begins with -, the rest is a sequence of variables
|
||||
(separated by commas or semicolons) that are all set to true;
|
||||
example: -var1,var2 --> var1,var2 = true,true
|
||||
|
||||
* if an argument begins with !, the rest is a Lua chunk;
|
||||
example: !a=(40+3)*5;b=20;name="John";window={w=600,h=480}
|
||||
|
||||
* if an argument contains =, then it is an assignment in the form
|
||||
var1,...=value (no space is allowed around the =)
|
||||
* if value begins with $, the $ is skipped, the rest is a string
|
||||
example: var1,var2=$ --> var1,var2 = "",""
|
||||
example: var1,var2=$125 --> var1,var2 = "125","125"
|
||||
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
|
||||
* otherwise it is a string
|
||||
example: name=John --> name = "John"
|
||||
|
||||
* if an argument neither begins with one of the special characters (-,!,$),
|
||||
nor contains =, it is inserted as is into the array part of the output
|
||||
table.
|
||||
|
||||
options (optional): a list of names of all command-line options and parameters
|
||||
permitted in the application; used to check that each found option
|
||||
is valid; no checks are done if not supplied.
|
||||
|
||||
params (optional): a list of names of all command-line parameters required
|
||||
by the application; used to check that each required parameter is present;
|
||||
no checks are done if not supplied.
|
||||
|
||||
Returns:
|
||||
On success: the output table, e.g. { [1]="./myfile.txt", name="John", age=40 }
|
||||
On error: nil followed by error message string.
|
||||
|
||||
--]]------------------------------------------------------------------------
|
||||
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("^([^=]+)%=(.*)") -- 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
|
||||
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
|
||||
285
compiler/lua53.can
Normal file
285
compiler/lua53.can
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
return function(ast, opts)
|
||||
local options = {
|
||||
indentation = "\t",
|
||||
newline = "\n",
|
||||
requirePrefix = "CANDRAN_"
|
||||
}
|
||||
|
||||
local indentLevel = 0
|
||||
local function newline()
|
||||
return options.newline .. string.rep(options.indentation, indentLevel)
|
||||
end
|
||||
local function indent()
|
||||
indentLevel += 1
|
||||
return newline()
|
||||
end
|
||||
local function unindent()
|
||||
indentLevel -= 1
|
||||
return newline()
|
||||
end
|
||||
|
||||
local required = {}
|
||||
local requireStr = ""
|
||||
local function addRequire(str, name, field)
|
||||
if not required[str] then
|
||||
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: { stat* } --
|
||||
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,
|
||||
|
||||
-- stat --
|
||||
|
||||
-- Do{ stat* }
|
||||
Do = function(t)
|
||||
return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end"
|
||||
end,
|
||||
-- Set{ {lhs+} 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")
|
||||
end
|
||||
return r
|
||||
end
|
||||
end,
|
||||
-- While{ expr block }
|
||||
While = function(t)
|
||||
return "while " .. lua(t[1]) .. " do" .. indent() .. lua(t[2]) .. unindent() .. "end"
|
||||
end,
|
||||
-- Repeat{ block expr }
|
||||
Repeat = function(t)
|
||||
return "repeat".. indent() .. lua(t[1]) .. unindent() .. "until " .. lua(t[2])
|
||||
end,
|
||||
-- If{ (expr block)+ block? }
|
||||
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{ ident expr expr expr? block }
|
||||
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{ {ident+} {expr+} block }
|
||||
Forin = function(t)
|
||||
return "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent() .. lua(t[3]) .. unindent() .. "end"
|
||||
end,
|
||||
-- Local{ {ident+} {expr+}? }
|
||||
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{ ident expr }
|
||||
Localrec = function(t)
|
||||
return "local function "..lua(t[1][1])..lua(t[2][1], "_functionWithoutKeyword")
|
||||
end,
|
||||
-- Goto{ <string> }
|
||||
Goto = function(t)
|
||||
return "goto " .. lua(t[1], "Id")
|
||||
end,
|
||||
-- Label{ <string> }
|
||||
Label = function(t)
|
||||
return "::" .. lua(t[1], "Id") .. "::"
|
||||
end,
|
||||
-- Return{ <expr*> }
|
||||
Return = function(t)
|
||||
return "return "..lua(t, "_lhs")
|
||||
end,
|
||||
-- Break
|
||||
Break = function()
|
||||
return "break"
|
||||
end,
|
||||
-- apply (below)
|
||||
|
||||
-- expr --
|
||||
|
||||
-- Nil
|
||||
Nil = function()
|
||||
return "nil"
|
||||
end,
|
||||
-- Dots
|
||||
Dots = function()
|
||||
return "..."
|
||||
end,
|
||||
-- Boolean{ <boolean> }
|
||||
Boolean = function(t)
|
||||
return tostring(t[1])
|
||||
end,
|
||||
-- Number{ <number> }
|
||||
Number = function(t)
|
||||
return tostring(t[1])
|
||||
end,
|
||||
-- String{ <string> }
|
||||
String = function(t)
|
||||
return ("%q"):format(t[1])
|
||||
end,
|
||||
-- Function{ { ( `ParPair{ Id expr } | `Id{ <string> } )* `Dots? } block }
|
||||
_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 += 1
|
||||
table.insert(decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id)
|
||||
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 += 1
|
||||
table.insert(decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][i][2]) .. " end")
|
||||
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,
|
||||
-- Table{ ( `Pair{ expr expr } | expr )* }
|
||||
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{ opid expr expr? }
|
||||
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{ expr }
|
||||
Paren = function(t)
|
||||
return "(" .. lua(t[1]) .. ")"
|
||||
end,
|
||||
-- apply (below)
|
||||
-- lhs (below)
|
||||
|
||||
-- apply --
|
||||
|
||||
-- Call{ expr expr* }
|
||||
Call = function(t)
|
||||
return lua(t[1]) .. "(" .. lua(t, "_lhs", 2) .. ")"
|
||||
end,
|
||||
|
||||
-- Invoke{ expr `String{ <string> } expr* }
|
||||
Invoke = function(t)
|
||||
return lua(t[1])..":"..lua(t[2], "Id").."("..lua(t, "_lhs", 3)..")"
|
||||
end,
|
||||
|
||||
-- lhs --
|
||||
_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{ <string> }
|
||||
Id = function(t)
|
||||
return t[1]
|
||||
end,
|
||||
-- Index{ expr expr }
|
||||
Index = function(t)
|
||||
return lua(t[1]).."["..lua(t[2]).."]"
|
||||
end,
|
||||
|
||||
-- opid --
|
||||
_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
|
||||
})
|
||||
|
||||
#placeholder("patch")
|
||||
|
||||
if opts then
|
||||
for k, v in pairs(opts) do
|
||||
options[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local r = lua(ast)
|
||||
return requireStr .. r
|
||||
end
|
||||
33
compiler/luajit.can
Normal file
33
compiler/luajit.can
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
|
||||
#local patch = output
|
||||
#output = ""
|
||||
#import("compiler.lua53", { patch = patch })
|
||||
|
||||
return lua53
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
--
|
||||
-- beautify
|
||||
--
|
||||
-- A command line utility for beautifying lua source code using the beautifier.
|
||||
--
|
||||
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format_Beautify = require'FormatBeautiful'
|
||||
local ParseLua = Parser.ParseLua
|
||||
local PrintTable = util.PrintTable
|
||||
|
||||
local function splitFilename(name)
|
||||
--table.foreach(arg, print)
|
||||
if name:find(".") then
|
||||
local p, ext = name:match("()%.([^%.]*)$")
|
||||
if p and ext then
|
||||
if #ext == 0 then
|
||||
return name, nil
|
||||
else
|
||||
local filename = name:sub(1,p-1)
|
||||
return filename, ext
|
||||
end
|
||||
else
|
||||
return name, nil
|
||||
end
|
||||
else
|
||||
return name, nil
|
||||
end
|
||||
end
|
||||
|
||||
if #arg == 1 then
|
||||
local name, ext = splitFilename(arg[1])
|
||||
local outname = name.."_formatted"
|
||||
if ext then outname = outname.."."..ext end
|
||||
--
|
||||
local inf = io.open(arg[1], 'r')
|
||||
if not inf then
|
||||
print("Failed to open '"..arg[1].."' for reading")
|
||||
return
|
||||
end
|
||||
--
|
||||
local sourceText = inf:read('*all')
|
||||
inf:close()
|
||||
--
|
||||
local st, ast = ParseLua(sourceText)
|
||||
if not st then
|
||||
--we failed to parse the file, show why
|
||||
print(ast)
|
||||
return
|
||||
end
|
||||
--
|
||||
local outf = io.open(outname, 'w')
|
||||
if not outf then
|
||||
print("Failed to open '"..outname.."' for writing")
|
||||
return
|
||||
end
|
||||
--
|
||||
outf:write(Format_Beautify(ast))
|
||||
outf:close()
|
||||
--
|
||||
print("Beautification complete")
|
||||
|
||||
elseif #arg == 2 then
|
||||
--keep the user from accidentally overwriting their non-minified file with
|
||||
if arg[1]:find("_formatted") then
|
||||
print("Did you mix up the argument order?\n"..
|
||||
"Current command will beautify '"..arg[1].."' and overwrite '"..arg[2].."' with the results")
|
||||
while true do
|
||||
io.write("Confirm (yes/no): ")
|
||||
local msg = io.read('*line')
|
||||
if msg == 'yes' then
|
||||
break
|
||||
elseif msg == 'no' then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
local inf = io.open(arg[1], 'r')
|
||||
if not inf then
|
||||
print("Failed to open '"..arg[1].."' for reading")
|
||||
return
|
||||
end
|
||||
--
|
||||
local sourceText = inf:read('*all')
|
||||
inf:close()
|
||||
--
|
||||
local st, ast = ParseLua(sourceText)
|
||||
if not st then
|
||||
--we failed to parse the file, show why
|
||||
print(ast)
|
||||
return
|
||||
end
|
||||
--
|
||||
if arg[1] == arg[2] then
|
||||
print("Are you SURE you want to overwrite the source file with a beautified version?\n"..
|
||||
"You will be UNABLE to get the original source back!")
|
||||
while true do
|
||||
io.write("Confirm (yes/no): ")
|
||||
local msg = io.read('*line')
|
||||
if msg == 'yes' then
|
||||
break
|
||||
elseif msg == 'no' then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
local outf = io.open(arg[2], 'w')
|
||||
if not outf then
|
||||
print("Failed to open '"..arg[2].."' for writing")
|
||||
return
|
||||
end
|
||||
--
|
||||
outf:write(Format_Beautify(ast))
|
||||
outf:close()
|
||||
--
|
||||
print("Beautification complete")
|
||||
|
||||
else
|
||||
print("Invalid arguments!\nUsage: lua CommandLineLuaBeautify.lua source_file [destination_file]")
|
||||
end
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
|
||||
--
|
||||
-- beautify.interactive
|
||||
--
|
||||
-- For testing: Lets you enter lines of text to be beautified to verify the
|
||||
-- correctness of their implementation.
|
||||
--
|
||||
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format_Beautify = require'FormatBeautiful'
|
||||
local ParseLua = Parser.ParseLua
|
||||
local PrintTable = util.PrintTable
|
||||
|
||||
while true do
|
||||
io.write('> ')
|
||||
local line = io.read('*line')
|
||||
local fileFrom, fileTo = line:match("^file (.*) (.*)")
|
||||
if fileFrom and fileTo then
|
||||
local file = io.open(fileFrom, 'r')
|
||||
local fileTo = io.open(fileTo, 'w')
|
||||
if file and fileTo then
|
||||
local st, ast = ParseLua(file:read('*all'))
|
||||
if st then
|
||||
fileTo:write(Format_Beautify(ast)..'\n')
|
||||
io.write("Beautification Complete\n")
|
||||
else
|
||||
io.write(""..tostring(ast).."\n")
|
||||
end
|
||||
file:close()
|
||||
fileTo:close()
|
||||
else
|
||||
io.write("File does not exist\n")
|
||||
end
|
||||
else
|
||||
local st, ast = ParseLua(line)
|
||||
if st then
|
||||
io.write("====== AST =======\n")
|
||||
io.write(PrintTable(ast)..'\n')
|
||||
io.write("==== BEAUTIFIED ====\n")
|
||||
io.write(Format_Beautify(ast))
|
||||
io.write("==================\n")
|
||||
else
|
||||
io.write(""..tostring(ast).."\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
|
||||
--
|
||||
-- CommandLineLiveMinify.lua
|
||||
--
|
||||
-- For testing: Lets you enter lines of text to be minified to verify the
|
||||
-- correctness of their implementation.
|
||||
--
|
||||
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format_Mini = require'FormatMini'
|
||||
local ParseLua = Parser.ParseLua
|
||||
local PrintTable = util.PrintTable
|
||||
|
||||
while true do
|
||||
io.write('> ')
|
||||
local line = io.read('*line')
|
||||
local fileFrom, fileTo = line:match("^file (.*) (.*)")
|
||||
if fileFrom and fileTo then
|
||||
local file = io.open(fileFrom, 'r')
|
||||
local fileTo = io.open(fileTo, 'w')
|
||||
if file and fileTo then
|
||||
local st, ast = ParseLua(file:read('*all'))
|
||||
if st then
|
||||
fileTo:write(Format_Mini(ast)..'\n')
|
||||
io.write("Minification Complete\n")
|
||||
else
|
||||
io.write(""..tostring(ast).."\n")
|
||||
end
|
||||
file:close()
|
||||
fileTo:close()
|
||||
else
|
||||
io.write("File does not exist\n")
|
||||
end
|
||||
else
|
||||
local st, ast = ParseLua(line)
|
||||
if st then
|
||||
io.write("====== AST =======\n")
|
||||
io.write(PrintTable(ast)..'\n')
|
||||
io.write("==== MINIFIED ====\n")
|
||||
io.write(Format_Mini(ast)..'\n')
|
||||
io.write("==================\n")
|
||||
else
|
||||
io.write(""..tostring(ast).."\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
|
||||
--
|
||||
-- CommandlineMinify.lua
|
||||
--
|
||||
-- A command line utility for minifying lua source code using the minifier.
|
||||
--
|
||||
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format_Mini = require'FormatMini'
|
||||
local ParseLua = Parser.ParseLua
|
||||
local PrintTable = util.PrintTable
|
||||
|
||||
local function splitFilename(name)
|
||||
table.foreach(arg, print)
|
||||
if name:find(".") then
|
||||
local p, ext = name:match("()%.([^%.]*)$")
|
||||
if p and ext then
|
||||
if #ext == 0 then
|
||||
return name, nil
|
||||
else
|
||||
local filename = name:sub(1,p-1)
|
||||
return filename, ext
|
||||
end
|
||||
else
|
||||
return name, nil
|
||||
end
|
||||
else
|
||||
return name, nil
|
||||
end
|
||||
end
|
||||
|
||||
if #arg == 1 then
|
||||
local name, ext = splitFilename(arg[1])
|
||||
local outname = name.."_min"
|
||||
if ext then outname = outname.."."..ext end
|
||||
--
|
||||
local inf = io.open(arg[1], 'r')
|
||||
if not inf then
|
||||
print("Failed to open `"..arg[1].."` for reading")
|
||||
return
|
||||
end
|
||||
--
|
||||
local sourceText = inf:read('*all')
|
||||
inf:close()
|
||||
--
|
||||
local st, ast = ParseLua(sourceText)
|
||||
if not st then
|
||||
--we failed to parse the file, show why
|
||||
print(ast)
|
||||
return
|
||||
end
|
||||
--
|
||||
local outf = io.open(outname, 'w')
|
||||
if not outf then
|
||||
print("Failed to open `"..outname.."` for writing")
|
||||
return
|
||||
end
|
||||
--
|
||||
outf:write(Format_Mini(ast))
|
||||
outf:close()
|
||||
--
|
||||
print("Minification complete")
|
||||
|
||||
elseif #arg == 2 then
|
||||
--keep the user from accidentally overwriting their non-minified file with
|
||||
if arg[1]:find("_min") then
|
||||
print("Did you mix up the argument order?\n"..
|
||||
"Current command will minify `"..arg[1].."` and OVERWRITE `"..arg[2].."` with the results")
|
||||
while true do
|
||||
io.write("Confirm (yes/cancel): ")
|
||||
local msg = io.read('*line')
|
||||
if msg == 'yes' then
|
||||
break
|
||||
elseif msg == 'cancel' then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
local inf = io.open(arg[1], 'r')
|
||||
if not inf then
|
||||
print("Failed to open `"..arg[1].."` for reading")
|
||||
return
|
||||
end
|
||||
--
|
||||
local sourceText = inf:read('*all')
|
||||
inf:close()
|
||||
--
|
||||
local st, ast = ParseLua(sourceText)
|
||||
if not st then
|
||||
--we failed to parse the file, show why
|
||||
print(ast)
|
||||
return
|
||||
end
|
||||
--
|
||||
if arg[1] == arg[2] then
|
||||
print("Are you SURE you want to overwrite the source file with a minified version?\n"..
|
||||
"You will be UNABLE to get the original source back!")
|
||||
while true do
|
||||
io.write("Confirm (yes/cancel): ")
|
||||
local msg = io.read('*line')
|
||||
if msg == 'yes' then
|
||||
break
|
||||
elseif msg == 'cancel' then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
local outf = io.open(arg[2], 'w')
|
||||
if not outf then
|
||||
print("Failed to open `"..arg[2].."` for writing")
|
||||
return
|
||||
end
|
||||
--
|
||||
outf:write(Format_Mini(ast))
|
||||
outf:close()
|
||||
--
|
||||
print("Minification complete")
|
||||
|
||||
else
|
||||
print("Invalid arguments, Usage:\nLuaMinify source [destination]")
|
||||
end
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
--
|
||||
-- Beautifier
|
||||
--
|
||||
-- Returns a beautified version of the code, including comments
|
||||
--
|
||||
|
||||
local parser = require"ParseLua"
|
||||
local ParseLua = parser.ParseLua
|
||||
local util = require'Util'
|
||||
local lookupify = util.lookupify
|
||||
|
||||
local LowerChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
||||
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
|
||||
local UpperChars = lookupify{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
|
||||
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
|
||||
local Digits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||||
|
||||
local function Format_Beautify(ast)
|
||||
local formatStatlist, formatExpr
|
||||
local indent = 0
|
||||
local EOL = "\n"
|
||||
|
||||
local function getIndentation()
|
||||
return string.rep(" ", indent)
|
||||
end
|
||||
|
||||
local function joinStatementsSafe(a, b, sep)
|
||||
sep = sep or ''
|
||||
local aa, bb = a:sub(-1,-1), b:sub(1,1)
|
||||
if UpperChars[aa] or LowerChars[aa] or aa == '_' then
|
||||
if not (UpperChars[bb] or LowerChars[bb] or bb == '_' or Digits[bb]) then
|
||||
--bb is a symbol, can join without sep
|
||||
return a .. b
|
||||
elseif bb == '(' then
|
||||
--prevent ambiguous syntax
|
||||
return a..sep..b
|
||||
else
|
||||
return a..sep..b
|
||||
end
|
||||
elseif Digits[aa] then
|
||||
if bb == '(' then
|
||||
--can join statements directly
|
||||
return a..b
|
||||
else
|
||||
return a..sep..b
|
||||
end
|
||||
elseif aa == '' then
|
||||
return a..b
|
||||
else
|
||||
if bb == '(' then
|
||||
--don't want to accidentally call last statement, can't join directly
|
||||
return a..sep..b
|
||||
else
|
||||
return a..b
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
formatExpr = function(expr)
|
||||
local out = string.rep('(', expr.ParenCount or 0)
|
||||
if expr.AstType == 'VarExpr' then
|
||||
if expr.Variable then
|
||||
out = out .. expr.Variable.Name
|
||||
else
|
||||
out = out .. expr.Name
|
||||
end
|
||||
|
||||
elseif expr.AstType == 'NumberExpr' then
|
||||
out = out..expr.Value.Data
|
||||
|
||||
elseif expr.AstType == 'StringExpr' then
|
||||
out = out..expr.Value.Data
|
||||
|
||||
elseif expr.AstType == 'BooleanExpr' then
|
||||
out = out..tostring(expr.Value)
|
||||
|
||||
elseif expr.AstType == 'NilExpr' then
|
||||
out = joinStatementsSafe(out, "nil")
|
||||
|
||||
elseif expr.AstType == 'BinopExpr' then
|
||||
out = joinStatementsSafe(out, formatExpr(expr.Lhs)) .. " "
|
||||
out = joinStatementsSafe(out, expr.Op) .. " "
|
||||
out = joinStatementsSafe(out, formatExpr(expr.Rhs))
|
||||
|
||||
elseif expr.AstType == 'UnopExpr' then
|
||||
out = joinStatementsSafe(out, expr.Op) .. (#expr.Op ~= 1 and " " or "")
|
||||
out = joinStatementsSafe(out, formatExpr(expr.Rhs))
|
||||
|
||||
elseif expr.AstType == 'DotsExpr' then
|
||||
out = out.."..."
|
||||
|
||||
elseif expr.AstType == 'CallExpr' then
|
||||
out = out..formatExpr(expr.Base)
|
||||
out = out.."("
|
||||
for i = 1, #expr.Arguments do
|
||||
out = out..formatExpr(expr.Arguments[i])
|
||||
if i ~= #expr.Arguments then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
out = out..")"
|
||||
|
||||
elseif expr.AstType == 'TableCallExpr' then
|
||||
out = out..formatExpr(expr.Base) .. " "
|
||||
out = out..formatExpr(expr.Arguments[1])
|
||||
|
||||
elseif expr.AstType == 'StringCallExpr' then
|
||||
out = out..formatExpr(expr.Base) .. " "
|
||||
out = out..expr.Arguments[1].Data
|
||||
|
||||
elseif expr.AstType == 'IndexExpr' then
|
||||
out = out..formatExpr(expr.Base).."["..formatExpr(expr.Index).."]"
|
||||
|
||||
elseif expr.AstType == 'MemberExpr' then
|
||||
out = out..formatExpr(expr.Base)..expr.Indexer..expr.Ident.Data
|
||||
|
||||
elseif expr.AstType == 'Function' then
|
||||
-- anonymous function
|
||||
out = out.."function("
|
||||
if #expr.Arguments > 0 then
|
||||
for i = 1, #expr.Arguments do
|
||||
out = out..expr.Arguments[i].Name
|
||||
if i ~= #expr.Arguments then
|
||||
out = out..", "
|
||||
elseif expr.VarArg then
|
||||
out = out..", ..."
|
||||
end
|
||||
end
|
||||
elseif expr.VarArg then
|
||||
out = out.."..."
|
||||
end
|
||||
out = out..")" .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(expr.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end")
|
||||
elseif expr.AstType == 'ConstructorExpr' then
|
||||
out = out.."{ "
|
||||
for i = 1, #expr.EntryList do
|
||||
local entry = expr.EntryList[i]
|
||||
if entry.Type == 'Key' then
|
||||
out = out.."["..formatExpr(entry.Key).."] = "..formatExpr(entry.Value)
|
||||
elseif entry.Type == 'Value' then
|
||||
out = out..formatExpr(entry.Value)
|
||||
elseif entry.Type == 'KeyString' then
|
||||
out = out..entry.Key.." = "..formatExpr(entry.Value)
|
||||
end
|
||||
if i ~= #expr.EntryList then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
out = out.." }"
|
||||
|
||||
elseif expr.AstType == 'Parentheses' then
|
||||
out = out.."("..formatExpr(expr.Inner)..")"
|
||||
|
||||
end
|
||||
out = out..string.rep(')', expr.ParenCount or 0)
|
||||
return out
|
||||
end
|
||||
|
||||
local formatStatement = function(statement)
|
||||
local out = ""
|
||||
if statement.AstType == 'AssignmentStatement' then
|
||||
out = getIndentation()
|
||||
for i = 1, #statement.Lhs do
|
||||
out = out..formatExpr(statement.Lhs[i])
|
||||
if i ~= #statement.Lhs then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
if #statement.Rhs > 0 then
|
||||
out = out.." = "
|
||||
for i = 1, #statement.Rhs do
|
||||
out = out..formatExpr(statement.Rhs[i])
|
||||
if i ~= #statement.Rhs then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif statement.AstType == 'CallStatement' then
|
||||
out = getIndentation() .. formatExpr(statement.Expression)
|
||||
elseif statement.AstType == 'LocalStatement' then
|
||||
out = getIndentation() .. out.."local "
|
||||
for i = 1, #statement.LocalList do
|
||||
out = out..statement.LocalList[i].Name
|
||||
if i ~= #statement.LocalList then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
if #statement.InitList > 0 then
|
||||
out = out.." = "
|
||||
for i = 1, #statement.InitList do
|
||||
out = out..formatExpr(statement.InitList[i])
|
||||
if i ~= #statement.InitList then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif statement.AstType == 'IfStatement' then
|
||||
out = getIndentation() .. joinStatementsSafe("if ", formatExpr(statement.Clauses[1].Condition))
|
||||
out = joinStatementsSafe(out, " then") .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Clauses[1].Body))
|
||||
indent = indent - 1
|
||||
for i = 2, #statement.Clauses do
|
||||
local st = statement.Clauses[i]
|
||||
if st.Condition then
|
||||
out = getIndentation() .. joinStatementsSafe(out, getIndentation() .. "elseif ")
|
||||
out = joinStatementsSafe(out, formatExpr(st.Condition))
|
||||
out = joinStatementsSafe(out, " then") .. EOL
|
||||
else
|
||||
out = joinStatementsSafe(out, getIndentation() .. "else") .. EOL
|
||||
end
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(st.Body))
|
||||
indent = indent - 1
|
||||
end
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end") .. EOL
|
||||
elseif statement.AstType == 'WhileStatement' then
|
||||
out = getIndentation() .. joinStatementsSafe("while ", formatExpr(statement.Condition))
|
||||
out = joinStatementsSafe(out, " do") .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end") .. EOL
|
||||
elseif statement.AstType == 'DoStatement' then
|
||||
out = getIndentation() .. joinStatementsSafe(out, "do") .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end") .. EOL
|
||||
elseif statement.AstType == 'ReturnStatement' then
|
||||
out = getIndentation() .. "return "
|
||||
for i = 1, #statement.Arguments do
|
||||
out = joinStatementsSafe(out, formatExpr(statement.Arguments[i]))
|
||||
if i ~= #statement.Arguments then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
elseif statement.AstType == 'BreakStatement' then
|
||||
out = getIndentation() .. "break"
|
||||
elseif statement.AstType == 'RepeatStatement' then
|
||||
out = getIndentation() .. "repeat" .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "until ")
|
||||
out = joinStatementsSafe(out, formatExpr(statement.Condition)) .. EOL
|
||||
elseif statement.AstType == 'Function' then
|
||||
if statement.IsLocal then
|
||||
out = "local "
|
||||
end
|
||||
out = joinStatementsSafe(out, "function ")
|
||||
out = getIndentation() .. out
|
||||
if statement.IsLocal then
|
||||
out = out..statement.Name.Name
|
||||
else
|
||||
out = out..formatExpr(statement.Name)
|
||||
end
|
||||
out = out.."("
|
||||
if #statement.Arguments > 0 then
|
||||
for i = 1, #statement.Arguments do
|
||||
out = out..statement.Arguments[i].Name
|
||||
if i ~= #statement.Arguments then
|
||||
out = out..", "
|
||||
elseif statement.VarArg then
|
||||
out = out..",..."
|
||||
end
|
||||
end
|
||||
elseif statement.VarArg then
|
||||
out = out.."..."
|
||||
end
|
||||
out = out..")" .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end") .. EOL
|
||||
elseif statement.AstType == 'GenericForStatement' then
|
||||
out = getIndentation() .. "for "
|
||||
for i = 1, #statement.VariableList do
|
||||
out = out..statement.VariableList[i].Name
|
||||
if i ~= #statement.VariableList then
|
||||
out = out..", "
|
||||
end
|
||||
end
|
||||
out = out.." in "
|
||||
for i = 1, #statement.Generators do
|
||||
out = joinStatementsSafe(out, formatExpr(statement.Generators[i]))
|
||||
if i ~= #statement.Generators then
|
||||
out = joinStatementsSafe(out, ', ')
|
||||
end
|
||||
end
|
||||
out = joinStatementsSafe(out, " do") .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end") .. EOL
|
||||
elseif statement.AstType == 'NumericForStatement' then
|
||||
out = getIndentation() .. "for "
|
||||
out = out..statement.Variable.Name.." = "
|
||||
out = out..formatExpr(statement.Start)..", "..formatExpr(statement.End)
|
||||
if statement.Step then
|
||||
out = out..", "..formatExpr(statement.Step)
|
||||
end
|
||||
out = joinStatementsSafe(out, " do") .. EOL
|
||||
indent = indent + 1
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
indent = indent - 1
|
||||
out = joinStatementsSafe(out, getIndentation() .. "end") .. EOL
|
||||
elseif statement.AstType == 'LabelStatement' then
|
||||
out = getIndentation() .. "::" .. statement.Label .. "::" .. EOL
|
||||
elseif statement.AstType == 'GotoStatement' then
|
||||
out = getIndentation() .. "goto " .. statement.Label .. EOL
|
||||
elseif statement.AstType == 'Comment' then
|
||||
if statement.CommentType == 'Shebang' then
|
||||
out = getIndentation() .. statement.Data
|
||||
--out = out .. EOL
|
||||
elseif statement.CommentType == 'Comment' then
|
||||
out = getIndentation() .. statement.Data
|
||||
--out = out .. EOL
|
||||
elseif statement.CommentType == 'LongComment' then
|
||||
out = getIndentation() .. statement.Data
|
||||
--out = out .. EOL
|
||||
end
|
||||
elseif statement.AstType == 'Eof' then
|
||||
-- Ignore
|
||||
else
|
||||
print("Unknown AST Type: ", statement.AstType)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
formatStatlist = function(statList)
|
||||
local out = ''
|
||||
for _, stat in pairs(statList.Body) do
|
||||
out = joinStatementsSafe(out, formatStatement(stat) .. EOL)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
return formatStatlist(ast)
|
||||
end
|
||||
|
||||
return Format_Beautify
|
||||
|
|
@ -1,440 +0,0 @@
|
|||
require'strict'
|
||||
require'ParseLua'
|
||||
local util = require'Util'
|
||||
|
||||
local function debug_printf(...)
|
||||
--[[
|
||||
util.printf(...)
|
||||
--]]
|
||||
end
|
||||
|
||||
--
|
||||
-- FormatIdentity.lua
|
||||
--
|
||||
-- Returns the exact source code that was used to create an AST, preserving all
|
||||
-- comments and whitespace.
|
||||
-- This can be used to get back a Lua source after renaming some variables in
|
||||
-- an AST.
|
||||
--
|
||||
|
||||
local function Format_Identity(ast)
|
||||
local out = {
|
||||
rope = {}, -- List of strings
|
||||
line = 1,
|
||||
char = 1,
|
||||
|
||||
appendStr = function(self, str)
|
||||
table.insert(self.rope, str)
|
||||
|
||||
local lines = util.splitLines(str)
|
||||
if #lines == 1 then
|
||||
self.char = self.char + #str
|
||||
else
|
||||
self.line = self.line + #lines - 1
|
||||
local lastLine = lines[#lines]
|
||||
self.char = #lastLine
|
||||
end
|
||||
end,
|
||||
|
||||
appendToken = function(self, token)
|
||||
self:appendWhite(token)
|
||||
--[*[
|
||||
--debug_printf("appendToken(%q)", token.Data)
|
||||
local data = token.Data
|
||||
local lines = util.splitLines(data)
|
||||
while self.line + #lines < token.Line do
|
||||
print("Inserting extra line")
|
||||
self.str = self.str .. '\n'
|
||||
self.line = self.line + 1
|
||||
self.char = 1
|
||||
end
|
||||
--]]
|
||||
self:appendStr(token.Data)
|
||||
end,
|
||||
|
||||
appendTokens = function(self, tokens)
|
||||
for _,token in ipairs(tokens) do
|
||||
self:appendToken( token )
|
||||
end
|
||||
end,
|
||||
|
||||
appendWhite = function(self, token)
|
||||
if token.LeadingWhite then
|
||||
self:appendTokens( token.LeadingWhite )
|
||||
--self.str = self.str .. ' '
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local formatStatlist, formatExpr;
|
||||
|
||||
formatExpr = function(expr)
|
||||
local tok_it = 1
|
||||
local function appendNextToken(str)
|
||||
local tok = expr.Tokens[tok_it];
|
||||
if str and tok.Data ~= str then
|
||||
error("Expected token '" .. str .. "'. Tokens: " .. util.PrintTable(expr.Tokens))
|
||||
end
|
||||
out:appendToken( tok )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendToken(token)
|
||||
out:appendToken( token )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendWhite()
|
||||
local tok = expr.Tokens[tok_it];
|
||||
if not tok then error(util.PrintTable(expr)) end
|
||||
out:appendWhite( tok )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendStr(str)
|
||||
appendWhite()
|
||||
out:appendStr(str)
|
||||
end
|
||||
local function peek()
|
||||
if tok_it < #expr.Tokens then
|
||||
return expr.Tokens[tok_it].Data
|
||||
end
|
||||
end
|
||||
local function appendComma(mandatory, seperators)
|
||||
if true then
|
||||
seperators = seperators or { "," }
|
||||
seperators = util.lookupify( seperators )
|
||||
if not mandatory and not seperators[peek()] then
|
||||
return
|
||||
end
|
||||
assert(seperators[peek()], "Missing comma or semicolon")
|
||||
appendNextToken()
|
||||
else
|
||||
local p = peek()
|
||||
if p == "," or p == ";" then
|
||||
appendNextToken()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
debug_printf("formatExpr(%s) at line %i", expr.AstType, expr.Tokens[1] and expr.Tokens[1].Line or -1)
|
||||
|
||||
if expr.AstType == 'VarExpr' then
|
||||
if expr.Variable then
|
||||
appendStr( expr.Variable.Name )
|
||||
else
|
||||
appendStr( expr.Name )
|
||||
end
|
||||
|
||||
elseif expr.AstType == 'NumberExpr' then
|
||||
appendToken( expr.Value )
|
||||
|
||||
elseif expr.AstType == 'StringExpr' then
|
||||
appendToken( expr.Value )
|
||||
|
||||
elseif expr.AstType == 'BooleanExpr' then
|
||||
appendNextToken( expr.Value and "true" or "false" )
|
||||
|
||||
elseif expr.AstType == 'NilExpr' then
|
||||
appendNextToken( "nil" )
|
||||
|
||||
elseif expr.AstType == 'BinopExpr' then
|
||||
formatExpr(expr.Lhs)
|
||||
appendStr( expr.Op )
|
||||
formatExpr(expr.Rhs)
|
||||
|
||||
elseif expr.AstType == 'UnopExpr' then
|
||||
appendStr( expr.Op )
|
||||
formatExpr(expr.Rhs)
|
||||
|
||||
elseif expr.AstType == 'DotsExpr' then
|
||||
appendNextToken( "..." )
|
||||
|
||||
elseif expr.AstType == 'CallExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendNextToken( "(" )
|
||||
for i,arg in ipairs( expr.Arguments ) do
|
||||
formatExpr(arg)
|
||||
appendComma( i ~= #expr.Arguments )
|
||||
end
|
||||
appendNextToken( ")" )
|
||||
|
||||
elseif expr.AstType == 'TableCallExpr' then
|
||||
formatExpr( expr.Base )
|
||||
formatExpr( expr.Arguments[1] )
|
||||
|
||||
elseif expr.AstType == 'StringCallExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendToken( expr.Arguments[1] )
|
||||
|
||||
elseif expr.AstType == 'IndexExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendNextToken( "[" )
|
||||
formatExpr(expr.Index)
|
||||
appendNextToken( "]" )
|
||||
|
||||
elseif expr.AstType == 'MemberExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendNextToken() -- . or :
|
||||
appendToken(expr.Ident)
|
||||
|
||||
elseif expr.AstType == 'Function' then
|
||||
-- anonymous function
|
||||
appendNextToken( "function" )
|
||||
appendNextToken( "(" )
|
||||
if #expr.Arguments > 0 then
|
||||
for i = 1, #expr.Arguments do
|
||||
appendStr( expr.Arguments[i].Name )
|
||||
if i ~= #expr.Arguments then
|
||||
appendNextToken(",")
|
||||
elseif expr.VarArg then
|
||||
appendNextToken(",")
|
||||
appendNextToken("...")
|
||||
end
|
||||
end
|
||||
elseif expr.VarArg then
|
||||
appendNextToken("...")
|
||||
end
|
||||
appendNextToken(")")
|
||||
formatStatlist(expr.Body)
|
||||
appendNextToken("end")
|
||||
|
||||
elseif expr.AstType == 'ConstructorExpr' then
|
||||
appendNextToken( "{" )
|
||||
for i = 1, #expr.EntryList do
|
||||
local entry = expr.EntryList[i]
|
||||
if entry.Type == 'Key' then
|
||||
appendNextToken( "[" )
|
||||
formatExpr(entry.Key)
|
||||
appendNextToken( "]" )
|
||||
appendNextToken( "=" )
|
||||
formatExpr(entry.Value)
|
||||
elseif entry.Type == 'Value' then
|
||||
formatExpr(entry.Value)
|
||||
elseif entry.Type == 'KeyString' then
|
||||
appendStr(entry.Key)
|
||||
appendNextToken( "=" )
|
||||
formatExpr(entry.Value)
|
||||
end
|
||||
appendComma( i ~= #expr.EntryList, { ",", ";" } )
|
||||
end
|
||||
appendNextToken( "}" )
|
||||
|
||||
elseif expr.AstType == 'Parentheses' then
|
||||
appendNextToken( "(" )
|
||||
formatExpr(expr.Inner)
|
||||
appendNextToken( ")" )
|
||||
|
||||
else
|
||||
print("Unknown AST Type: ", statement.AstType)
|
||||
end
|
||||
|
||||
assert(tok_it == #expr.Tokens + 1)
|
||||
debug_printf("/formatExpr")
|
||||
end
|
||||
|
||||
|
||||
local formatStatement = function(statement)
|
||||
local tok_it = 1
|
||||
local function appendNextToken(str)
|
||||
local tok = statement.Tokens[tok_it];
|
||||
assert(tok, string.format("Not enough tokens for %q. First token at %i:%i",
|
||||
str, statement.Tokens[1].Line, statement.Tokens[1].Char))
|
||||
assert(tok.Data == str,
|
||||
string.format('Expected token %q, got %q', str, tok.Data))
|
||||
out:appendToken( tok )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendToken(token)
|
||||
out:appendToken( str )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendWhite()
|
||||
local tok = statement.Tokens[tok_it];
|
||||
out:appendWhite( tok )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendStr(str)
|
||||
appendWhite()
|
||||
out:appendStr(str)
|
||||
end
|
||||
local function appendComma(mandatory)
|
||||
if mandatory
|
||||
or (tok_it < #statement.Tokens and statement.Tokens[tok_it].Data == ",") then
|
||||
appendNextToken( "," )
|
||||
end
|
||||
end
|
||||
|
||||
debug_printf("")
|
||||
debug_printf(string.format("formatStatement(%s) at line %i", statement.AstType, statement.Tokens[1] and statement.Tokens[1].Line or -1))
|
||||
|
||||
if statement.AstType == 'AssignmentStatement' then
|
||||
for i,v in ipairs(statement.Lhs) do
|
||||
formatExpr(v)
|
||||
appendComma( i ~= #statement.Lhs )
|
||||
end
|
||||
if #statement.Rhs > 0 then
|
||||
appendNextToken( "=" )
|
||||
for i,v in ipairs(statement.Rhs) do
|
||||
formatExpr(v)
|
||||
appendComma( i ~= #statement.Rhs )
|
||||
end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'CallStatement' then
|
||||
formatExpr(statement.Expression)
|
||||
|
||||
elseif statement.AstType == 'LocalStatement' then
|
||||
appendNextToken( "local" )
|
||||
for i = 1, #statement.LocalList do
|
||||
appendStr( statement.LocalList[i].Name )
|
||||
appendComma( i ~= #statement.LocalList )
|
||||
end
|
||||
if #statement.InitList > 0 then
|
||||
appendNextToken( "=" )
|
||||
for i = 1, #statement.InitList do
|
||||
formatExpr(statement.InitList[i])
|
||||
appendComma( i ~= #statement.InitList )
|
||||
end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'IfStatement' then
|
||||
appendNextToken( "if" )
|
||||
formatExpr( statement.Clauses[1].Condition )
|
||||
appendNextToken( "then" )
|
||||
formatStatlist( statement.Clauses[1].Body )
|
||||
for i = 2, #statement.Clauses do
|
||||
local st = statement.Clauses[i]
|
||||
if st.Condition then
|
||||
appendNextToken( "elseif" )
|
||||
formatExpr(st.Condition)
|
||||
appendNextToken( "then" )
|
||||
else
|
||||
appendNextToken( "else" )
|
||||
end
|
||||
formatStatlist(st.Body)
|
||||
end
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'WhileStatement' then
|
||||
appendNextToken( "while" )
|
||||
formatExpr(statement.Condition)
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'DoStatement' then
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'ReturnStatement' then
|
||||
appendNextToken( "return" )
|
||||
for i = 1, #statement.Arguments do
|
||||
formatExpr(statement.Arguments[i])
|
||||
appendComma( i ~= #statement.Arguments )
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'BreakStatement' then
|
||||
appendNextToken( "break" )
|
||||
|
||||
elseif statement.AstType == 'RepeatStatement' then
|
||||
appendNextToken( "repeat" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "until" )
|
||||
formatExpr(statement.Condition)
|
||||
|
||||
elseif statement.AstType == 'Function' then
|
||||
--print(util.PrintTable(statement))
|
||||
|
||||
if statement.IsLocal then
|
||||
appendNextToken( "local" )
|
||||
end
|
||||
appendNextToken( "function" )
|
||||
|
||||
if statement.IsLocal then
|
||||
appendStr(statement.Name.Name)
|
||||
else
|
||||
formatExpr(statement.Name)
|
||||
end
|
||||
|
||||
appendNextToken( "(" )
|
||||
if #statement.Arguments > 0 then
|
||||
for i = 1, #statement.Arguments do
|
||||
appendStr( statement.Arguments[i].Name )
|
||||
appendComma( i ~= #statement.Arguments or statement.VarArg )
|
||||
if i == #statement.Arguments and statement.VarArg then
|
||||
appendNextToken( "..." )
|
||||
end
|
||||
end
|
||||
elseif statement.VarArg then
|
||||
appendNextToken( "..." )
|
||||
end
|
||||
appendNextToken( ")" )
|
||||
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'GenericForStatement' then
|
||||
appendNextToken( "for" )
|
||||
for i = 1, #statement.VariableList do
|
||||
appendStr( statement.VariableList[i].Name )
|
||||
appendComma( i ~= #statement.VariableList )
|
||||
end
|
||||
appendNextToken( "in" )
|
||||
for i = 1, #statement.Generators do
|
||||
formatExpr(statement.Generators[i])
|
||||
appendComma( i ~= #statement.Generators )
|
||||
end
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'NumericForStatement' then
|
||||
appendNextToken( "for" )
|
||||
appendStr( statement.Variable.Name )
|
||||
appendNextToken( "=" )
|
||||
formatExpr(statement.Start)
|
||||
appendNextToken( "," )
|
||||
formatExpr(statement.End)
|
||||
if statement.Step then
|
||||
appendNextToken( "," )
|
||||
formatExpr(statement.Step)
|
||||
end
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'LabelStatement' then
|
||||
appendNextToken( "::" )
|
||||
appendStr( statement.Label )
|
||||
appendNextToken( "::" )
|
||||
|
||||
elseif statement.AstType == 'GotoStatement' then
|
||||
appendNextToken( "goto" )
|
||||
appendStr( statement.Label )
|
||||
|
||||
elseif statement.AstType == 'Eof' then
|
||||
appendWhite()
|
||||
|
||||
else
|
||||
print("Unknown AST Type: ", statement.AstType)
|
||||
end
|
||||
|
||||
if statement.Semicolon then
|
||||
appendNextToken(";")
|
||||
end
|
||||
|
||||
assert(tok_it == #statement.Tokens + 1)
|
||||
debug_printf("/formatStatment")
|
||||
end
|
||||
|
||||
formatStatlist = function(statList)
|
||||
for _, stat in ipairs(statList.Body) do
|
||||
formatStatement(stat)
|
||||
end
|
||||
end
|
||||
|
||||
formatStatlist(ast)
|
||||
|
||||
return true, table.concat(out.rope)
|
||||
end
|
||||
|
||||
return Format_Identity
|
||||
|
|
@ -1,601 +0,0 @@
|
|||
--
|
||||
-- CANDRAN
|
||||
-- Based on the FormatIdentity.lua of LuaMinify.
|
||||
-- Modified by Thomas99 to format valid Lua code from Candran AST.
|
||||
--
|
||||
-- Modified parts are marked with "-- CANDRAN" comments.
|
||||
--
|
||||
|
||||
--[[
|
||||
This file is part of LuaMinify by stravant (https://github.com/stravant/LuaMinify).
|
||||
|
||||
LICENSE :
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2013
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
--require'strict' -- CANDRAN : comment, useless here
|
||||
|
||||
-- CANDRAN : add Candran syntaxic additions
|
||||
local candran = require("candran").syntax
|
||||
|
||||
require'lib.LuaMinify.ParseCandran'
|
||||
local util = require'lib.LuaMinify.Util'
|
||||
|
||||
local function debug_printf(...)
|
||||
--[[
|
||||
util.printf(...)
|
||||
--]]
|
||||
end
|
||||
|
||||
--
|
||||
-- FormatIdentity.lua
|
||||
--
|
||||
-- Returns the exact source code that was used to create an AST, preserving all
|
||||
-- comments and whitespace.
|
||||
-- This can be used to get back a Lua source after renaming some variables in
|
||||
-- an AST.
|
||||
--
|
||||
|
||||
local function Format_Identity(ast)
|
||||
local out = {
|
||||
rope = {}, -- List of strings
|
||||
line = 1,
|
||||
char = 1,
|
||||
|
||||
appendStr = function(self, str)
|
||||
table.insert(self.rope, str)
|
||||
|
||||
local lines = util.splitLines(str)
|
||||
if #lines == 1 then
|
||||
self.char = self.char + #str
|
||||
else
|
||||
self.line = self.line + #lines - 1
|
||||
local lastLine = lines[#lines]
|
||||
self.char = #lastLine
|
||||
end
|
||||
end,
|
||||
|
||||
-- CANDRAN : options
|
||||
appendToken = function(self, token, options)
|
||||
local options = options or {} -- CANDRAN
|
||||
self:appendWhite(token, options)
|
||||
--[*[
|
||||
--debug_printf("appendToken(%q)", token.Data)
|
||||
local data = token.Data
|
||||
local lines = util.splitLines(data)
|
||||
while self.line + #lines < token.Line do
|
||||
if not options.no_newline then self:appendStr('\n') end -- CANDRAN : options
|
||||
self.line = self.line + 1
|
||||
self.char = 1
|
||||
end
|
||||
--]]
|
||||
if options.no_newline then data = data:gsub("[\n\r]*", "") end -- CANDRAN : options
|
||||
if options.no_leading_white then data = data:gsub("^%s+", "") end
|
||||
self:appendStr(data)
|
||||
end,
|
||||
|
||||
-- CANDRAN : options
|
||||
appendTokens = function(self, tokens, options)
|
||||
for _,token in ipairs(tokens) do
|
||||
self:appendToken( token, options ) -- CANDRAN : options
|
||||
end
|
||||
end,
|
||||
|
||||
-- CANDRAN : options
|
||||
appendWhite = function(self, token, options)
|
||||
if token.LeadingWhite then
|
||||
self:appendTokens( token.LeadingWhite, options ) -- CANDRAN : options
|
||||
--self.str = self.str .. ' '
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local formatStatlist, formatExpr, formatStatement;
|
||||
|
||||
-- CANDRAN : added options argument
|
||||
-- CANDRAN : options = { no_newline = false, no_leading_white = false }
|
||||
formatExpr = function(expr, options)
|
||||
local options = options or {} -- CANDRAN
|
||||
local tok_it = 1
|
||||
local function appendNextToken(str)
|
||||
local tok = expr.Tokens[tok_it];
|
||||
if str and tok.Data ~= str then
|
||||
error("Expected token '" .. str .. "'. Tokens: " .. util.PrintTable(expr.Tokens))
|
||||
end
|
||||
out:appendToken( tok, options ) -- CANDRAN : options
|
||||
tok_it = tok_it + 1
|
||||
options.no_leading_white = false -- CANDRAN : not the leading token anymore
|
||||
end
|
||||
local function appendToken(token)
|
||||
out:appendToken( token, options ) -- CANDRAN : options
|
||||
tok_it = tok_it + 1
|
||||
options.no_leading_white = false -- CANDRAN : not the leading token anymore
|
||||
end
|
||||
local function appendWhite()
|
||||
local tok = expr.Tokens[tok_it];
|
||||
if not tok then error(util.PrintTable(expr)) end
|
||||
out:appendWhite( tok, options ) -- CANDRAN : options
|
||||
tok_it = tok_it + 1
|
||||
options.no_leading_white = false -- CANDRAN : not the leading token anymore
|
||||
end
|
||||
local function appendStr(str)
|
||||
appendWhite()
|
||||
out:appendStr(str)
|
||||
end
|
||||
local function peek()
|
||||
if tok_it < #expr.Tokens then
|
||||
return expr.Tokens[tok_it].Data
|
||||
end
|
||||
end
|
||||
local function appendComma(mandatory, seperators)
|
||||
if true then
|
||||
seperators = seperators or { "," }
|
||||
seperators = util.lookupify( seperators )
|
||||
if not mandatory and not seperators[peek()] then
|
||||
return
|
||||
end
|
||||
assert(seperators[peek()], "Missing comma or semicolon")
|
||||
appendNextToken()
|
||||
else
|
||||
local p = peek()
|
||||
if p == "," or p == ";" then
|
||||
appendNextToken()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
debug_printf("formatExpr(%s) at line %i", expr.AstType, expr.Tokens[1] and expr.Tokens[1].Line or -1)
|
||||
|
||||
if expr.AstType == 'VarExpr' then
|
||||
if expr.Variable then
|
||||
appendStr( expr.Variable.Name )
|
||||
else
|
||||
appendStr( expr.Name )
|
||||
end
|
||||
|
||||
elseif expr.AstType == 'NumberExpr' then
|
||||
appendToken( expr.Value )
|
||||
|
||||
elseif expr.AstType == 'StringExpr' then
|
||||
appendToken( expr.Value )
|
||||
|
||||
elseif expr.AstType == 'BooleanExpr' then
|
||||
appendNextToken( expr.Value and "true" or "false" )
|
||||
|
||||
elseif expr.AstType == 'NilExpr' then
|
||||
appendNextToken( "nil" )
|
||||
|
||||
elseif expr.AstType == 'BinopExpr' then
|
||||
formatExpr(expr.Lhs)
|
||||
appendStr( expr.Op )
|
||||
formatExpr(expr.Rhs)
|
||||
|
||||
elseif expr.AstType == 'UnopExpr' then
|
||||
appendStr( expr.Op )
|
||||
formatExpr(expr.Rhs)
|
||||
|
||||
elseif expr.AstType == 'DotsExpr' then
|
||||
appendNextToken( "..." )
|
||||
|
||||
elseif expr.AstType == 'CallExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendNextToken( "(" )
|
||||
for i,arg in ipairs( expr.Arguments ) do
|
||||
formatExpr(arg)
|
||||
appendComma( i ~= #expr.Arguments )
|
||||
end
|
||||
appendNextToken( ")" )
|
||||
|
||||
elseif expr.AstType == 'TableCallExpr' then
|
||||
formatExpr( expr.Base )
|
||||
formatExpr( expr.Arguments[1] )
|
||||
|
||||
elseif expr.AstType == 'StringCallExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendToken( expr.Arguments[1] )
|
||||
|
||||
elseif expr.AstType == 'IndexExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendNextToken( "[" )
|
||||
formatExpr(expr.Index)
|
||||
appendNextToken( "]" )
|
||||
|
||||
elseif expr.AstType == 'MemberExpr' then
|
||||
formatExpr(expr.Base)
|
||||
appendNextToken() -- . or :
|
||||
appendToken(expr.Ident)
|
||||
|
||||
elseif expr.AstType == 'Function' then
|
||||
-- anonymous function
|
||||
appendNextToken( "function" )
|
||||
appendNextToken( "(" )
|
||||
if #expr.Arguments > 0 then
|
||||
for i = 1, #expr.Arguments do
|
||||
appendStr( expr.Arguments[i].Name )
|
||||
if i ~= #expr.Arguments then
|
||||
appendNextToken(",")
|
||||
elseif expr.VarArg then
|
||||
appendNextToken(",")
|
||||
appendNextToken("...")
|
||||
end
|
||||
end
|
||||
elseif expr.VarArg then
|
||||
appendNextToken("...")
|
||||
end
|
||||
appendNextToken(")")
|
||||
formatStatlist(expr.Body)
|
||||
appendNextToken("end")
|
||||
|
||||
elseif expr.AstType == 'ConstructorExpr' then
|
||||
-- CANDRAN : function to get a value with its applied decorators
|
||||
local function appendValue(entry)
|
||||
out:appendStr(" ")
|
||||
if entry.Decorated then
|
||||
for _,d in ipairs(entry.DecoratorChain) do
|
||||
formatExpr(d)
|
||||
out:appendStr("(")
|
||||
end
|
||||
end
|
||||
formatExpr(entry.Value, { no_leading_white = true })
|
||||
if entry.Decorated then
|
||||
for _ in ipairs(entry.DecoratorChain) do
|
||||
out:appendStr(")")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
appendNextToken( "{" )
|
||||
for i = 1, #expr.EntryList do
|
||||
local entry = expr.EntryList[i]
|
||||
if entry.Type == 'Key' then
|
||||
appendNextToken( "[" )
|
||||
formatExpr(entry.Key)
|
||||
appendNextToken( "]" )
|
||||
appendNextToken( "=" )
|
||||
appendValue(entry) -- CANDRAN : respect decorators
|
||||
elseif entry.Type == 'Value' then
|
||||
appendValue(entry) -- CANDRAN : respect decorators
|
||||
elseif entry.Type == 'KeyString' then
|
||||
appendStr(entry.Key)
|
||||
appendNextToken( "=" )
|
||||
appendValue(entry) -- CANDRAN : respect decorators
|
||||
end
|
||||
appendComma( i ~= #expr.EntryList, { ",", ";" } )
|
||||
end
|
||||
appendNextToken( "}" )
|
||||
|
||||
elseif expr.AstType == 'Parentheses' then
|
||||
appendNextToken( "(" )
|
||||
formatExpr(expr.Inner)
|
||||
appendNextToken( ")" )
|
||||
|
||||
else
|
||||
print("Unknown AST Type: ", statement.AstType)
|
||||
end
|
||||
|
||||
assert(tok_it == #expr.Tokens + 1)
|
||||
debug_printf("/formatExpr")
|
||||
end
|
||||
|
||||
formatStatement = function(statement)
|
||||
local tok_it = 1
|
||||
local function appendNextToken(str)
|
||||
local tok = statement.Tokens[tok_it];
|
||||
assert(tok, string.format("Not enough tokens for %q. First token at %i:%i",
|
||||
str, statement.Tokens[1].Line, statement.Tokens[1].Char))
|
||||
assert(tok.Data == str,
|
||||
string.format('Expected token %q, got %q', str, tok.Data))
|
||||
out:appendToken( tok )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendToken(token)
|
||||
out:appendToken( str )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendWhite()
|
||||
local tok = statement.Tokens[tok_it];
|
||||
out:appendWhite( tok )
|
||||
tok_it = tok_it + 1
|
||||
end
|
||||
local function appendStr(str)
|
||||
appendWhite()
|
||||
out:appendStr(str)
|
||||
end
|
||||
local function appendComma(mandatory)
|
||||
if mandatory
|
||||
or (tok_it < #statement.Tokens and statement.Tokens[tok_it].Data == ",") then
|
||||
appendNextToken( "," )
|
||||
end
|
||||
end
|
||||
|
||||
debug_printf("")
|
||||
debug_printf(string.format("formatStatement(%s) at line %i", statement.AstType, statement.Tokens[1] and statement.Tokens[1].Line or -1))
|
||||
|
||||
if statement.AstType == 'AssignmentStatement' then
|
||||
local newlineToCheck -- CANDRAN : position of a potential newline to eliminate in some edge cases
|
||||
|
||||
for i,v in ipairs(statement.Lhs) do
|
||||
formatExpr(v)
|
||||
appendComma( i ~= #statement.Lhs )
|
||||
end
|
||||
if #statement.Rhs > 0 then
|
||||
-- CANDRAN : get the assignment operator used (default to =)
|
||||
local assignmentToken = "="
|
||||
local candranAssignmentExists = util.lookupify(candran.assignment)
|
||||
for i,v in pairs(statement.Tokens) do
|
||||
if candranAssignmentExists[v.Data] then
|
||||
assignmentToken = v.Data
|
||||
break
|
||||
end
|
||||
end
|
||||
appendNextToken(assignmentToken) -- CANDRAN : accept Candran assignments operators
|
||||
--appendNextToken( "=" )
|
||||
newlineToCheck = #out.rope + 1 -- CANDRAN : the potential newline position afte the =
|
||||
|
||||
if assignmentToken == "=" then
|
||||
for i,v in ipairs(statement.Rhs) do
|
||||
formatExpr(v)
|
||||
appendComma( i ~= #statement.Rhs )
|
||||
end
|
||||
else
|
||||
out.rope[#out.rope] = "= " -- CANDRAN : remplace +=, -=, etc. with =
|
||||
for i,v in ipairs(statement.Rhs) do
|
||||
if i <= #statement.Lhs then -- CANDRAN : impossible to assign more variables than indicated in Lhs
|
||||
formatExpr(statement.Lhs[i], { no_newline = true }) -- CANDRAN : write variable to assign
|
||||
out:appendStr(" "..assignmentToken:gsub("=$","")) -- CANDRAN : assignment operation
|
||||
formatExpr(v) -- CANDRAN : write variable to add/sub/etc.
|
||||
if i ~= #statement.Rhs then -- CANDRAN : add comma to allow multi-assignment
|
||||
appendComma( i ~= #statement.Rhs )
|
||||
if i >= #statement.Lhs then
|
||||
out.rope[#out.rope] = "" -- CANDRAN : if this was the last element, remove the comma
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- CANDRAN : eliminate the bad newlines
|
||||
if out.rope[newlineToCheck] == "\n" then
|
||||
out.rope[newlineToCheck] = ""
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'CallStatement' then
|
||||
formatExpr(statement.Expression)
|
||||
|
||||
elseif statement.AstType == 'LocalStatement' then
|
||||
appendNextToken( "local" )
|
||||
for i = 1, #statement.LocalList do
|
||||
appendStr( statement.LocalList[i].Name )
|
||||
appendComma( i ~= #statement.LocalList )
|
||||
end
|
||||
if #statement.InitList > 0 then
|
||||
appendNextToken( "=" )
|
||||
for i = 1, #statement.InitList do
|
||||
formatExpr(statement.InitList[i])
|
||||
appendComma( i ~= #statement.InitList )
|
||||
end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'IfStatement' then
|
||||
appendNextToken( "if" )
|
||||
formatExpr( statement.Clauses[1].Condition )
|
||||
appendNextToken( "then" )
|
||||
formatStatlist( statement.Clauses[1].Body )
|
||||
for i = 2, #statement.Clauses do
|
||||
local st = statement.Clauses[i]
|
||||
if st.Condition then
|
||||
appendNextToken( "elseif" )
|
||||
formatExpr(st.Condition)
|
||||
appendNextToken( "then" )
|
||||
else
|
||||
appendNextToken( "else" )
|
||||
end
|
||||
formatStatlist(st.Body)
|
||||
end
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'WhileStatement' then
|
||||
appendNextToken( "while" )
|
||||
formatExpr(statement.Condition)
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'DoStatement' then
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'ReturnStatement' then
|
||||
appendNextToken( "return" )
|
||||
for i = 1, #statement.Arguments do
|
||||
formatExpr(statement.Arguments[i])
|
||||
appendComma( i ~= #statement.Arguments )
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'BreakStatement' then
|
||||
appendNextToken( "break" )
|
||||
|
||||
elseif statement.AstType == 'RepeatStatement' then
|
||||
appendNextToken( "repeat" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "until" )
|
||||
formatExpr(statement.Condition)
|
||||
|
||||
-- CANDRAN : add decorator support (@)
|
||||
elseif statement.AstType == 'DecoratedStatement' then
|
||||
-- CANDRAN : list of the chained decorators
|
||||
local decoratorChain = {statement}
|
||||
|
||||
-- CANDRAN : get the decorated statement
|
||||
local decorated = statement.Decorated
|
||||
while decorated.AstType == "DecoratedStatement" do
|
||||
table.insert(decoratorChain, decorated)
|
||||
decorated = decorated.Decorated
|
||||
end
|
||||
|
||||
-- CANDRAN : write the decorated statement like a normal statement
|
||||
formatStatement(decorated)
|
||||
|
||||
-- CANDRAN : mark the decorator token as used (and add whitespace)
|
||||
appendNextToken(candran.decorator)
|
||||
out.rope[#out.rope] = ""
|
||||
|
||||
-- CANDRAN : get the variable(s) to decorate name(s)
|
||||
local names = {}
|
||||
if decorated.AstType == "Function" then
|
||||
table.insert(names, decorated.Name.Name)
|
||||
elseif decorated.AstType == "AssignmentStatement" then
|
||||
for _,var in ipairs(decorated.Lhs) do
|
||||
table.insert(names, var.Name)
|
||||
end
|
||||
elseif decorated.AstType == "LocalStatement" then
|
||||
for _,var in ipairs(decorated.LocalList) do
|
||||
table.insert(names, var.Name)
|
||||
end
|
||||
else
|
||||
error("Invalid statement type to decorate : "..decorated.AstType)
|
||||
end
|
||||
|
||||
-- CANDRAN : redefine the variable(s) ( name, name2, ... = ... )
|
||||
for i,name in ipairs(names) do
|
||||
out:appendStr(name)
|
||||
if i ~= #names then out:appendStr(", ") end
|
||||
end
|
||||
out:appendStr(" = ")
|
||||
|
||||
for i,name in ipairs(names) do
|
||||
-- CANDRAN : write the decorator chain ( a(b(c(... )
|
||||
for _,v in pairs(decoratorChain) do
|
||||
formatExpr(v.Decorator)
|
||||
out:appendStr("(")
|
||||
end
|
||||
|
||||
-- CANDRAN : pass the undecorated variable name to the decorator chain
|
||||
out:appendStr(name)
|
||||
|
||||
-- CANDRAN : close parantheses
|
||||
for _ in pairs(decoratorChain) do
|
||||
out:appendStr(")")
|
||||
end
|
||||
|
||||
if i ~= #names then out:appendStr(", ") end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'Function' then
|
||||
--print(util.PrintTable(statement))
|
||||
|
||||
if statement.IsLocal then
|
||||
appendNextToken( "local" )
|
||||
end
|
||||
appendNextToken( "function" )
|
||||
|
||||
if statement.IsLocal then
|
||||
appendStr(statement.Name.Name)
|
||||
else
|
||||
formatExpr(statement.Name)
|
||||
end
|
||||
|
||||
appendNextToken( "(" )
|
||||
if #statement.Arguments > 0 then
|
||||
for i = 1, #statement.Arguments do
|
||||
appendStr( statement.Arguments[i].Name )
|
||||
appendComma( i ~= #statement.Arguments or statement.VarArg )
|
||||
if i == #statement.Arguments and statement.VarArg then
|
||||
appendNextToken( "..." )
|
||||
end
|
||||
end
|
||||
elseif statement.VarArg then
|
||||
appendNextToken( "..." )
|
||||
end
|
||||
appendNextToken( ")" )
|
||||
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'GenericForStatement' then
|
||||
appendNextToken( "for" )
|
||||
for i = 1, #statement.VariableList do
|
||||
appendStr( statement.VariableList[i].Name )
|
||||
appendComma( i ~= #statement.VariableList )
|
||||
end
|
||||
appendNextToken( "in" )
|
||||
for i = 1, #statement.Generators do
|
||||
formatExpr(statement.Generators[i])
|
||||
appendComma( i ~= #statement.Generators )
|
||||
end
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'NumericForStatement' then
|
||||
appendNextToken( "for" )
|
||||
appendStr( statement.Variable.Name )
|
||||
appendNextToken( "=" )
|
||||
formatExpr(statement.Start)
|
||||
appendNextToken( "," )
|
||||
formatExpr(statement.End)
|
||||
if statement.Step then
|
||||
appendNextToken( "," )
|
||||
formatExpr(statement.Step)
|
||||
end
|
||||
appendNextToken( "do" )
|
||||
formatStatlist(statement.Body)
|
||||
appendNextToken( "end" )
|
||||
|
||||
elseif statement.AstType == 'LabelStatement' then
|
||||
appendNextToken( "::" )
|
||||
appendStr( statement.Label )
|
||||
appendNextToken( "::" )
|
||||
|
||||
elseif statement.AstType == 'GotoStatement' then
|
||||
appendNextToken( "goto" )
|
||||
appendStr( statement.Label )
|
||||
|
||||
elseif statement.AstType == 'Eof' then
|
||||
appendWhite()
|
||||
|
||||
else
|
||||
print("Unknown AST Type: ", statement.AstType)
|
||||
end
|
||||
|
||||
if statement.Semicolon then
|
||||
appendNextToken(";")
|
||||
end
|
||||
|
||||
assert(tok_it == #statement.Tokens + 1)
|
||||
debug_printf("/formatStatment")
|
||||
end
|
||||
|
||||
formatStatlist = function(statList)
|
||||
for _, stat in ipairs(statList.Body) do
|
||||
formatStatement(stat)
|
||||
end
|
||||
end
|
||||
|
||||
formatStatlist(ast)
|
||||
|
||||
return true, table.concat(out.rope)
|
||||
end
|
||||
|
||||
return Format_Identity
|
||||
|
|
@ -1,364 +0,0 @@
|
|||
|
||||
local parser = require'ParseLua'
|
||||
local ParseLua = parser.ParseLua
|
||||
local util = require'Util'
|
||||
local lookupify = util.lookupify
|
||||
|
||||
--
|
||||
-- FormatMini.lua
|
||||
--
|
||||
-- Returns the minified version of an AST. Operations which are performed:
|
||||
-- - All comments and whitespace are ignored
|
||||
-- - All local variables are renamed
|
||||
--
|
||||
|
||||
local LowerChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
||||
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
|
||||
local UpperChars = lookupify{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
|
||||
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
|
||||
local Digits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
|
||||
local Symbols = lookupify{'+', '-', '*', '/', '^', '%', ',', '{', '}', '[', ']', '(', ')', ';', '#'}
|
||||
|
||||
local function Format_Mini(ast)
|
||||
local formatStatlist, formatExpr;
|
||||
local count = 0
|
||||
--
|
||||
local function joinStatementsSafe(a, b, sep)
|
||||
--print(a, b)
|
||||
if count > 150 then
|
||||
count = 0
|
||||
return a.."\n"..b
|
||||
end
|
||||
sep = sep or ' '
|
||||
local aa, bb = a:sub(-1,-1), b:sub(1,1)
|
||||
if UpperChars[aa] or LowerChars[aa] or aa == '_' then
|
||||
if not (UpperChars[bb] or LowerChars[bb] or bb == '_' or Digits[bb]) then
|
||||
--bb is a symbol, can join without sep
|
||||
return a..b
|
||||
elseif bb == '(' then
|
||||
print("==============>>>",aa,bb)
|
||||
--prevent ambiguous syntax
|
||||
return a..sep..b
|
||||
else
|
||||
return a..sep..b
|
||||
end
|
||||
elseif Digits[aa] then
|
||||
if bb == '(' then
|
||||
--can join statements directly
|
||||
return a..b
|
||||
elseif Symbols[bb] then
|
||||
return a .. b
|
||||
else
|
||||
return a..sep..b
|
||||
end
|
||||
elseif aa == '' then
|
||||
return a..b
|
||||
else
|
||||
if bb == '(' then
|
||||
--don't want to accidentally call last statement, can't join directly
|
||||
return a..sep..b
|
||||
else
|
||||
--print("asdf", '"'..a..'"', '"'..b..'"')
|
||||
return a..b
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
formatExpr = function(expr, precedence)
|
||||
local precedence = precedence or 0
|
||||
local currentPrecedence = 0
|
||||
local skipParens = false
|
||||
local out = ""
|
||||
if expr.AstType == 'VarExpr' then
|
||||
if expr.Variable then
|
||||
out = out..expr.Variable.Name
|
||||
else
|
||||
out = out..expr.Name
|
||||
end
|
||||
|
||||
elseif expr.AstType == 'NumberExpr' then
|
||||
out = out..expr.Value.Data
|
||||
|
||||
elseif expr.AstType == 'StringExpr' then
|
||||
out = out..expr.Value.Data
|
||||
|
||||
elseif expr.AstType == 'BooleanExpr' then
|
||||
out = out..tostring(expr.Value)
|
||||
|
||||
elseif expr.AstType == 'NilExpr' then
|
||||
out = joinStatementsSafe(out, "nil")
|
||||
|
||||
elseif expr.AstType == 'BinopExpr' then
|
||||
currentPrecedence = expr.OperatorPrecedence
|
||||
out = joinStatementsSafe(out, formatExpr(expr.Lhs, currentPrecedence))
|
||||
out = joinStatementsSafe(out, expr.Op)
|
||||
out = joinStatementsSafe(out, formatExpr(expr.Rhs))
|
||||
if expr.Op == '^' or expr.Op == '..' then
|
||||
currentPrecedence = currentPrecedence - 1
|
||||
end
|
||||
|
||||
if currentPrecedence < precedence then
|
||||
skipParens = false
|
||||
else
|
||||
skipParens = true
|
||||
end
|
||||
--print(skipParens, precedence, currentPrecedence)
|
||||
elseif expr.AstType == 'UnopExpr' then
|
||||
out = joinStatementsSafe(out, expr.Op)
|
||||
out = joinStatementsSafe(out, formatExpr(expr.Rhs))
|
||||
|
||||
elseif expr.AstType == 'DotsExpr' then
|
||||
out = out.."..."
|
||||
|
||||
elseif expr.AstType == 'CallExpr' then
|
||||
out = out..formatExpr(expr.Base)
|
||||
out = out.."("
|
||||
for i = 1, #expr.Arguments do
|
||||
out = out..formatExpr(expr.Arguments[i])
|
||||
if i ~= #expr.Arguments then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
out = out..")"
|
||||
|
||||
elseif expr.AstType == 'TableCallExpr' then
|
||||
out = out..formatExpr(expr.Base)
|
||||
out = out..formatExpr(expr.Arguments[1])
|
||||
|
||||
elseif expr.AstType == 'StringCallExpr' then
|
||||
out = out..formatExpr(expr.Base)
|
||||
out = out..expr.Arguments[1].Data
|
||||
|
||||
elseif expr.AstType == 'IndexExpr' then
|
||||
out = out..formatExpr(expr.Base).."["..formatExpr(expr.Index).."]"
|
||||
|
||||
elseif expr.AstType == 'MemberExpr' then
|
||||
out = out..formatExpr(expr.Base)..expr.Indexer..expr.Ident.Data
|
||||
|
||||
elseif expr.AstType == 'Function' then
|
||||
expr.Scope:ObfuscateVariables()
|
||||
out = out.."function("
|
||||
if #expr.Arguments > 0 then
|
||||
for i = 1, #expr.Arguments do
|
||||
out = out..expr.Arguments[i].Name
|
||||
if i ~= #expr.Arguments then
|
||||
out = out..","
|
||||
elseif expr.VarArg then
|
||||
out = out..",..."
|
||||
end
|
||||
end
|
||||
elseif expr.VarArg then
|
||||
out = out.."..."
|
||||
end
|
||||
out = out..")"
|
||||
out = joinStatementsSafe(out, formatStatlist(expr.Body))
|
||||
out = joinStatementsSafe(out, "end")
|
||||
|
||||
elseif expr.AstType == 'ConstructorExpr' then
|
||||
out = out.."{"
|
||||
for i = 1, #expr.EntryList do
|
||||
local entry = expr.EntryList[i]
|
||||
if entry.Type == 'Key' then
|
||||
out = out.."["..formatExpr(entry.Key).."]="..formatExpr(entry.Value)
|
||||
elseif entry.Type == 'Value' then
|
||||
out = out..formatExpr(entry.Value)
|
||||
elseif entry.Type == 'KeyString' then
|
||||
out = out..entry.Key.."="..formatExpr(entry.Value)
|
||||
end
|
||||
if i ~= #expr.EntryList then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
out = out.."}"
|
||||
|
||||
elseif expr.AstType == 'Parentheses' then
|
||||
out = out.."("..formatExpr(expr.Inner)..")"
|
||||
|
||||
end
|
||||
--print(">>", skipParens, expr.ParenCount, out)
|
||||
if not skipParens then
|
||||
--print("hehe")
|
||||
out = string.rep('(', expr.ParenCount or 0) .. out
|
||||
out = out .. string.rep(')', expr.ParenCount or 0)
|
||||
--print("", out)
|
||||
end
|
||||
count = count + #out
|
||||
return --[[print(out) or]] out
|
||||
end
|
||||
|
||||
local formatStatement = function(statement)
|
||||
local out = ''
|
||||
if statement.AstType == 'AssignmentStatement' then
|
||||
for i = 1, #statement.Lhs do
|
||||
out = out..formatExpr(statement.Lhs[i])
|
||||
if i ~= #statement.Lhs then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
if #statement.Rhs > 0 then
|
||||
out = out.."="
|
||||
for i = 1, #statement.Rhs do
|
||||
out = out..formatExpr(statement.Rhs[i])
|
||||
if i ~= #statement.Rhs then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'CallStatement' then
|
||||
out = formatExpr(statement.Expression)
|
||||
|
||||
elseif statement.AstType == 'LocalStatement' then
|
||||
out = out.."local "
|
||||
for i = 1, #statement.LocalList do
|
||||
out = out..statement.LocalList[i].Name
|
||||
if i ~= #statement.LocalList then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
if #statement.InitList > 0 then
|
||||
out = out.."="
|
||||
for i = 1, #statement.InitList do
|
||||
out = out..formatExpr(statement.InitList[i])
|
||||
if i ~= #statement.InitList then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'IfStatement' then
|
||||
out = joinStatementsSafe("if", formatExpr(statement.Clauses[1].Condition))
|
||||
out = joinStatementsSafe(out, "then")
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Clauses[1].Body))
|
||||
for i = 2, #statement.Clauses do
|
||||
local st = statement.Clauses[i]
|
||||
if st.Condition then
|
||||
out = joinStatementsSafe(out, "elseif")
|
||||
out = joinStatementsSafe(out, formatExpr(st.Condition))
|
||||
out = joinStatementsSafe(out, "then")
|
||||
else
|
||||
out = joinStatementsSafe(out, "else")
|
||||
end
|
||||
out = joinStatementsSafe(out, formatStatlist(st.Body))
|
||||
end
|
||||
out = joinStatementsSafe(out, "end")
|
||||
|
||||
elseif statement.AstType == 'WhileStatement' then
|
||||
out = joinStatementsSafe("while", formatExpr(statement.Condition))
|
||||
out = joinStatementsSafe(out, "do")
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
out = joinStatementsSafe(out, "end")
|
||||
|
||||
elseif statement.AstType == 'DoStatement' then
|
||||
out = joinStatementsSafe(out, "do")
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
out = joinStatementsSafe(out, "end")
|
||||
|
||||
elseif statement.AstType == 'ReturnStatement' then
|
||||
out = "return"
|
||||
for i = 1, #statement.Arguments do
|
||||
out = joinStatementsSafe(out, formatExpr(statement.Arguments[i]))
|
||||
if i ~= #statement.Arguments then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
|
||||
elseif statement.AstType == 'BreakStatement' then
|
||||
out = "break"
|
||||
|
||||
elseif statement.AstType == 'RepeatStatement' then
|
||||
out = "repeat"
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
out = joinStatementsSafe(out, "until")
|
||||
out = joinStatementsSafe(out, formatExpr(statement.Condition))
|
||||
|
||||
elseif statement.AstType == 'Function' then
|
||||
statement.Scope:ObfuscateVariables()
|
||||
if statement.IsLocal then
|
||||
out = "local"
|
||||
end
|
||||
out = joinStatementsSafe(out, "function ")
|
||||
if statement.IsLocal then
|
||||
out = out..statement.Name.Name
|
||||
else
|
||||
out = out..formatExpr(statement.Name)
|
||||
end
|
||||
out = out.."("
|
||||
if #statement.Arguments > 0 then
|
||||
for i = 1, #statement.Arguments do
|
||||
out = out..statement.Arguments[i].Name
|
||||
if i ~= #statement.Arguments then
|
||||
out = out..","
|
||||
elseif statement.VarArg then
|
||||
--print("Apply vararg")
|
||||
out = out..",..."
|
||||
end
|
||||
end
|
||||
elseif statement.VarArg then
|
||||
out = out.."..."
|
||||
end
|
||||
out = out..")"
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
out = joinStatementsSafe(out, "end")
|
||||
|
||||
elseif statement.AstType == 'GenericForStatement' then
|
||||
statement.Scope:ObfuscateVariables()
|
||||
out = "for "
|
||||
for i = 1, #statement.VariableList do
|
||||
out = out..statement.VariableList[i].Name
|
||||
if i ~= #statement.VariableList then
|
||||
out = out..","
|
||||
end
|
||||
end
|
||||
out = out.." in"
|
||||
for i = 1, #statement.Generators do
|
||||
out = joinStatementsSafe(out, formatExpr(statement.Generators[i]))
|
||||
if i ~= #statement.Generators then
|
||||
out = joinStatementsSafe(out, ',')
|
||||
end
|
||||
end
|
||||
out = joinStatementsSafe(out, "do")
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
out = joinStatementsSafe(out, "end")
|
||||
|
||||
elseif statement.AstType == 'NumericForStatement' then
|
||||
out = "for "
|
||||
out = out..statement.Variable.Name.."="
|
||||
out = out..formatExpr(statement.Start)..","..formatExpr(statement.End)
|
||||
if statement.Step then
|
||||
out = out..","..formatExpr(statement.Step)
|
||||
end
|
||||
out = joinStatementsSafe(out, "do")
|
||||
out = joinStatementsSafe(out, formatStatlist(statement.Body))
|
||||
out = joinStatementsSafe(out, "end")
|
||||
elseif statement.AstType == 'LabelStatement' then
|
||||
out = getIndentation() .. "::" .. statement.Label .. "::"
|
||||
elseif statement.AstType == 'GotoStatement' then
|
||||
out = getIndentation() .. "goto " .. statement.Label
|
||||
elseif statement.AstType == 'Comment' then
|
||||
-- ignore
|
||||
elseif statement.AstType == 'Eof' then
|
||||
-- ignore
|
||||
else
|
||||
print("Unknown AST Type: " .. statement.AstType)
|
||||
end
|
||||
count = count + #out
|
||||
return out
|
||||
end
|
||||
|
||||
formatStatlist = function(statList)
|
||||
local out = ''
|
||||
statList.Scope:ObfuscateVariables()
|
||||
for _, stat in pairs(statList.Body) do
|
||||
out = joinStatementsSafe(out, formatStatement(stat), ';')
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
ast.Scope:ObfuscateVariables()
|
||||
return formatStatlist(ast)
|
||||
end
|
||||
|
||||
return Format_Mini
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
@echo off
|
||||
lua CommandLineMinify.lua %*
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
lua CommandLineMinify.lua $@
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,44 +0,0 @@
|
|||
Lua Parsing and Refactorization tools
|
||||
=========
|
||||
|
||||
A collection of tools for working with Lua source code. Primarily a Lua source code minifier, but also includes some static analysis tools and a general Lua lexer and parser.
|
||||
|
||||
Currently the minifier performs:
|
||||
|
||||
- Stripping of all comments and whitespace
|
||||
- True semantic renaming of all local variables to a reduced form
|
||||
- Reduces the source to the minimal spacing, spaces are only inserted where actually needed.
|
||||
|
||||
|
||||
LuaMinify Command Line Utility Usage
|
||||
------------------------------------
|
||||
|
||||
The `LuaMinify` shell and batch files are given as shortcuts to running a command line instance of the minifier with the following usage:
|
||||
|
||||
LuaMinify sourcefile [destfile]
|
||||
|
||||
Which will minify to a given destination file, or to a copy of the source file with _min appended to the filename if no output file is given.
|
||||
|
||||
|
||||
LuaMinify Roblox Plugin Usage
|
||||
-----------------------------
|
||||
|
||||
First, download the source code, which you can do by hitting this button:
|
||||
|
||||

|
||||
|
||||
Then copy the `RobloxPlugin` folder from the source into your Roblox Plugins directory, which can be found by hitting `Tools->Open Plugins Folder` in Roblox Studio.
|
||||
|
||||
Features/Todo
|
||||
-------------
|
||||
Features:
|
||||
|
||||
- Lua scanner/parser, which generates a full AST
|
||||
- Lua reconstructor
|
||||
- minimal
|
||||
- full reconstruction (TODO: options, comments)
|
||||
- TODO: exact reconstructor
|
||||
- support for embedded long strings/comments e.g. [[abc [[ def ]] ghi]]
|
||||
|
||||
Todo:
|
||||
- use table.concat instead of appends in the reconstructors
|
||||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
Before Width: | Height: | Size: 355 B |
|
|
@ -1,93 +0,0 @@
|
|||
--
|
||||
-- MinifyToolbar.lua
|
||||
--
|
||||
-- The main script that generates a toolbar for studio that allows minification of selected
|
||||
-- scripts, calling on the _G.Minify function defined in `Minify.lua`
|
||||
--
|
||||
|
||||
local plugin = PluginManager():CreatePlugin()
|
||||
local toolbar = plugin:CreateToolbar("Minify")
|
||||
local minifyButton = toolbar:CreateButton("", "Minify Selected Script", 'MinifyButtonIcon.png')
|
||||
local toggleReplaceButton = toolbar:CreateButton("Replace", "If enabled, selected script will be REPLACED "..
|
||||
"with a minified version",
|
||||
'ReplaceButtonIcon.png')
|
||||
|
||||
local replace = false
|
||||
|
||||
toggleReplaceButton.Click:connect(function()
|
||||
replace = not replace
|
||||
toggleReplaceButton:SetActive(replace)
|
||||
end)
|
||||
|
||||
minifyButton.Click:connect(function()
|
||||
for _, o in pairs(game.Selection:Get()) do
|
||||
if o:IsA('Script') then
|
||||
--can't read linkedsource, bail out
|
||||
if o.LinkedSource ~= '' then
|
||||
Spawn(function()
|
||||
error("Minify Plugin: Cannot Minify a script with a LinkedSource", 0)
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
--see if it has been minified
|
||||
if o.Name:sub(-4,-1) == '_Min' then
|
||||
local original = o:FindFirstChild(o.Name:sub(1,-5))
|
||||
if original then
|
||||
local st, min = _G.Minify(original.Source)
|
||||
if st then
|
||||
game:GetService("ChangeHistoryService"):SetWaypoint("Minify `"..original.Name.."`")
|
||||
if replace then
|
||||
o.Source = min
|
||||
original:Destroy()
|
||||
else
|
||||
o.Source = min
|
||||
end
|
||||
else
|
||||
Spawn(function()
|
||||
error("Minify Plugin: "..min, 0)
|
||||
end)
|
||||
return
|
||||
end
|
||||
else
|
||||
if replace then
|
||||
local st, min = _G.Minify(o.Source)
|
||||
if st then
|
||||
game:GetService("ChangeHistoryService"):SetWaypoint("Minify `"..original.Name.."`")
|
||||
o.Source = min
|
||||
else
|
||||
Spawn(function()
|
||||
error("Minify Plugin: "..min, 0)
|
||||
end)
|
||||
return
|
||||
end
|
||||
else
|
||||
Spawn(function()
|
||||
error("Minify Plugin: Missing original script `"..o.Name:sub(1,-5).."`", 0)
|
||||
end)
|
||||
end
|
||||
end
|
||||
else
|
||||
local st, min = _G.Minify(o.Source)
|
||||
if st then
|
||||
game:GetService("ChangeHistoryService"):SetWaypoint("Minify `"..o.Name.."`")
|
||||
if replace then
|
||||
o.Source = min
|
||||
o.Name = o.Name.."_Min"
|
||||
else
|
||||
local original = o:Clone()
|
||||
original.Parent = o
|
||||
original.Disabled = true
|
||||
o.Name = o.Name.."_Min"
|
||||
o.Source = min
|
||||
end
|
||||
else
|
||||
Spawn(function()
|
||||
error("Minify Plugin: "..min, 0)
|
||||
end)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 502 B |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
|
|
@ -1,221 +0,0 @@
|
|||
--[[
|
||||
This file is part of LuaMinify by stravant (https://github.com/stravant/LuaMinify).
|
||||
|
||||
LICENSE :
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2013
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Scope = {
|
||||
new = function(self, parent)
|
||||
local s = {
|
||||
Parent = parent,
|
||||
Locals = { },
|
||||
Globals = { },
|
||||
oldLocalNamesMap = { },
|
||||
oldGlobalNamesMap = { },
|
||||
Children = { },
|
||||
}
|
||||
|
||||
if parent then
|
||||
table.insert(parent.Children, s)
|
||||
end
|
||||
|
||||
return setmetatable(s, { __index = self })
|
||||
end,
|
||||
|
||||
AddLocal = function(self, v)
|
||||
table.insert(self.Locals, v)
|
||||
end,
|
||||
|
||||
AddGlobal = function(self, v)
|
||||
table.insert(self.Globals, v)
|
||||
end,
|
||||
|
||||
CreateLocal = function(self, name)
|
||||
local v
|
||||
v = self:GetLocal(name)
|
||||
if v then return v end
|
||||
v = { }
|
||||
v.Scope = self
|
||||
v.Name = name
|
||||
v.IsGlobal = false
|
||||
v.CanRename = true
|
||||
v.References = 1
|
||||
self:AddLocal(v)
|
||||
return v
|
||||
end,
|
||||
|
||||
GetLocal = function(self, name)
|
||||
for k, var in pairs(self.Locals) do
|
||||
if var.Name == name then return var end
|
||||
end
|
||||
|
||||
if self.Parent then
|
||||
return self.Parent:GetLocal(name)
|
||||
end
|
||||
end,
|
||||
|
||||
GetOldLocal = function(self, name)
|
||||
if self.oldLocalNamesMap[name] then
|
||||
return self.oldLocalNamesMap[name]
|
||||
end
|
||||
return self:GetLocal(name)
|
||||
end,
|
||||
|
||||
mapLocal = function(self, name, var)
|
||||
self.oldLocalNamesMap[name] = var
|
||||
end,
|
||||
|
||||
GetOldGlobal = function(self, name)
|
||||
if self.oldGlobalNamesMap[name] then
|
||||
return self.oldGlobalNamesMap[name]
|
||||
end
|
||||
return self:GetGlobal(name)
|
||||
end,
|
||||
|
||||
mapGlobal = function(self, name, var)
|
||||
self.oldGlobalNamesMap[name] = var
|
||||
end,
|
||||
|
||||
GetOldVariable = function(self, name)
|
||||
return self:GetOldLocal(name) or self:GetOldGlobal(name)
|
||||
end,
|
||||
|
||||
RenameLocal = function(self, oldName, newName)
|
||||
oldName = type(oldName) == 'string' and oldName or oldName.Name
|
||||
local found = false
|
||||
local var = self:GetLocal(oldName)
|
||||
if var then
|
||||
var.Name = newName
|
||||
self:mapLocal(oldName, var)
|
||||
found = true
|
||||
end
|
||||
if not found and self.Parent then
|
||||
self.Parent:RenameLocal(oldName, newName)
|
||||
end
|
||||
end,
|
||||
|
||||
RenameGlobal = function(self, oldName, newName)
|
||||
oldName = type(oldName) == 'string' and oldName or oldName.Name
|
||||
local found = false
|
||||
local var = self:GetGlobal(oldName)
|
||||
if var then
|
||||
var.Name = newName
|
||||
self:mapGlobal(oldName, var)
|
||||
found = true
|
||||
end
|
||||
if not found and self.Parent then
|
||||
self.Parent:RenameGlobal(oldName, newName)
|
||||
end
|
||||
end,
|
||||
|
||||
RenameVariable = function(self, oldName, newName)
|
||||
oldName = type(oldName) == 'string' and oldName or oldName.Name
|
||||
if self:GetLocal(oldName) then
|
||||
self:RenameLocal(oldName, newName)
|
||||
else
|
||||
self:RenameGlobal(oldName, newName)
|
||||
end
|
||||
end,
|
||||
|
||||
GetAllVariables = function(self)
|
||||
local ret = self:getVars(true) -- down
|
||||
for k, v in pairs(self:getVars(false)) do -- up
|
||||
table.insert(ret, v)
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
|
||||
getVars = function(self, top)
|
||||
local ret = { }
|
||||
if top then
|
||||
for k, v in pairs(self.Children) do
|
||||
for k2, v2 in pairs(v:getVars(true)) do
|
||||
table.insert(ret, v2)
|
||||
end
|
||||
end
|
||||
else
|
||||
for k, v in pairs(self.Locals) do
|
||||
table.insert(ret, v)
|
||||
end
|
||||
for k, v in pairs(self.Globals) do
|
||||
table.insert(ret, v)
|
||||
end
|
||||
if self.Parent then
|
||||
for k, v in pairs(self.Parent:getVars(false)) do
|
||||
table.insert(ret, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
|
||||
CreateGlobal = function(self, name)
|
||||
local v
|
||||
v = self:GetGlobal(name)
|
||||
if v then return v end
|
||||
v = { }
|
||||
v.Scope = self
|
||||
v.Name = name
|
||||
v.IsGlobal = true
|
||||
v.CanRename = true
|
||||
v.References = 1
|
||||
self:AddGlobal(v)
|
||||
return v
|
||||
end,
|
||||
|
||||
GetGlobal = function(self, name)
|
||||
for k, v in pairs(self.Globals) do
|
||||
if v.Name == name then return v end
|
||||
end
|
||||
|
||||
if self.Parent then
|
||||
return self.Parent:GetGlobal(name)
|
||||
end
|
||||
end,
|
||||
|
||||
GetVariable = function(self, name)
|
||||
return self:GetLocal(name) or self:GetGlobal(name)
|
||||
end,
|
||||
|
||||
ObfuscateLocals = function(self, recommendedMaxLength, validNameChars)
|
||||
recommendedMaxLength = recommendedMaxLength or 7
|
||||
local chars = validNameChars or "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuioplkjhgfdsazxcvbnm_"
|
||||
local chars2 = validNameChars or "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuioplkjhgfdsazxcvbnm_1234567890"
|
||||
for _, var in pairs(self.Locals) do
|
||||
local id = ""
|
||||
local tries = 0
|
||||
repeat
|
||||
local n = math.random(1, #chars)
|
||||
id = id .. chars:sub(n, n)
|
||||
for i = 1, math.random(0, tries > 5 and 30 or recommendedMaxLength) do
|
||||
local n = math.random(1, #chars2)
|
||||
id = id .. chars2:sub(n, n)
|
||||
end
|
||||
tries = tries + 1
|
||||
until not self:GetVariable(id)
|
||||
self:RenameLocal(var.Name, id)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
return Scope
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
--[[
|
||||
This file is part of LuaMinify by stravant (https://github.com/stravant/LuaMinify).
|
||||
|
||||
LICENSE :
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2013
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
--
|
||||
-- Util.lua
|
||||
--
|
||||
-- Provides some common utilities shared throughout the project.
|
||||
--
|
||||
|
||||
local function lookupify(tb)
|
||||
for _, v in pairs(tb) do
|
||||
tb[v] = true
|
||||
end
|
||||
return tb
|
||||
end
|
||||
|
||||
|
||||
local function CountTable(tb)
|
||||
local c = 0
|
||||
for _ in pairs(tb) do c = c + 1 end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
local function PrintTable(tb, atIndent)
|
||||
if tb.Print then
|
||||
return tb.Print()
|
||||
end
|
||||
atIndent = atIndent or 0
|
||||
local useNewlines = (CountTable(tb) > 1)
|
||||
local baseIndent = string.rep(' ', atIndent+1)
|
||||
local out = "{"..(useNewlines and '\n' or '')
|
||||
for k, v in pairs(tb) do
|
||||
if type(v) ~= 'function' then
|
||||
--do
|
||||
out = out..(useNewlines and baseIndent or '')
|
||||
if type(k) == 'number' then
|
||||
--nothing to do
|
||||
elseif type(k) == 'string' and k:match("^[A-Za-z_][A-Za-z0-9_]*$") then
|
||||
out = out..k.." = "
|
||||
elseif type(k) == 'string' then
|
||||
out = out.."[\""..k.."\"] = "
|
||||
else
|
||||
out = out.."["..tostring(k).."] = "
|
||||
end
|
||||
if type(v) == 'string' then
|
||||
out = out.."\""..v.."\""
|
||||
elseif type(v) == 'number' then
|
||||
out = out..v
|
||||
elseif type(v) == 'table' then
|
||||
out = out..PrintTable(v, atIndent+(useNewlines and 1 or 0))
|
||||
else
|
||||
out = out..tostring(v)
|
||||
end
|
||||
if next(tb, k) then
|
||||
out = out..","
|
||||
end
|
||||
if useNewlines then
|
||||
out = out..'\n'
|
||||
end
|
||||
end
|
||||
end
|
||||
out = out..(useNewlines and string.rep(' ', atIndent) or '').."}"
|
||||
return out
|
||||
end
|
||||
|
||||
|
||||
local function splitLines(str)
|
||||
if str:match("\n") then
|
||||
local lines = {}
|
||||
for line in str:gmatch("[^\n]*") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
assert(#lines > 0)
|
||||
return lines
|
||||
else
|
||||
return { str }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function printf(fmt, ...)
|
||||
return print(string.format(fmt, ...))
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
PrintTable = PrintTable,
|
||||
CountTable = CountTable,
|
||||
lookupify = lookupify,
|
||||
splitLines = splitLines,
|
||||
printf = printf,
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
-- From http://metalua.luaforge.net/src/lib/strict.lua.html
|
||||
--
|
||||
-- strict.lua
|
||||
-- checks uses of undeclared global variables
|
||||
-- All global variables must be 'declared' through a regular assignment
|
||||
-- (even assigning nil will do) in a main chunk before being used
|
||||
-- anywhere or assigned to inside a function.
|
||||
--
|
||||
|
||||
local mt = getmetatable(_G)
|
||||
if mt == nil then
|
||||
mt = {}
|
||||
setmetatable(_G, mt)
|
||||
end
|
||||
|
||||
__STRICT = true
|
||||
mt.__declared = {}
|
||||
|
||||
mt.__newindex = function (t, n, v)
|
||||
if __STRICT and not mt.__declared[n] then
|
||||
local w = debug.getinfo(2, "S").what
|
||||
if w ~= "main" and w ~= "C" then
|
||||
error("assign to undeclared variable '"..n.."'", 2)
|
||||
end
|
||||
mt.__declared[n] = true
|
||||
end
|
||||
rawset(t, n, v)
|
||||
end
|
||||
|
||||
mt.__index = function (t, n)
|
||||
if not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then
|
||||
error("variable '"..n.."' is not declared", 2)
|
||||
end
|
||||
return rawget(t, n)
|
||||
end
|
||||
|
||||
function global(...)
|
||||
for _, v in ipairs{...} do mt.__declared[v] = true end
|
||||
end
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
-- Adapted from Yueliang
|
||||
|
||||
package.path = "../?.lua;" .. package.path
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format = require'FormatBeautiful'
|
||||
|
||||
for w in io.lines("test_lines.txt") do
|
||||
--print(w)
|
||||
local success, ast = Parser.ParseLua(w)
|
||||
if w:find("FAIL") then
|
||||
--[[if success then
|
||||
print("ERROR PARSING LINE:")
|
||||
print("Should fail: true. Did fail: " .. tostring(not success))
|
||||
print("Line: " .. w)
|
||||
else
|
||||
--print("Suceeded!")
|
||||
end]]
|
||||
else
|
||||
if not success then
|
||||
print("ERROR PARSING LINE:")
|
||||
print("Should fail: false. Did fail: " .. tostring(not success))
|
||||
print("Line: " .. w)
|
||||
else
|
||||
success, ast = Format(ast)
|
||||
--print(success, ast)
|
||||
if not success then
|
||||
print("ERROR BEAUTIFYING LINE:")
|
||||
print("Message: " .. ast)
|
||||
print("Line: " .. w)
|
||||
end
|
||||
local success_ = success
|
||||
success, ast = loadstring(success)
|
||||
if not success then
|
||||
print("ERROR PARSING BEAUTIFIED LINE:")
|
||||
print("Message: " .. ast)
|
||||
print("Line: " .. success_)
|
||||
end
|
||||
--print("Suceeded!")
|
||||
end
|
||||
end
|
||||
end
|
||||
print"Done!"
|
||||
os.remove("tmp")
|
||||
|
||||
|
||||
--[[
|
||||
function readAll(file)
|
||||
local f = io.open(file, "rb")
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
|
||||
local text = readAll('../ParseLua.lua')
|
||||
local success, ast = Parser.ParseLua(text)
|
||||
local nice
|
||||
nice = Format(ast)
|
||||
print(nice)
|
||||
--]]
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
package.path = "../?.lua;" .. package.path
|
||||
local Parser = require'ParseLua'
|
||||
local util = require'Util'
|
||||
local FormatIdentity = require'FormatIdentity'
|
||||
local FormatMini = require'FormatMini'
|
||||
local FormatBeautiful = require'FormatBeautiful'
|
||||
require'strict'
|
||||
|
||||
function readAll(file)
|
||||
local f = io.open(file, "rb")
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
|
||||
local g_lexTime = 0
|
||||
local g_parseTime = 0
|
||||
local g_reconstructTime = 0
|
||||
|
||||
function reconstructText(text)
|
||||
local preLex = os.clock()
|
||||
|
||||
local success, tokens, ast, reconstructed
|
||||
success, tokens = Parser.LexLua(text)
|
||||
if not success then
|
||||
print("ERROR: " .. tokens)
|
||||
return
|
||||
end
|
||||
|
||||
local preParse = os.clock()
|
||||
|
||||
success, ast = Parser.ParseLua(tokens)
|
||||
if not success then
|
||||
print("ERROR: " .. ast)
|
||||
return
|
||||
end
|
||||
|
||||
local preReconstruct = os.clock()
|
||||
|
||||
local DO_MINI = false
|
||||
local DO_CHECK = false
|
||||
|
||||
if DO_MINI then
|
||||
success, reconstructed = FormatMini(ast)
|
||||
else
|
||||
success, reconstructed = FormatIdentity(ast)
|
||||
end
|
||||
|
||||
if not success then
|
||||
print("ERROR: " .. reconstructed)
|
||||
return
|
||||
end
|
||||
|
||||
local post = os.clock()
|
||||
g_lexTime = g_lexTime + (preParse - preLex)
|
||||
g_parseTime = g_parseTime + (preReconstruct - preParse)
|
||||
g_reconstructTime = g_reconstructTime + (post - preReconstruct)
|
||||
|
||||
if DO_CHECK then
|
||||
--[[
|
||||
print()
|
||||
print("Reconstructed: ")
|
||||
print("--------------------")
|
||||
print(reconstructed)
|
||||
print("--------------------")
|
||||
print("Done. ")
|
||||
--]]
|
||||
|
||||
if reconstructed == text then
|
||||
--print("Reconstruction succeeded")
|
||||
else
|
||||
print("Reconstruction failed")
|
||||
|
||||
local inputLines = util.splitLines(text)
|
||||
local outputLines = util.splitLines(reconstructed)
|
||||
local n = math.max(#inputLines, #outputLines)
|
||||
for i = 1,n do
|
||||
if inputLines[i] ~= outputLines[i] then
|
||||
util.printf("ERROR on line %i", i)
|
||||
util.printf("Input: %q", inputLines[i])
|
||||
util.printf("Output: %q", outputLines[i])
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[*[
|
||||
local files = {
|
||||
"../ParseLua.lua",
|
||||
"../FormatIdentity.lua",
|
||||
"../Scope.lua",
|
||||
"../strict.lua",
|
||||
"../Type.lua",
|
||||
"Test_identity.lua"
|
||||
}
|
||||
|
||||
for _,path in ipairs(files) do
|
||||
print(path)
|
||||
local text = readAll(path)
|
||||
reconstructText(text)
|
||||
end
|
||||
|
||||
--]]
|
||||
|
||||
print("test_lines.txt")
|
||||
|
||||
local line_nr = 0
|
||||
for text in io.lines("test_lines.txt") do
|
||||
line_nr = line_nr + 1
|
||||
if not text:find("FAIL") then
|
||||
--util.printf("\nText: %q", text)
|
||||
reconstructText(text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
reconstructText('function a(p,q,r,...) end')
|
||||
|
||||
util.printf("Lex time: %f s", g_lexTime)
|
||||
util.printf("Parse time: %f s", g_parseTime)
|
||||
util.printf("Format time: %f s", g_reconstructTime)
|
||||
|
|
@ -1,523 +0,0 @@
|
|||
; -- FAIL
|
||||
local -- FAIL
|
||||
local; -- FAIL
|
||||
local = -- FAIL
|
||||
local end -- FAIL
|
||||
local a
|
||||
local a;
|
||||
local a, b, c
|
||||
local a; local b local c;
|
||||
local a = 1
|
||||
local a local b = a
|
||||
local a, b = 1, 2
|
||||
local a, b, c = 1, 2, 3
|
||||
local a, b, c = 1
|
||||
local a = 1, 2, 3
|
||||
local a, local -- FAIL
|
||||
local 1 -- FAIL
|
||||
local "foo" -- FAIL
|
||||
local a = local -- FAIL
|
||||
local a, b, = -- FAIL
|
||||
local a, b = 1, local -- FAIL
|
||||
local a, b = , local -- FAIL
|
||||
do -- FAIL
|
||||
end -- FAIL
|
||||
do end
|
||||
do ; end -- FAIL
|
||||
do 1 end -- FAIL
|
||||
do "foo" end -- FAIL
|
||||
do local a, b end
|
||||
do local a local b end
|
||||
do local a; local b; end
|
||||
do local a = 1 end
|
||||
do do end end
|
||||
do do end; end
|
||||
do do do end end end
|
||||
do do do end; end; end
|
||||
do do do return end end end
|
||||
do end do -- FAIL
|
||||
do end end -- FAIL
|
||||
do return end
|
||||
do return return end -- FAIL
|
||||
do break end -- FAIL
|
||||
while -- FAIL
|
||||
while do -- FAIL
|
||||
while = -- FAIL
|
||||
while 1 do -- FAIL
|
||||
while 1 do end
|
||||
while 1 do local a end
|
||||
while 1 do local a local b end
|
||||
while 1 do local a; local b; end
|
||||
while 1 do 2 end -- FAIL
|
||||
while 1 do "foo" end -- FAIL
|
||||
while true do end
|
||||
while 1 do ; end -- FAIL
|
||||
while 1 do while -- FAIL
|
||||
while 1 end -- FAIL
|
||||
while 1 2 do -- FAIL
|
||||
while 1 = 2 do -- FAIL
|
||||
while 1 do return end
|
||||
while 1 do return return end -- FAIL
|
||||
while 1 do do end end
|
||||
while 1 do do return end end
|
||||
while 1 do break end
|
||||
while 1 do break break end -- FAIL
|
||||
while 1 do do break end end
|
||||
repeat -- FAIL
|
||||
repeat until -- FAIL
|
||||
repeat until 0
|
||||
repeat until false
|
||||
repeat until local -- FAIL
|
||||
repeat end -- FAIL
|
||||
repeat 1 -- FAIL
|
||||
repeat = -- FAIL
|
||||
repeat local a until 1
|
||||
repeat local a local b until 0
|
||||
repeat local a; local b; until 0
|
||||
repeat ; until 1 -- FAIL
|
||||
repeat 2 until 1 -- FAIL
|
||||
repeat "foo" until 1 -- FAIL
|
||||
repeat return until 0
|
||||
repeat return return until 0 -- FAIL
|
||||
repeat break until 0
|
||||
repeat break break until 0 -- FAIL
|
||||
repeat do end until 0
|
||||
repeat do return end until 0
|
||||
repeat do break end until 0
|
||||
for -- FAIL
|
||||
for do -- FAIL
|
||||
for end -- FAIL
|
||||
for 1 -- FAIL
|
||||
for a -- FAIL
|
||||
for true -- FAIL
|
||||
for a, in -- FAIL
|
||||
for a in -- FAIL
|
||||
for a do -- FAIL
|
||||
for a in do -- FAIL
|
||||
for a in b do -- FAIL
|
||||
for a in b end -- FAIL
|
||||
for a in b, do -- FAIL
|
||||
for a in b do end
|
||||
for a in b do local a local b end
|
||||
for a in b do local a; local b; end
|
||||
for a in b do 1 end -- FAIL
|
||||
for a in b do "foo" end -- FAIL
|
||||
for a b in -- FAIL
|
||||
for a, b, c in p do end
|
||||
for a, b, c in p, q, r do end
|
||||
for a in 1 do end
|
||||
for a in true do end
|
||||
for a in "foo" do end
|
||||
for a in b do break end
|
||||
for a in b do break break end -- FAIL
|
||||
for a in b do return end
|
||||
for a in b do return return end -- FAIL
|
||||
for a in b do do end end
|
||||
for a in b do do break end end
|
||||
for a in b do do return end end
|
||||
for = -- FAIL
|
||||
for a = -- FAIL
|
||||
for a, b = -- FAIL
|
||||
for a = do -- FAIL
|
||||
for a = 1, do -- FAIL
|
||||
for a = p, q, do -- FAIL
|
||||
for a = p q do -- FAIL
|
||||
for a = b do end -- FAIL
|
||||
for a = 1, 2, 3, 4 do end -- FAIL
|
||||
for a = p, q do end
|
||||
for a = 1, 2 do end
|
||||
for a = 1, 2 do local a local b end
|
||||
for a = 1, 2 do local a; local b; end
|
||||
for a = 1, 2 do 3 end -- FAIL
|
||||
for a = 1, 2 do "foo" end -- FAIL
|
||||
for a = p, q, r do end
|
||||
for a = 1, 2, 3 do end
|
||||
for a = p, q do break end
|
||||
for a = p, q do break break end -- FAIL
|
||||
for a = 1, 2 do return end
|
||||
for a = 1, 2 do return return end -- FAIL
|
||||
for a = p, q do do end end
|
||||
for a = p, q do do break end end
|
||||
for a = p, q do do return end end
|
||||
break -- FAIL
|
||||
return
|
||||
return;
|
||||
return return -- FAIL
|
||||
return 1
|
||||
return local -- FAIL
|
||||
return "foo"
|
||||
return 1, -- FAIL
|
||||
return 1,2,3
|
||||
return a,b,c,d
|
||||
return 1,2;
|
||||
return ...
|
||||
return 1,a,...
|
||||
if -- FAIL
|
||||
elseif -- FAIL
|
||||
else -- FAIL
|
||||
then -- FAIL
|
||||
if then -- FAIL
|
||||
if 1 -- FAIL
|
||||
if 1 then -- FAIL
|
||||
if 1 else -- FAIL
|
||||
if 1 then else -- FAIL
|
||||
if 1 then elseif -- FAIL
|
||||
if 1 then end
|
||||
if 1 then local a end
|
||||
if 1 then local a local b end
|
||||
if 1 then local a; local b; end
|
||||
if 1 then else end
|
||||
if 1 then local a else local b end
|
||||
if 1 then local a; else local b; end
|
||||
if 1 then elseif 2 -- FAIL
|
||||
if 1 then elseif 2 then -- FAIL
|
||||
if 1 then elseif 2 then end
|
||||
if 1 then local a elseif 2 then local b end
|
||||
if 1 then local a; elseif 2 then local b; end
|
||||
if 1 then elseif 2 then else end
|
||||
if 1 then else if 2 then end end
|
||||
if 1 then else if 2 then end -- FAIL
|
||||
if 1 then break end -- FAIL
|
||||
if 1 then return end
|
||||
if 1 then return return end -- FAIL
|
||||
if 1 then end; if 1 then end;
|
||||
function -- FAIL
|
||||
function 1 -- FAIL
|
||||
function end -- FAIL
|
||||
function a -- FAIL
|
||||
function a end -- FAIL
|
||||
function a( end -- FAIL
|
||||
function a() end
|
||||
function a(1 -- FAIL
|
||||
function a("foo" -- FAIL
|
||||
function a(p -- FAIL
|
||||
function a(p,) -- FAIL
|
||||
function a(p q -- FAIL
|
||||
function a(p) end
|
||||
function a(p,q,) end -- FAIL
|
||||
function a(p,q,r) end
|
||||
function a(p,q,1 -- FAIL
|
||||
function a(p) do -- FAIL
|
||||
function a(p) 1 end -- FAIL
|
||||
function a(p) return end
|
||||
function a(p) break end -- FAIL
|
||||
function a(p) return return end -- FAIL
|
||||
function a(p) do end end
|
||||
function a.( -- FAIL
|
||||
function a.1 -- FAIL
|
||||
function a.b() end
|
||||
function a.b, -- FAIL
|
||||
function a.b.( -- FAIL
|
||||
function a.b.c.d() end
|
||||
function a: -- FAIL
|
||||
function a:1 -- FAIL
|
||||
function a:b() end
|
||||
function a:b: -- FAIL
|
||||
function a:b. -- FAIL
|
||||
function a.b.c:d() end
|
||||
function a(...) end
|
||||
function a(..., -- FAIL
|
||||
function a(p,...) end
|
||||
function a(p,q,r,...) end
|
||||
function a() local a local b end
|
||||
function a() local a; local b; end
|
||||
function a() end; function a() end;
|
||||
local function -- FAIL
|
||||
local function 1 -- FAIL
|
||||
local function end -- FAIL
|
||||
local function a -- FAIL
|
||||
local function a end -- FAIL
|
||||
local function a( end -- FAIL
|
||||
local function a() end
|
||||
local function a(1 -- FAIL
|
||||
local function a("foo" -- FAIL
|
||||
local function a(p -- FAIL
|
||||
local function a(p,) -- FAIL
|
||||
local function a(p q -- FAIL
|
||||
local function a(p) end
|
||||
local function a(p,q,) end -- FAIL
|
||||
local function a(p,q,r) end
|
||||
local function a(p,q,1 -- FAIL
|
||||
local function a(p) do -- FAIL
|
||||
local function a(p) 1 end -- FAIL
|
||||
local function a(p) return end
|
||||
local function a(p) break end -- FAIL
|
||||
local function a(p) return return end -- FAIL
|
||||
local function a(p) do end end
|
||||
local function a. -- FAIL
|
||||
local function a: -- FAIL
|
||||
local function a(...) end
|
||||
local function a(..., -- FAIL
|
||||
local function a(p,...) end
|
||||
local function a(p,q,r,...) end
|
||||
local function a() local a local b end
|
||||
local function a() local a; local b; end
|
||||
local function a() end; local function a() end;
|
||||
a -- FAIL
|
||||
a, -- FAIL
|
||||
a,b,c -- FAIL
|
||||
a,b = -- FAIL
|
||||
a = 1
|
||||
a = 1,2,3
|
||||
a,b,c = 1
|
||||
a,b,c = 1,2,3
|
||||
a.b = 1
|
||||
a.b.c = 1
|
||||
a[b] = 1
|
||||
a[b][c] = 1
|
||||
a.b[c] = 1
|
||||
a[b].c = 1
|
||||
0 = -- FAIL
|
||||
"foo" = -- FAIL
|
||||
true = -- FAIL
|
||||
(a) = -- FAIL
|
||||
{} = -- FAIL
|
||||
a:b() = -- FAIL
|
||||
a() = -- FAIL
|
||||
a.b:c() = -- FAIL
|
||||
a[b]() = -- FAIL
|
||||
a = a b -- FAIL
|
||||
a = 1 2 -- FAIL
|
||||
a = a = 1 -- FAIL
|
||||
a( -- FAIL
|
||||
a()
|
||||
a(1)
|
||||
a(1,) -- FAIL
|
||||
a(1,2,3)
|
||||
1() -- FAIL
|
||||
a()()
|
||||
a.b()
|
||||
a[b]()
|
||||
a.1 -- FAIL
|
||||
a.b -- FAIL
|
||||
a[b] -- FAIL
|
||||
a.b.( -- FAIL
|
||||
a.b.c()
|
||||
a[b][c]()
|
||||
a[b].c()
|
||||
a.b[c]()
|
||||
a:b()
|
||||
a:b -- FAIL
|
||||
a:1 -- FAIL
|
||||
a.b:c()
|
||||
a[b]:c()
|
||||
a:b: -- FAIL
|
||||
a:b():c()
|
||||
a:b().c[d]:e()
|
||||
a:b()[c].d:e()
|
||||
(a)()
|
||||
()() -- FAIL
|
||||
(1)()
|
||||
("foo")()
|
||||
(true)()
|
||||
(a)()()
|
||||
(a.b)()
|
||||
(a[b])()
|
||||
(a).b()
|
||||
(a)[b]()
|
||||
(a):b()
|
||||
(a).b[c]:d()
|
||||
(a)[b].c:d()
|
||||
(a):b():c()
|
||||
(a):b().c[d]:e()
|
||||
(a):b()[c].d:e()
|
||||
a"foo"
|
||||
a[[foo]]
|
||||
a.b"foo"
|
||||
a[b]"foo"
|
||||
a:b"foo"
|
||||
a{}
|
||||
a.b{}
|
||||
a[b]{}
|
||||
a:b{}
|
||||
a()"foo"
|
||||
a"foo"()
|
||||
a"foo".b()
|
||||
a"foo"[b]()
|
||||
a"foo":c()
|
||||
a"foo""bar"
|
||||
a"foo"{}
|
||||
(a):b"foo".c[d]:e"bar"
|
||||
(a):b"foo"[c].d:e"bar"
|
||||
a(){}
|
||||
a{}()
|
||||
a{}.b()
|
||||
a{}[b]()
|
||||
a{}:c()
|
||||
a{}"foo"
|
||||
a{}{}
|
||||
(a):b{}.c[d]:e{}
|
||||
(a):b{}[c].d:e{}
|
||||
a = -- FAIL
|
||||
a = a
|
||||
a = nil
|
||||
a = false
|
||||
a = 1
|
||||
a = "foo"
|
||||
a = [[foo]]
|
||||
a = {}
|
||||
a = (a)
|
||||
a = (nil)
|
||||
a = (true)
|
||||
a = (1)
|
||||
a = ("foo")
|
||||
a = ([[foo]])
|
||||
a = ({})
|
||||
a = a.b
|
||||
a = a.b. -- FAIL
|
||||
a = a.b.c
|
||||
a = a:b -- FAIL
|
||||
a = a[b]
|
||||
a = a[1]
|
||||
a = a["foo"]
|
||||
a = a[b][c]
|
||||
a = a.b[c]
|
||||
a = a[b].c
|
||||
a = (a)[b]
|
||||
a = (a).c
|
||||
a = () -- FAIL
|
||||
a = a()
|
||||
a = a.b()
|
||||
a = a[b]()
|
||||
a = a:b()
|
||||
a = (a)()
|
||||
a = (a).b()
|
||||
a = (a)[b]()
|
||||
a = (a):b()
|
||||
a = a"foo"
|
||||
a = a{}
|
||||
a = function -- FAIL
|
||||
a = function 1 -- FAIL
|
||||
a = function a -- FAIL
|
||||
a = function end -- FAIL
|
||||
a = function( -- FAIL
|
||||
a = function() end
|
||||
a = function(1 -- FAIL
|
||||
a = function(p) end
|
||||
a = function(p,) -- FAIL
|
||||
a = function(p q -- FAIL
|
||||
a = function(p,q,r) end
|
||||
a = function(p,q,1 -- FAIL
|
||||
a = function(...) end
|
||||
a = function(..., -- FAIL
|
||||
a = function(p,...) end
|
||||
a = function(p,q,r,...) end
|
||||
a = ...
|
||||
a = a, b, ...
|
||||
a = (...)
|
||||
a = ..., 1, 2
|
||||
a = function() return ... end -- FAIL
|
||||
a = -10
|
||||
a = -"foo"
|
||||
a = -a
|
||||
a = -nil
|
||||
a = -true
|
||||
a = -{}
|
||||
a = -function() end
|
||||
a = -a()
|
||||
a = -(a)
|
||||
a = - -- FAIL
|
||||
a = not 10
|
||||
a = not "foo"
|
||||
a = not a
|
||||
a = not nil
|
||||
a = not true
|
||||
a = not {}
|
||||
a = not function() end
|
||||
a = not a()
|
||||
a = not (a)
|
||||
a = not -- FAIL
|
||||
a = #10
|
||||
a = #"foo"
|
||||
a = #a
|
||||
a = #nil
|
||||
a = #true
|
||||
a = #{}
|
||||
a = #function() end
|
||||
a = #a()
|
||||
a = #(a)
|
||||
a = # -- FAIL
|
||||
a = 1 + 2; a = 1 - 2
|
||||
a = 1 * 2; a = 1 / 2
|
||||
a = 1 ^ 2; a = 1 % 2
|
||||
a = 1 .. 2
|
||||
a = 1 + -- FAIL
|
||||
a = 1 .. -- FAIL
|
||||
a = 1 * / -- FAIL
|
||||
a = 1 + -2; a = 1 - -2
|
||||
a = 1 * - -- FAIL
|
||||
a = 1 * not 2; a = 1 / not 2
|
||||
a = 1 / not -- FAIL
|
||||
a = 1 * #"foo"; a = 1 / #"foo"
|
||||
a = 1 / # -- FAIL
|
||||
a = 1 + 2 - 3 * 4 / 5 % 6 ^ 7
|
||||
a = ((1 + 2) - 3) * (4 / (5 % 6 ^ 7))
|
||||
a = (1 + (2 - (3 * (4 / (5 % 6 ^ ((7)))))))
|
||||
a = ((1 -- FAIL
|
||||
a = ((1 + 2) -- FAIL
|
||||
a = 1) -- FAIL
|
||||
a = a + b - c
|
||||
a = "foo" + "bar"
|
||||
a = "foo".."bar".."baz"
|
||||
a = true + false - nil
|
||||
a = {} * {}
|
||||
a = function() end / function() end
|
||||
a = a() ^ b()
|
||||
a = ... % ...
|
||||
a = 1 == 2; a = 1 ~= 2
|
||||
a = 1 < 2; a = 1 <= 2
|
||||
a = 1 > 2; a = 1 >= 2
|
||||
a = 1 < 2 < 3
|
||||
a = 1 >= 2 >= 3
|
||||
a = 1 == -- FAIL
|
||||
a = ~= 2 -- FAIL
|
||||
a = "foo" == "bar"
|
||||
a = "foo" > "bar"
|
||||
a = a ~= b
|
||||
a = true == false
|
||||
a = 1 and 2; a = 1 or 2
|
||||
a = 1 and -- FAIL
|
||||
a = or 1 -- FAIL
|
||||
a = 1 and 2 and 3
|
||||
a = 1 or 2 or 3
|
||||
a = 1 and 2 or 3
|
||||
a = a and b or c
|
||||
a = a() and (b)() or c.d
|
||||
a = "foo" and "bar"
|
||||
a = true or false
|
||||
a = {} and {} or {}
|
||||
a = (1) and ("foo") or (nil)
|
||||
a = function() end == function() end
|
||||
a = function() end or function() end
|
||||
a = { -- FAIL
|
||||
a = {}
|
||||
a = {,} -- FAIL
|
||||
a = {;} -- FAIL
|
||||
a = {,,} -- FAIL
|
||||
a = {;;} -- FAIL
|
||||
a = {{ -- FAIL
|
||||
a = {{{}}}
|
||||
a = {{},{},{{}},}
|
||||
a = { 1 }
|
||||
a = { 1, }
|
||||
a = { 1; }
|
||||
a = { 1, 2 }
|
||||
a = { a, b, c, }
|
||||
a = { true; false, nil; }
|
||||
a = { a.b, a[b]; a:c(), }
|
||||
a = { 1 + 2, a > b, "a" or "b" }
|
||||
a = { a=1, }
|
||||
a = { a=1, b="foo", c=nil }
|
||||
a = { a -- FAIL
|
||||
a = { a= -- FAIL
|
||||
a = { a=, -- FAIL
|
||||
a = { a=; -- FAIL
|
||||
a = { 1, a="foo" -- FAIL
|
||||
a = { 1, a="foo"; b={}, d=true; }
|
||||
a = { [ -- FAIL
|
||||
a = { [1 -- FAIL
|
||||
a = { [1] -- FAIL
|
||||
a = { [a]= -- FAIL
|
||||
a = { ["foo"]="bar" }
|
||||
a = { [1]=a, [2]=b, }
|
||||
a = { true, a=1; ["foo"]="bar", }
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
-- Adapted from Yueliang
|
||||
|
||||
package.path = "../?.lua;" .. package.path
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format_Mini = require'FormatMini'
|
||||
local line_nr = 0
|
||||
|
||||
for w in io.lines("test_lines.txt") do
|
||||
line_nr = line_nr + 1
|
||||
--print(w)
|
||||
local success, ast = Parser.ParseLua(w)
|
||||
if w:find("FAIL") then
|
||||
--[[if success then
|
||||
print("ERROR PARSING LINE:")
|
||||
print("Should fail: true. Did fail: " .. tostring(not success))
|
||||
print("Line: " .. w)
|
||||
else
|
||||
--print("Suceeded!")
|
||||
end]]
|
||||
else
|
||||
if not success then
|
||||
print("ERROR PARSING LINE:")
|
||||
print("Should fail: false. Did fail: " .. tostring(not success))
|
||||
print("Line: " .. w)
|
||||
else
|
||||
success, ast = Format_Mini(ast)
|
||||
--print(success, ast)
|
||||
if not success then
|
||||
print("ERROR MINIFYING LINE:")
|
||||
print("Message: " .. ast)
|
||||
print("Line: " .. w)
|
||||
end
|
||||
success, ast = loadstring(success)
|
||||
if not success then
|
||||
print("ERROR PARSING MINIFIED LINE:")
|
||||
print("Message: " .. ast)
|
||||
print("Line nr: " .. line_nr)
|
||||
print("Line: " .. w)
|
||||
end
|
||||
--print("Suceeded!")
|
||||
end
|
||||
end
|
||||
end
|
||||
print"Done!"
|
||||
os.remove("tmp")
|
||||
|
||||
--[[
|
||||
function readAll(file)
|
||||
local f = io.open(file, "rb")
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
|
||||
local text = readAll('../ParseLua.lua')
|
||||
local success, ast = Parser.ParseLua(text)
|
||||
local nice
|
||||
nice = Format_Mini(ast)
|
||||
print(nice)
|
||||
--]]
|
||||
|
|
@ -1,561 +0,0 @@
|
|||
-- Adapted from Yueliang
|
||||
|
||||
local source = [=[
|
||||
; -- FAIL
|
||||
local -- FAIL
|
||||
local; -- FAIL
|
||||
local = -- FAIL
|
||||
local end -- FAIL
|
||||
local a
|
||||
local a;
|
||||
local a, b, c
|
||||
local a; local b local c;
|
||||
local a = 1
|
||||
local a local b = a
|
||||
local a, b = 1, 2
|
||||
local a, b, c = 1, 2, 3
|
||||
local a, b, c = 1
|
||||
local a = 1, 2, 3
|
||||
local a, local -- FAIL
|
||||
local 1 -- FAIL
|
||||
local "foo" -- FAIL
|
||||
local a = local -- FAIL
|
||||
local a, b, = -- FAIL
|
||||
local a, b = 1, local -- FAIL
|
||||
local a, b = , local -- FAIL
|
||||
do -- FAIL
|
||||
end -- FAIL
|
||||
do end
|
||||
do ; end -- FAIL
|
||||
do 1 end -- FAIL
|
||||
do "foo" end -- FAIL
|
||||
do local a, b end
|
||||
do local a local b end
|
||||
do local a; local b; end
|
||||
do local a = 1 end
|
||||
do do end end
|
||||
do do end; end
|
||||
do do do end end end
|
||||
do do do end; end; end
|
||||
do do do return end end end
|
||||
do end do -- FAIL
|
||||
do end end -- FAIL
|
||||
do return end
|
||||
do return return end -- FAIL
|
||||
do break end -- FAIL
|
||||
while -- FAIL
|
||||
while do -- FAIL
|
||||
while = -- FAIL
|
||||
while 1 do -- FAIL
|
||||
while 1 do end
|
||||
while 1 do local a end
|
||||
while 1 do local a local b end
|
||||
while 1 do local a; local b; end
|
||||
while 1 do 2 end -- FAIL
|
||||
while 1 do "foo" end -- FAIL
|
||||
while true do end
|
||||
while 1 do ; end -- FAIL
|
||||
while 1 do while -- FAIL
|
||||
while 1 end -- FAIL
|
||||
while 1 2 do -- FAIL
|
||||
while 1 = 2 do -- FAIL
|
||||
while 1 do return end
|
||||
while 1 do return return end -- FAIL
|
||||
while 1 do do end end
|
||||
while 1 do do return end end
|
||||
while 1 do break end
|
||||
while 1 do break break end -- FAIL
|
||||
while 1 do do break end end
|
||||
repeat -- FAIL
|
||||
repeat until -- FAIL
|
||||
repeat until 0
|
||||
repeat until false
|
||||
repeat until local -- FAIL
|
||||
repeat end -- FAIL
|
||||
repeat 1 -- FAIL
|
||||
repeat = -- FAIL
|
||||
repeat local a until 1
|
||||
repeat local a local b until 0
|
||||
repeat local a; local b; until 0
|
||||
repeat ; until 1 -- FAIL
|
||||
repeat 2 until 1 -- FAIL
|
||||
repeat "foo" until 1 -- FAIL
|
||||
repeat return until 0
|
||||
repeat return return until 0 -- FAIL
|
||||
repeat break until 0
|
||||
repeat break break until 0 -- FAIL
|
||||
repeat do end until 0
|
||||
repeat do return end until 0
|
||||
repeat do break end until 0
|
||||
for -- FAIL
|
||||
for do -- FAIL
|
||||
for end -- FAIL
|
||||
for 1 -- FAIL
|
||||
for a -- FAIL
|
||||
for true -- FAIL
|
||||
for a, in -- FAIL
|
||||
for a in -- FAIL
|
||||
for a do -- FAIL
|
||||
for a in do -- FAIL
|
||||
for a in b do -- FAIL
|
||||
for a in b end -- FAIL
|
||||
for a in b, do -- FAIL
|
||||
for a in b do end
|
||||
for a in b do local a local b end
|
||||
for a in b do local a; local b; end
|
||||
for a in b do 1 end -- FAIL
|
||||
for a in b do "foo" end -- FAIL
|
||||
for a b in -- FAIL
|
||||
for a, b, c in p do end
|
||||
for a, b, c in p, q, r do end
|
||||
for a in 1 do end
|
||||
for a in true do end
|
||||
for a in "foo" do end
|
||||
for a in b do break end
|
||||
for a in b do break break end -- FAIL
|
||||
for a in b do return end
|
||||
for a in b do return return end -- FAIL
|
||||
for a in b do do end end
|
||||
for a in b do do break end end
|
||||
for a in b do do return end end
|
||||
for = -- FAIL
|
||||
for a = -- FAIL
|
||||
for a, b = -- FAIL
|
||||
for a = do -- FAIL
|
||||
for a = 1, do -- FAIL
|
||||
for a = p, q, do -- FAIL
|
||||
for a = p q do -- FAIL
|
||||
for a = b do end -- FAIL
|
||||
for a = 1, 2, 3, 4 do end -- FAIL
|
||||
for a = p, q do end
|
||||
for a = 1, 2 do end
|
||||
for a = 1, 2 do local a local b end
|
||||
for a = 1, 2 do local a; local b; end
|
||||
for a = 1, 2 do 3 end -- FAIL
|
||||
for a = 1, 2 do "foo" end -- FAIL
|
||||
for a = p, q, r do end
|
||||
for a = 1, 2, 3 do end
|
||||
for a = p, q do break end
|
||||
for a = p, q do break break end -- FAIL
|
||||
for a = 1, 2 do return end
|
||||
for a = 1, 2 do return return end -- FAIL
|
||||
for a = p, q do do end end
|
||||
for a = p, q do do break end end
|
||||
for a = p, q do do return end end
|
||||
break -- FAIL
|
||||
return
|
||||
return;
|
||||
return return -- FAIL
|
||||
return 1
|
||||
return local -- FAIL
|
||||
return "foo"
|
||||
return 1, -- FAIL
|
||||
return 1,2,3
|
||||
return a,b,c,d
|
||||
return 1,2;
|
||||
return ...
|
||||
return 1,a,...
|
||||
if -- FAIL
|
||||
elseif -- FAIL
|
||||
else -- FAIL
|
||||
then -- FAIL
|
||||
if then -- FAIL
|
||||
if 1 -- FAIL
|
||||
if 1 then -- FAIL
|
||||
if 1 else -- FAIL
|
||||
if 1 then else -- FAIL
|
||||
if 1 then elseif -- FAIL
|
||||
if 1 then end
|
||||
if 1 then local a end
|
||||
if 1 then local a local b end
|
||||
if 1 then local a; local b; end
|
||||
if 1 then else end
|
||||
if 1 then local a else local b end
|
||||
if 1 then local a; else local b; end
|
||||
if 1 then elseif 2 -- FAIL
|
||||
if 1 then elseif 2 then -- FAIL
|
||||
if 1 then elseif 2 then end
|
||||
if 1 then local a elseif 2 then local b end
|
||||
if 1 then local a; elseif 2 then local b; end
|
||||
if 1 then elseif 2 then else end
|
||||
if 1 then else if 2 then end end
|
||||
if 1 then else if 2 then end -- FAIL
|
||||
if 1 then break end -- FAIL
|
||||
if 1 then return end
|
||||
if 1 then return return end -- FAIL
|
||||
if 1 then end; if 1 then end;
|
||||
function -- FAIL
|
||||
function 1 -- FAIL
|
||||
function end -- FAIL
|
||||
function a -- FAIL
|
||||
function a end -- FAIL
|
||||
function a( end -- FAIL
|
||||
function a() end
|
||||
function a(1 -- FAIL
|
||||
function a("foo" -- FAIL
|
||||
function a(p -- FAIL
|
||||
function a(p,) -- FAIL
|
||||
function a(p q -- FAIL
|
||||
function a(p) end
|
||||
function a(p,q,) end -- FAIL
|
||||
function a(p,q,r) end
|
||||
function a(p,q,1 -- FAIL
|
||||
function a(p) do -- FAIL
|
||||
function a(p) 1 end -- FAIL
|
||||
function a(p) return end
|
||||
function a(p) break end -- FAIL
|
||||
function a(p) return return end -- FAIL
|
||||
function a(p) do end end
|
||||
function a.( -- FAIL
|
||||
function a.1 -- FAIL
|
||||
function a.b() end
|
||||
function a.b, -- FAIL
|
||||
function a.b.( -- FAIL
|
||||
function a.b.c.d() end
|
||||
function a: -- FAIL
|
||||
function a:1 -- FAIL
|
||||
function a:b() end
|
||||
function a:b: -- FAIL
|
||||
function a:b. -- FAIL
|
||||
function a.b.c:d() end
|
||||
function a(...) end
|
||||
function a(..., -- FAIL
|
||||
function a(p,...) end
|
||||
function a(p,q,r,...) end
|
||||
function a() local a local b end
|
||||
function a() local a; local b; end
|
||||
function a() end; function a() end;
|
||||
local function -- FAIL
|
||||
local function 1 -- FAIL
|
||||
local function end -- FAIL
|
||||
local function a -- FAIL
|
||||
local function a end -- FAIL
|
||||
local function a( end -- FAIL
|
||||
local function a() end
|
||||
local function a(1 -- FAIL
|
||||
local function a("foo" -- FAIL
|
||||
local function a(p -- FAIL
|
||||
local function a(p,) -- FAIL
|
||||
local function a(p q -- FAIL
|
||||
local function a(p) end
|
||||
local function a(p,q,) end -- FAIL
|
||||
local function a(p,q,r) end
|
||||
local function a(p,q,1 -- FAIL
|
||||
local function a(p) do -- FAIL
|
||||
local function a(p) 1 end -- FAIL
|
||||
local function a(p) return end
|
||||
local function a(p) break end -- FAIL
|
||||
local function a(p) return return end -- FAIL
|
||||
local function a(p) do end end
|
||||
local function a. -- FAIL
|
||||
local function a: -- FAIL
|
||||
local function a(...) end
|
||||
local function a(..., -- FAIL
|
||||
local function a(p,...) end
|
||||
local function a(p,q,r,...) end
|
||||
local function a() local a local b end
|
||||
local function a() local a; local b; end
|
||||
local function a() end; local function a() end;
|
||||
a -- FAIL
|
||||
a, -- FAIL
|
||||
a,b,c -- FAIL
|
||||
a,b = -- FAIL
|
||||
a = 1
|
||||
a = 1,2,3
|
||||
a,b,c = 1
|
||||
a,b,c = 1,2,3
|
||||
a.b = 1
|
||||
a.b.c = 1
|
||||
a[b] = 1
|
||||
a[b][c] = 1
|
||||
a.b[c] = 1
|
||||
a[b].c = 1
|
||||
0 = -- FAIL
|
||||
"foo" = -- FAIL
|
||||
true = -- FAIL
|
||||
(a) = -- FAIL
|
||||
{} = -- FAIL
|
||||
a:b() = -- FAIL
|
||||
a() = -- FAIL
|
||||
a.b:c() = -- FAIL
|
||||
a[b]() = -- FAIL
|
||||
a = a b -- FAIL
|
||||
a = 1 2 -- FAIL
|
||||
a = a = 1 -- FAIL
|
||||
a( -- FAIL
|
||||
a()
|
||||
a(1)
|
||||
a(1,) -- FAIL
|
||||
a(1,2,3)
|
||||
1() -- FAIL
|
||||
a()()
|
||||
a.b()
|
||||
a[b]()
|
||||
a.1 -- FAIL
|
||||
a.b -- FAIL
|
||||
a[b] -- FAIL
|
||||
a.b.( -- FAIL
|
||||
a.b.c()
|
||||
a[b][c]()
|
||||
a[b].c()
|
||||
a.b[c]()
|
||||
a:b()
|
||||
a:b -- FAIL
|
||||
a:1 -- FAIL
|
||||
a.b:c()
|
||||
a[b]:c()
|
||||
a:b: -- FAIL
|
||||
a:b():c()
|
||||
a:b().c[d]:e()
|
||||
a:b()[c].d:e()
|
||||
(a)()
|
||||
()() -- FAIL
|
||||
(1)()
|
||||
("foo")()
|
||||
(true)()
|
||||
(a)()()
|
||||
(a.b)()
|
||||
(a[b])()
|
||||
(a).b()
|
||||
(a)[b]()
|
||||
(a):b()
|
||||
(a).b[c]:d()
|
||||
(a)[b].c:d()
|
||||
(a):b():c()
|
||||
(a):b().c[d]:e()
|
||||
(a):b()[c].d:e()
|
||||
a"foo"
|
||||
a[[foo]]
|
||||
a.b"foo"
|
||||
a[b]"foo"
|
||||
a:b"foo"
|
||||
a{}
|
||||
a.b{}
|
||||
a[b]{}
|
||||
a:b{}
|
||||
a()"foo"
|
||||
a"foo"()
|
||||
a"foo".b()
|
||||
a"foo"[b]()
|
||||
a"foo":c()
|
||||
a"foo""bar"
|
||||
a"foo"{}
|
||||
(a):b"foo".c[d]:e"bar"
|
||||
(a):b"foo"[c].d:e"bar"
|
||||
a(){}
|
||||
a{}()
|
||||
a{}.b()
|
||||
a{}[b]()
|
||||
a{}:c()
|
||||
a{}"foo"
|
||||
a{}{}
|
||||
(a):b{}.c[d]:e{}
|
||||
(a):b{}[c].d:e{}
|
||||
a = -- FAIL
|
||||
a = a
|
||||
a = nil
|
||||
a = false
|
||||
a = 1
|
||||
a = "foo"
|
||||
a = [[foo]]
|
||||
a = {}
|
||||
a = (a)
|
||||
a = (nil)
|
||||
a = (true)
|
||||
a = (1)
|
||||
a = ("foo")
|
||||
a = ([[foo]])
|
||||
a = ({})
|
||||
a = a.b
|
||||
a = a.b. -- FAIL
|
||||
a = a.b.c
|
||||
a = a:b -- FAIL
|
||||
a = a[b]
|
||||
a = a[1]
|
||||
a = a["foo"]
|
||||
a = a[b][c]
|
||||
a = a.b[c]
|
||||
a = a[b].c
|
||||
a = (a)[b]
|
||||
a = (a).c
|
||||
a = () -- FAIL
|
||||
a = a()
|
||||
a = a.b()
|
||||
a = a[b]()
|
||||
a = a:b()
|
||||
a = (a)()
|
||||
a = (a).b()
|
||||
a = (a)[b]()
|
||||
a = (a):b()
|
||||
a = a"foo"
|
||||
a = a{}
|
||||
a = function -- FAIL
|
||||
a = function 1 -- FAIL
|
||||
a = function a -- FAIL
|
||||
a = function end -- FAIL
|
||||
a = function( -- FAIL
|
||||
a = function() end
|
||||
a = function(1 -- FAIL
|
||||
a = function(p) end
|
||||
a = function(p,) -- FAIL
|
||||
a = function(p q -- FAIL
|
||||
a = function(p,q,r) end
|
||||
a = function(p,q,1 -- FAIL
|
||||
a = function(...) end
|
||||
a = function(..., -- FAIL
|
||||
a = function(p,...) end
|
||||
a = function(p,q,r,...) end
|
||||
a = ...
|
||||
a = a, b, ...
|
||||
a = (...)
|
||||
a = ..., 1, 2
|
||||
a = function() return ... end -- FAIL
|
||||
a = -10
|
||||
a = -"foo"
|
||||
a = -a
|
||||
a = -nil
|
||||
a = -true
|
||||
a = -{}
|
||||
a = -function() end
|
||||
a = -a()
|
||||
a = -(a)
|
||||
a = - -- FAIL
|
||||
a = not 10
|
||||
a = not "foo"
|
||||
a = not a
|
||||
a = not nil
|
||||
a = not true
|
||||
a = not {}
|
||||
a = not function() end
|
||||
a = not a()
|
||||
a = not (a)
|
||||
a = not -- FAIL
|
||||
a = #10
|
||||
a = #"foo"
|
||||
a = #a
|
||||
a = #nil
|
||||
a = #true
|
||||
a = #{}
|
||||
a = #function() end
|
||||
a = #a()
|
||||
a = #(a)
|
||||
a = # -- FAIL
|
||||
a = 1 + 2; a = 1 - 2
|
||||
a = 1 * 2; a = 1 / 2
|
||||
a = 1 ^ 2; a = 1 % 2
|
||||
a = 1 .. 2
|
||||
a = 1 + -- FAIL
|
||||
a = 1 .. -- FAIL
|
||||
a = 1 * / -- FAIL
|
||||
a = 1 + -2; a = 1 - -2
|
||||
a = 1 * - -- FAIL
|
||||
a = 1 * not 2; a = 1 / not 2
|
||||
a = 1 / not -- FAIL
|
||||
a = 1 * #"foo"; a = 1 / #"foo"
|
||||
a = 1 / # -- FAIL
|
||||
a = 1 + 2 - 3 * 4 / 5 % 6 ^ 7
|
||||
a = ((1 + 2) - 3) * (4 / (5 % 6 ^ 7))
|
||||
a = (1 + (2 - (3 * (4 / (5 % 6 ^ ((7)))))))
|
||||
a = ((1 -- FAIL
|
||||
a = ((1 + 2) -- FAIL
|
||||
a = 1) -- FAIL
|
||||
a = a + b - c
|
||||
a = "foo" + "bar"
|
||||
a = "foo".."bar".."baz"
|
||||
a = true + false - nil
|
||||
a = {} * {}
|
||||
a = function() end / function() end
|
||||
a = a() ^ b()
|
||||
a = ... % ...
|
||||
a = 1 == 2; a = 1 ~= 2
|
||||
a = 1 < 2; a = 1 <= 2
|
||||
a = 1 > 2; a = 1 >= 2
|
||||
a = 1 < 2 < 3
|
||||
a = 1 >= 2 >= 3
|
||||
a = 1 == -- FAIL
|
||||
a = ~= 2 -- FAIL
|
||||
a = "foo" == "bar"
|
||||
a = "foo" > "bar"
|
||||
a = a ~= b
|
||||
a = true == false
|
||||
a = 1 and 2; a = 1 or 2
|
||||
a = 1 and -- FAIL
|
||||
a = or 1 -- FAIL
|
||||
a = 1 and 2 and 3
|
||||
a = 1 or 2 or 3
|
||||
a = 1 and 2 or 3
|
||||
a = a and b or c
|
||||
a = a() and (b)() or c.d
|
||||
a = "foo" and "bar"
|
||||
a = true or false
|
||||
a = {} and {} or {}
|
||||
a = (1) and ("foo") or (nil)
|
||||
a = function() end == function() end
|
||||
a = function() end or function() end
|
||||
a = { -- FAIL
|
||||
a = {}
|
||||
a = {,} -- FAIL
|
||||
a = {;} -- FAIL
|
||||
a = {,,} -- FAIL
|
||||
a = {;;} -- FAIL
|
||||
a = {{ -- FAIL
|
||||
a = {{{}}}
|
||||
a = {{},{},{{}},}
|
||||
a = { 1 }
|
||||
a = { 1, }
|
||||
a = { 1; }
|
||||
a = { 1, 2 }
|
||||
a = { a, b, c, }
|
||||
a = { true; false, nil; }
|
||||
a = { a.b, a[b]; a:c(), }
|
||||
a = { 1 + 2, a > b, "a" or "b" }
|
||||
a = { a=1, }
|
||||
a = { a=1, b="foo", c=nil }
|
||||
a = { a -- FAIL
|
||||
a = { a= -- FAIL
|
||||
a = { a=, -- FAIL
|
||||
a = { a=; -- FAIL
|
||||
a = { 1, a="foo" -- FAIL
|
||||
a = { 1, a="foo"; b={}, d=true; }
|
||||
a = { [ -- FAIL
|
||||
a = { [1 -- FAIL
|
||||
a = { [1] -- FAIL
|
||||
a = { [a]= -- FAIL
|
||||
a = { ["foo"]="bar" }
|
||||
a = { [1]=a, [2]=b, }
|
||||
a = { true, a=1; ["foo"]="bar", }
|
||||
]=]
|
||||
|
||||
package.path = "../?.lua;" .. package.path
|
||||
local util = require'Util'
|
||||
local Parser = require'ParseLua'
|
||||
local Format_Mini = require'FormatMini'
|
||||
|
||||
local f = io.open("tmp", 'wb')
|
||||
f:write(source)
|
||||
f:close()
|
||||
for w in io.lines("tmp") do
|
||||
--print(w)
|
||||
local success, ast = Parser.ParseLua(w)
|
||||
if w:find("FAIL") then
|
||||
if success then
|
||||
print("ERROR PARSING LINE:")
|
||||
print("Should fail: true. Did fail: " .. tostring(not success))
|
||||
--print("Message: " .. ast)
|
||||
print("Line: " .. w)
|
||||
else
|
||||
--print("Suceeded!")
|
||||
end
|
||||
else
|
||||
if not success then
|
||||
print("ERROR PARSING LINE:")
|
||||
print("Should fail: false. Did fail: " .. tostring(not success))
|
||||
print("Message: " .. ast)
|
||||
print("Line: " .. w)
|
||||
else
|
||||
--print("Suceeded!")
|
||||
end
|
||||
end
|
||||
end
|
||||
print"Done!"
|
||||
os.remove("tmp")
|
||||
111
lib/table.lua
111
lib/table.lua
|
|
@ -1,111 +0,0 @@
|
|||
--[[
|
||||
Table utility by Thomas99.
|
||||
|
||||
LICENSE :
|
||||
Copyright (c) 2015 Thomas99
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the
|
||||
use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including
|
||||
commercial applications, and to alter it and redistribute it freely, subject
|
||||
to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software in a
|
||||
product, an acknowledgment in the product documentation would be appreciated
|
||||
but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
]]
|
||||
|
||||
-- Diverses fonctions en rapport avec les tables.
|
||||
-- v0.1.0
|
||||
--
|
||||
-- Changements :
|
||||
-- - v0.1.0 :
|
||||
-- Première version versionnée. Il a dû se passer des trucs avant mais j'ai pas noté :p
|
||||
|
||||
-- Copie récursivement la table t dans la table dest (ou une table vide si non précisé) et la retourne
|
||||
-- replace (false) : indique si oui ou non, les clefs existant déjà dans dest doivent être écrasées par celles de t
|
||||
-- metatable (true) : copier ou non également les metatables
|
||||
-- filter (function) : filtre, si retourne true copie l'objet, sinon ne le copie pas
|
||||
-- Note : les metatables des objets ne sont jamais re-copiées (mais référence à la place), car sinon lors de la copie
|
||||
-- la classe de ces objets changera pour une nouvelle classe, et c'est pas pratique :p
|
||||
function table.copy(t, dest, replace, metatable, filter, copied)
|
||||
local copied = copied or {}
|
||||
local replace = replace or false
|
||||
local metatable = (metatable==nil or metatable) and true
|
||||
local filter = filter or function(name, source, destination) return true end
|
||||
|
||||
if type(t) ~= "table" then
|
||||
return t
|
||||
elseif copied[t] then -- si la table a déjà été copiée
|
||||
return copied[t]
|
||||
end
|
||||
|
||||
local dest = dest or {} -- la copie
|
||||
|
||||
copied[t] = dest -- on marque la table comme copiée
|
||||
|
||||
for k, v in pairs(t) do
|
||||
if filter(k, t, dest) then
|
||||
if replace then
|
||||
dest[k] = table.copy(v, dest[k], replace, metatable, filter, copied)
|
||||
else
|
||||
if dest[k] == nil or type(v) == "table" then -- si la clef n'existe pas déjà dans dest ou si c'est une table à copier
|
||||
dest[k] = table.copy(v, dest[k], replace, metatable, filter, copied)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- copie des metatables
|
||||
if metatable then
|
||||
if t.__classe then
|
||||
setmetatable(dest, getmetatable(t))
|
||||
else
|
||||
setmetatable(dest, table.copy(getmetatable(t), getmetatable(dest), replace, filter))
|
||||
end
|
||||
end
|
||||
|
||||
return dest
|
||||
end
|
||||
|
||||
-- retourne true si value est dans la table
|
||||
function table.isIn(table, value)
|
||||
for _,v in pairs(table) do
|
||||
if v == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- retourne la longueur exacte d'une table (fonctionne sur les tables à clef)
|
||||
function table.len(t)
|
||||
local len=0
|
||||
for i in pairs(t) do
|
||||
len=len+1
|
||||
end
|
||||
return len
|
||||
end
|
||||
|
||||
-- Sépare str en éléments séparés par le pattern et retourne une table
|
||||
function string.split(str, pattern)
|
||||
local t = {}
|
||||
local pos = 0
|
||||
|
||||
for i,p in string.gmatch(str, "(.-)"..pattern.."()") do
|
||||
table.insert(t, i)
|
||||
pos = p
|
||||
end
|
||||
|
||||
table.insert(t, str:sub(pos))
|
||||
|
||||
return t
|
||||
end
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2013
|
||||
Copyright (c) 2014 Andre Murbach Maidl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
127
lua-parser/README.md
Normal file
127
lua-parser/README.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
lua-parser
|
||||
==========
|
||||
[](https://travis-ci.org/andremm/lua-parser)
|
||||
|
||||
This is a Lua 5.3 parser written with [LPegLabel](https://github.com/sqmedeiros/lpeglabel) that
|
||||
generates an AST in a format that is similar to the one specified by [Metalua](https://github.com/fab13n/metalua-parser).
|
||||
The parser uses LPegLabel to provide more specific error messages.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
lua >= 5.1
|
||||
lpeglabel >= 1.0.0
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
The package `lua-parser` has two modules: `lua-parser.parser`
|
||||
and `lua-parser.pp`.
|
||||
|
||||
The module `lua-parser.parser` implements the function `parser.parse`:
|
||||
|
||||
* `parser.parse (subject, filename)`
|
||||
|
||||
Both subject and filename should be strings.
|
||||
It tries to parse subject and returns the AST in case of success.
|
||||
It returns **nil** plus an error message in case of error.
|
||||
In case of error, the parser uses the string filename to build an
|
||||
error message.
|
||||
|
||||
The module `lua-parser.pp` implements a pretty printer to the AST and
|
||||
a dump function:
|
||||
|
||||
* `pp.tostring (ast)`
|
||||
|
||||
It converts the AST to a string and returns this string.
|
||||
|
||||
* `pp.print (ast)`
|
||||
|
||||
It converts the AST to a string and prints this string.
|
||||
|
||||
* `pp.dump (ast[, i])`
|
||||
|
||||
It dumps the AST to the screen.
|
||||
The parameter **i** sets the indentation level.
|
||||
|
||||
AST format
|
||||
----------
|
||||
|
||||
block: { stat* }
|
||||
|
||||
stat:
|
||||
`Do{ stat* }
|
||||
| `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = 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
|
||||
| `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end
|
||||
| `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end
|
||||
| `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2...
|
||||
| `Localrec{ ident expr } -- only used for 'local function'
|
||||
| `Goto{ <string> } -- goto str
|
||||
| `Label{ <string> } -- ::str::
|
||||
| `Return{ <expr*> } -- return e1, e2...
|
||||
| `Break -- break
|
||||
| apply
|
||||
|
||||
expr:
|
||||
`Nil
|
||||
| `Dots
|
||||
| `Boolean{ <boolean> }
|
||||
| `Number{ <number> }
|
||||
| `String{ <string> }
|
||||
| `Function{ { `Id{ <string> }* `Dots? } block }
|
||||
| `Table{ ( `Pair{ expr expr } | expr )* }
|
||||
| `Op{ opid expr expr? }
|
||||
| `Paren{ expr } -- significant to cut multiple values returns
|
||||
| apply
|
||||
| lhs
|
||||
|
||||
apply:
|
||||
`Call{ expr expr* }
|
||||
| `Invoke{ expr `String{ <string> } expr* }
|
||||
|
||||
lhs: `Id{ <string> } | `Index{ expr expr }
|
||||
|
||||
opid: -- includes additional operators from Lua 5.3 and all relational operators
|
||||
'add' | 'sub' | 'mul' | 'div'
|
||||
| 'idiv' | 'mod' | 'pow' | 'concat'
|
||||
| 'band' | 'bor' | 'bxor' | 'shl' | 'shr'
|
||||
| 'eq' | 'ne' | 'lt' | 'gt' | 'le' | 'ge'
|
||||
| 'and' | 'or' | 'unm' | 'len' | 'bnot' | 'not'
|
||||
|
||||
|
||||
Usage
|
||||
--------
|
||||
|
||||
**Code example for parsing a string:**
|
||||
|
||||
|
||||
local parser = require "lua-parser.parser"
|
||||
local pp = require "lua-parser.pp"
|
||||
|
||||
if #arg ~= 1 then
|
||||
print("Usage: parse.lua <string>")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local ast, error_msg = parser.parse(arg[1], "example.lua")
|
||||
if not ast then
|
||||
print(error_msg)
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
pp.print(ast)
|
||||
os.exit(0)
|
||||
|
||||
**Running the above code example using a string without syntax error:**
|
||||
|
||||
$ lua parse.lua "for i=1, 10 do print(i) end"
|
||||
{ `Fornum{ `Id "i", `Number "1", `Number "10", { `Call{ `Id "print", `Id "i" } } } }
|
||||
|
||||
**Running the above code example using a string with syntax error:**
|
||||
|
||||
$ lua parse.lua "for i=1, 10 do print(i) "
|
||||
example.lua:1:24: syntax error, expected 'end' to close the for loop
|
||||
|
||||
479
lua-parser/parser.lua
Normal file
479
lua-parser/parser.lua
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
--[[
|
||||
This module implements a parser for Lua 5.3 with LPeg,
|
||||
and generates an Abstract Syntax Tree that is similar to the one generated by Metalua.
|
||||
For more information about Metalua, please, visit:
|
||||
https://github.com/fab13n/metalua-parser
|
||||
|
||||
block: { stat* }
|
||||
|
||||
stat:
|
||||
`Do{ stat* }
|
||||
| `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = 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
|
||||
| `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end
|
||||
| `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end
|
||||
| `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2...
|
||||
| `Localrec{ ident expr } -- only used for 'local function'
|
||||
| `Goto{ <string> } -- goto str
|
||||
| `Label{ <string> } -- ::str::
|
||||
| `Return{ <expr*> } -- return e1, e2...
|
||||
| `Break -- break
|
||||
| apply
|
||||
|
||||
expr:
|
||||
`Nil
|
||||
| `Dots
|
||||
| `Boolean{ <boolean> }
|
||||
| `Number{ <number> }
|
||||
| `String{ <string> }
|
||||
| `Function{ { `Id{ <string> }* `Dots? } block }
|
||||
| `Table{ ( `Pair{ expr expr } | expr )* }
|
||||
| `Op{ opid expr expr? }
|
||||
| `Paren{ expr } -- significant to cut multiple values returns
|
||||
| apply
|
||||
| lhs
|
||||
|
||||
apply:
|
||||
`Call{ expr expr* }
|
||||
| `Invoke{ expr `String{ <string> } expr* }
|
||||
|
||||
lhs: `Id{ <string> } | `Index{ expr expr }
|
||||
|
||||
opid: -- includes additional operators from Lua 5.3 and all relational operators
|
||||
'add' | 'sub' | 'mul' | 'div'
|
||||
| 'idiv' | 'mod' | 'pow' | 'concat'
|
||||
| 'band' | 'bor' | 'bxor' | 'shl' | 'shr'
|
||||
| 'eq' | 'ne' | 'lt' | 'gt' | 'le' | 'ge'
|
||||
| 'and' | 'or' | 'unm' | 'len' | 'bnot' | 'not'
|
||||
]]
|
||||
|
||||
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
|
||||
|
||||
|
||||
-- error message auxiliary functions
|
||||
|
||||
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
|
||||
|
||||
|
||||
-- regular combinators and auxiliary functions
|
||||
|
||||
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
|
||||
|
||||
-- grammar
|
||||
local G = { V"Lua",
|
||||
Lua = V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra");
|
||||
Shebang = P"#!" * (P(1) - P"\n")^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()); -- Cc({}) generates a bug since the {} would be shared across parses
|
||||
|
||||
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");
|
||||
|
||||
-- lexer
|
||||
Skip = (V"Space" + V"Comment")^0;
|
||||
Space = space^1;
|
||||
Comment = P"--" * V"LongStr" / function () return end
|
||||
+ P"--" * (P(1) - P"\n")^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'"\n'))^0) * expect(P'"', "Quote")
|
||||
+ P"'" * Cs((V"EscSeq" + (P(1)-S"'\n"))^0) * expect(P"'", "Quote");
|
||||
|
||||
EscSeq = P"\\" / "" -- remove backslash
|
||||
* ( P"a" / "\a"
|
||||
+ P"b" / "\b"
|
||||
+ P"f" / "\f"
|
||||
+ P"n" / "\n"
|
||||
+ P"r" / "\r"
|
||||
+ P"t" / "\t"
|
||||
+ P"v" / "\v"
|
||||
|
||||
+ P"\n" / "\n"
|
||||
+ P"\r" / "\n"
|
||||
|
||||
+ 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) -- true max is \u{10FFFF}
|
||||
-- utf8.char needs Lua 5.3
|
||||
-- string.char works only until \u{FF}
|
||||
|
||||
+ 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"\n"^-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
|
||||
|
||||
function parser.parse (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
|
||||
327
lua-parser/pp.lua
Normal file
327
lua-parser/pp.lua
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
--[[
|
||||
This module impements a pretty printer to the AST
|
||||
]]
|
||||
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
|
||||
|
||||
function var2str (var)
|
||||
local tag = var.tag
|
||||
local str = "`" .. tag
|
||||
if tag == "Id" then -- `Id{ <string> }
|
||||
str = str .. " " .. name2str(var[1])
|
||||
elseif tag == "Index" then -- `Index{ expr expr }
|
||||
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
|
||||
|
||||
function varlist2str (varlist)
|
||||
local l = {}
|
||||
for k, v in ipairs(varlist) do
|
||||
l[k] = var2str(v)
|
||||
end
|
||||
return "{ " .. table.concat(l, ", ") .. " }"
|
||||
end
|
||||
|
||||
function parlist2str (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
|
||||
|
||||
function fieldlist2str (fieldlist)
|
||||
local l = {}
|
||||
for k, v in ipairs(fieldlist) do
|
||||
local tag = v.tag
|
||||
if tag == "Pair" then -- `Pair{ expr expr }
|
||||
l[k] = "`" .. tag .. "{ "
|
||||
l[k] = l[k] .. exp2str(v[1]) .. ", " .. exp2str(v[2])
|
||||
l[k] = l[k] .. " }"
|
||||
else -- expr
|
||||
l[k] = exp2str(v)
|
||||
end
|
||||
end
|
||||
if #l > 0 then
|
||||
return "{ " .. table.concat(l, ", ") .. " }"
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
function exp2str (exp)
|
||||
local tag = exp.tag
|
||||
local str = "`" .. tag
|
||||
if tag == "Nil" or
|
||||
tag == "Dots" then
|
||||
elseif tag == "Boolean" then -- `Boolean{ <boolean> }
|
||||
str = str .. " " .. boolean2str(exp[1])
|
||||
elseif tag == "Number" then -- `Number{ <number> }
|
||||
str = str .. " " .. number2str(exp[1])
|
||||
elseif tag == "String" then -- `String{ <string> }
|
||||
str = str .. " " .. string2str(exp[1])
|
||||
elseif tag == "Function" then -- `Function{ { `Id{ <string> }* `Dots? } block }
|
||||
str = str .. "{ "
|
||||
str = str .. parlist2str(exp[1]) .. ", "
|
||||
str = str .. block2str(exp[2])
|
||||
str = str .. " }"
|
||||
elseif tag == "Table" then -- `Table{ ( `Pair{ expr expr } | expr )* }
|
||||
str = str .. fieldlist2str(exp)
|
||||
elseif tag == "Op" then -- `Op{ opid expr expr? }
|
||||
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 -- `Paren{ expr }
|
||||
str = str .. "{ " .. exp2str(exp[1]) .. " }"
|
||||
elseif tag == "Call" then -- `Call{ expr expr* }
|
||||
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 -- `Invoke{ expr `String{ <string> } expr* }
|
||||
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 -- `Id{ <string> }
|
||||
tag == "Index" then -- `Index{ expr expr }
|
||||
str = var2str(exp)
|
||||
else
|
||||
error("expecting an expression, but got a " .. tag)
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
function explist2str (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
|
||||
|
||||
function stm2str (stm)
|
||||
local tag = stm.tag
|
||||
local str = "`" .. tag
|
||||
if tag == "Do" then -- `Do{ stat* }
|
||||
local l = {}
|
||||
for k, v in ipairs(stm) do
|
||||
l[k] = stm2str(v)
|
||||
end
|
||||
str = str .. "{ " .. table.concat(l, ", ") .. " }"
|
||||
elseif tag == "Set" then -- `Set{ {lhs+} {expr+} }
|
||||
str = str .. "{ "
|
||||
str = str .. varlist2str(stm[1]) .. ", "
|
||||
str = str .. explist2str(stm[2])
|
||||
str = str .. " }"
|
||||
elseif tag == "While" then -- `While{ expr block }
|
||||
str = str .. "{ "
|
||||
str = str .. exp2str(stm[1]) .. ", "
|
||||
str = str .. block2str(stm[2])
|
||||
str = str .. " }"
|
||||
elseif tag == "Repeat" then -- `Repeat{ block expr }
|
||||
str = str .. "{ "
|
||||
str = str .. block2str(stm[1]) .. ", "
|
||||
str = str .. exp2str(stm[2])
|
||||
str = str .. " }"
|
||||
elseif tag == "If" then -- `If{ (expr block)+ block? }
|
||||
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 -- `Fornum{ ident expr expr expr? block }
|
||||
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 -- `Forin{ {ident+} {expr+} block }
|
||||
str = str .. "{ "
|
||||
str = str .. varlist2str(stm[1]) .. ", "
|
||||
str = str .. explist2str(stm[2]) .. ", "
|
||||
str = str .. block2str(stm[3])
|
||||
str = str .. " }"
|
||||
elseif tag == "Local" then -- `Local{ {ident+} {expr+}? }
|
||||
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 -- `Localrec{ ident expr }
|
||||
str = str .. "{ "
|
||||
str = str .. "{ " .. var2str(stm[1][1]) .. " }, "
|
||||
str = str .. "{ " .. exp2str(stm[2][1]) .. " }"
|
||||
str = str .. " }"
|
||||
elseif tag == "Goto" or -- `Goto{ <string> }
|
||||
tag == "Label" then -- `Label{ <string> }
|
||||
str = str .. "{ " .. name2str(stm[1]) .. " }"
|
||||
elseif tag == "Return" then -- `Return{ <expr>* }
|
||||
str = str .. explist2str(stm)
|
||||
elseif tag == "Break" then
|
||||
elseif tag == "Call" then -- `Call{ expr expr* }
|
||||
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 -- `Invoke{ expr `String{ <string> } expr* }
|
||||
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
|
||||
|
||||
function block2str (block)
|
||||
local l = {}
|
||||
for k, v in ipairs(block) do
|
||||
l[k] = stm2str(v)
|
||||
end
|
||||
return "{ " .. table.concat(l, ", ") .. " }"
|
||||
end
|
||||
|
||||
function pp.tostring (t)
|
||||
assert(type(t) == "table")
|
||||
return block2str(t)
|
||||
end
|
||||
|
||||
function pp.print (t)
|
||||
assert(type(t) == "table")
|
||||
print(pp.tostring(t))
|
||||
end
|
||||
|
||||
function pp.dump (t, i)
|
||||
if i == nil then i = 0 end
|
||||
io.write(string.format("{\n"))
|
||||
io.write(string.format("%s[tag] = %s\n", string.rep(" ", i+2), t.tag or "nil"))
|
||||
io.write(string.format("%s[pos] = %s\n", 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\n", tostring(v)))
|
||||
end
|
||||
end
|
||||
io.write(string.format("%s}\n", string.rep(" ", i)))
|
||||
end
|
||||
|
||||
return pp
|
||||
74
lua-parser/scope.lua
Normal file
74
lua-parser/scope.lua
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
--[[
|
||||
This module implements functions that handle scoping rules
|
||||
]]
|
||||
local scope = {}
|
||||
|
||||
function scope.lineno (s, i)
|
||||
if i == 1 then return 1, 1 end
|
||||
local l, lastline = 0, ""
|
||||
s = s:sub(1, i) .. "\n"
|
||||
for line in s:gmatch("[^\n]*[\n]") do
|
||||
l = l + 1
|
||||
lastline = line
|
||||
end
|
||||
local c = lastline:len() - 1
|
||||
return l, c ~= 0 and c or 1
|
||||
end
|
||||
|
||||
function scope.new_scope (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
|
||||
|
||||
function scope.begin_scope (env)
|
||||
env.scope = env.scope + 1
|
||||
end
|
||||
|
||||
function scope.end_scope (env)
|
||||
env.scope = env.scope - 1
|
||||
end
|
||||
|
||||
function scope.new_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
|
||||
|
||||
function scope.begin_function (env)
|
||||
env.fscope = env.fscope + 1
|
||||
end
|
||||
|
||||
function scope.end_function (env)
|
||||
env.fscope = env.fscope - 1
|
||||
end
|
||||
|
||||
function scope.begin_loop (env)
|
||||
if not env.loop then
|
||||
env.loop = 1
|
||||
else
|
||||
env.loop = env.loop + 1
|
||||
end
|
||||
end
|
||||
|
||||
function scope.end_loop (env)
|
||||
env.loop = env.loop - 1
|
||||
end
|
||||
|
||||
function scope.insideloop (env)
|
||||
return env.loop and env.loop > 0
|
||||
end
|
||||
|
||||
return scope
|
||||
394
lua-parser/validator.lua
Normal file
394
lua-parser/validator.lua
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
--[[
|
||||
This module impements a validator for the AST
|
||||
]]
|
||||
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
|
||||
|
||||
-- creates an error message for the input string
|
||||
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 <goto>"
|
||||
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
|
||||
|
||||
function traverse_parlist (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 = "<break> 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
|
||||
|
||||
function traverse_var (env, var)
|
||||
local tag = var.tag
|
||||
if tag == "Id" then -- `Id{ <string> }
|
||||
return true
|
||||
elseif tag == "Index" then -- `Index{ expr expr }
|
||||
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
|
||||
|
||||
function traverse_varlist (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
|
||||
|
||||
function traverse_exp (env, exp)
|
||||
local tag = exp.tag
|
||||
if tag == "Nil" or
|
||||
tag == "Boolean" or -- `Boolean{ <boolean> }
|
||||
tag == "Number" or -- `Number{ <number> }
|
||||
tag == "String" then -- `String{ <string> }
|
||||
return true
|
||||
elseif tag == "Dots" then
|
||||
return traverse_vararg(env, exp)
|
||||
elseif tag == "Function" then -- `Function{ { `Id{ <string> }* `Dots? } block }
|
||||
return traverse_function(env, exp)
|
||||
elseif tag == "Table" then -- `Table{ ( `Pair{ expr expr } | expr )* }
|
||||
return traverse_table(env, exp)
|
||||
elseif tag == "Op" then -- `Op{ opid expr expr? }
|
||||
return traverse_op(env, exp)
|
||||
elseif tag == "Paren" then -- `Paren{ expr }
|
||||
return traverse_paren(env, exp)
|
||||
elseif tag == "Call" then -- `Call{ expr expr* }
|
||||
return traverse_call(env, exp)
|
||||
elseif tag == "Invoke" then -- `Invoke{ expr `String{ <string> } expr* }
|
||||
return traverse_invoke(env, exp)
|
||||
elseif tag == "Id" or -- `Id{ <string> }
|
||||
tag == "Index" then -- `Index{ expr expr }
|
||||
return traverse_var(env, exp)
|
||||
else
|
||||
error("expecting an expression, but got a " .. tag)
|
||||
end
|
||||
end
|
||||
|
||||
function traverse_explist (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
|
||||
|
||||
function traverse_stm (env, stm)
|
||||
local tag = stm.tag
|
||||
if tag == "Do" then -- `Do{ stat* }
|
||||
return traverse_block(env, stm)
|
||||
elseif tag == "Set" then -- `Set{ {lhs+} {expr+} }
|
||||
return traverse_assignment(env, stm)
|
||||
elseif tag == "While" then -- `While{ expr block }
|
||||
return traverse_while(env, stm)
|
||||
elseif tag == "Repeat" then -- `Repeat{ block expr }
|
||||
return traverse_repeat(env, stm)
|
||||
elseif tag == "If" then -- `If{ (expr block)+ block? }
|
||||
return traverse_if(env, stm)
|
||||
elseif tag == "Fornum" then -- `Fornum{ ident expr expr expr? block }
|
||||
return traverse_fornum(env, stm)
|
||||
elseif tag == "Forin" then -- `Forin{ {ident+} {expr+} block }
|
||||
return traverse_forin(env, stm)
|
||||
elseif tag == "Local" then -- `Local{ {ident+} {expr+}? }
|
||||
return traverse_let(env, stm)
|
||||
elseif tag == "Localrec" then -- `Localrec{ ident expr }
|
||||
return traverse_letrec(env, stm)
|
||||
elseif tag == "Goto" then -- `Goto{ <string> }
|
||||
return traverse_goto(env, stm)
|
||||
elseif tag == "Label" then -- `Label{ <string> }
|
||||
return traverse_label(env, stm)
|
||||
elseif tag == "Return" then -- `Return{ <expr>* }
|
||||
return traverse_return(env, stm)
|
||||
elseif tag == "Break" then
|
||||
return traverse_break(env, stm)
|
||||
elseif tag == "Call" then -- `Call{ expr expr* }
|
||||
return traverse_call(env, stm)
|
||||
elseif tag == "Invoke" then -- `Invoke{ expr `String{ <string> } expr* }
|
||||
return traverse_invoke(env, stm)
|
||||
else
|
||||
error("expecting a statement, but got a " .. tag)
|
||||
end
|
||||
end
|
||||
|
||||
function traverse_block (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 }
|
||||
|
|
@ -2,7 +2,7 @@ print("========================")
|
|||
print("|| CANDRAN TESTS ||")
|
||||
print("========================")
|
||||
|
||||
local candran = dofile(arg[1] or "../build/candran.lua")
|
||||
local candran = dofile(arg[1] or "../candran.lua")
|
||||
|
||||
-- test helper
|
||||
local results = {} -- tests result
|
||||
|
|
@ -19,7 +19,7 @@ local function test(name, candranCode, result, args)
|
|||
end
|
||||
|
||||
-- load code
|
||||
local success, func = pcall(load, code)
|
||||
local success, func = pcall(loadstring or load, code)
|
||||
if not success then
|
||||
self.result = "error"
|
||||
self.message = "error while loading code :\n"..func
|
||||
|
|
@ -61,13 +61,13 @@ test("preprocessor condition", [[
|
|||
#end
|
||||
]], true)
|
||||
test("preprocessor args table", [[
|
||||
#if not (args and args.foo == "sky") then
|
||||
#if not foo == "sky" then
|
||||
# error("Invalid foo argument")
|
||||
#end
|
||||
return true
|
||||
]], true, { foo = "sky" })
|
||||
test("preprocessor print function", [[
|
||||
#print("local a = true")
|
||||
test("preprocessor write function", [[
|
||||
#write("local a = true")
|
||||
return a
|
||||
]], true)
|
||||
test("preprocessor import function", [[
|
||||
|
|
@ -75,7 +75,7 @@ test("preprocessor import function", [[
|
|||
return toInclude
|
||||
]], 5)
|
||||
test("preprocessor include function", "a = [[\n#include('toInclude.lua')\n]]\nreturn a",
|
||||
"local a = 5\nreturn a\n")
|
||||
"local a = 5\nreturn a\n\n")
|
||||
|
||||
test("+=", [[
|
||||
local a = 5
|
||||
|
|
@ -112,85 +112,12 @@ local a = "hello"
|
|||
a ..= " world"
|
||||
return a
|
||||
]], "hello world")
|
||||
|
||||
test("decorator", [[
|
||||
local a = function(func)
|
||||
local wrapper = function(...)
|
||||
local b = func(...)
|
||||
return b + 5
|
||||
end
|
||||
return wrapper
|
||||
test("default parameters", [[
|
||||
local function test(hey, def="re", no, foo=("bar"):gsub("bar", "batru"))
|
||||
return def..foo
|
||||
end
|
||||
@a
|
||||
function c(nb)
|
||||
return nb^2
|
||||
end
|
||||
return c(5)
|
||||
]], 30)
|
||||
test("decorator with arguments", [[
|
||||
local a = function(add)
|
||||
local b = function(func)
|
||||
local wrapper = function(...)
|
||||
local c = func(...)
|
||||
return c + add
|
||||
end
|
||||
return wrapper
|
||||
end
|
||||
return b
|
||||
end
|
||||
@a(10)
|
||||
function d(nb)
|
||||
return nb^2
|
||||
end
|
||||
return d(5)
|
||||
]], 35)
|
||||
test("multiple decorators", [[
|
||||
local a = function(func)
|
||||
local wrapper = function(...)
|
||||
local b = func(...)
|
||||
return b + 5
|
||||
end
|
||||
return wrapper
|
||||
end
|
||||
local c = function(func)
|
||||
local wrapper = function(...)
|
||||
local d = func(...)
|
||||
return d * 2
|
||||
end
|
||||
return wrapper
|
||||
end
|
||||
@a
|
||||
@c
|
||||
function e(nb)
|
||||
return nb^2
|
||||
end
|
||||
return e(5)
|
||||
]], 55)
|
||||
test("multiple decorators with arguments", [[
|
||||
local a = function(func)
|
||||
local wrapper = function(...)
|
||||
local b = func(...)
|
||||
return b + 5
|
||||
end
|
||||
return wrapper
|
||||
end
|
||||
local c = function(mul)
|
||||
local d = function(func)
|
||||
local wrapper = function(...)
|
||||
local e = func(...)
|
||||
return e * mul
|
||||
end
|
||||
return wrapper
|
||||
end
|
||||
return d
|
||||
end
|
||||
@a
|
||||
@c(3)
|
||||
function f(nb)
|
||||
return nb^2
|
||||
end
|
||||
return f(5)
|
||||
]], 80)
|
||||
return test(78, "SANDWICH", true)
|
||||
]], "SANDWICHbatru")
|
||||
|
||||
-- results
|
||||
print("=====================")
|
||||
|
|
|
|||
26
util.can
Normal file
26
util.can
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
local util = {}
|
||||
|
||||
function util.search(modpath, exts={"can", "lua"})
|
||||
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
|
||||
|
||||
function util.loadenv(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
|
||||
Loading…
Add table
Add a link
Reference in a new issue