mirror of
https://github.com/Reuh/candran.git
synced 2025-10-27 09:59:29 +00:00
Candran 0.3.0
* Added @ self alias * Added short anonymous functions declaration * Made assignment operators works in every direction, except up, down, behind and below, because this would be hard to visualize. * Moved files around. * Error rewriting. * Discover the amazing can commandline tool, which includes a fantastic° REPL and program running abilities. * Added functions which plagiarize Lua. * Added 0.1.0 to the version number. * If you still love pineapple flavored bread, don't hesitate to show your feelings. Also, the tests are out of date. Sad. °: not really.
This commit is contained in:
parent
2a1e293aa5
commit
4af2b41a0d
17 changed files with 2413 additions and 1865 deletions
154
README.md
154
README.md
|
|
@ -4,11 +4,9 @@ Candran is a dialect of the [Lua 5.3](http://www.lua.org) programming language w
|
|||
|
||||
Unlike Moonscript, Candran tries to stay close to the Lua syntax.
|
||||
|
||||
Candran code example :
|
||||
|
||||
````lua
|
||||
#import("lib.thing")
|
||||
#local debug = debug or false
|
||||
#local debug or= false
|
||||
|
||||
local function calculate(toadd=25)
|
||||
local result = thing.do()
|
||||
|
|
@ -19,11 +17,22 @@ local function calculate(toadd=25)
|
|||
return result
|
||||
end
|
||||
|
||||
print(calculate())
|
||||
local a = {
|
||||
hey = true,
|
||||
|
||||
newHop = :(foo, thing)
|
||||
@hey = thing(foo)
|
||||
end
|
||||
}
|
||||
|
||||
a:newHop(42, (foo)
|
||||
return "something " .. foo
|
||||
end)
|
||||
|
||||
````
|
||||
|
||||
##### Quick setup
|
||||
Install LPegLabel (```luarocks install LPegLabel```), download this repository and use Candran through ```canc.lua``` or ```candran.lua```.
|
||||
#### Quick setup
|
||||
Install LPegLabel (```luarocks install LPegLabel```), download this repository and use Candran through the scripts in ```bin/``` or use it as a library with the self-contained ```candran.lua```.
|
||||
|
||||
The language
|
||||
------------
|
||||
|
|
@ -53,7 +62,8 @@ The preprocessor has access to the following variables :
|
|||
|
||||
### Syntax additions
|
||||
After the preprocessor is run the Candran code is compiled to Lua. The Candran code adds the folowing syntax to Lua :
|
||||
##### New assignment operators
|
||||
|
||||
##### Assignment operators
|
||||
* ````var += nb````
|
||||
* ````var -= nb````
|
||||
* ````var *= nb````
|
||||
|
|
@ -71,6 +81,10 @@ After the preprocessor is run the Candran code is compiled to Lua. The Candran c
|
|||
|
||||
For example, a ````var += nb```` assignment will be compiled into ````var = var + nb````.
|
||||
|
||||
All theses operators can also be put right of the assigment operator, in which case ```var =+ nb``` will be compiled into ```var = nb + var```.
|
||||
|
||||
If you feel like writing hard to understand code, right and left operator can be used at the same time.
|
||||
|
||||
##### Default function parameters
|
||||
```lua
|
||||
function foo(bar = "default", other = thing.do())
|
||||
|
|
@ -83,43 +97,87 @@ It is equivalent to doing ```if arg == nil then arg = default end``` for each ar
|
|||
|
||||
The default values can be complete Lua expressions, and will be evaluated each time the function is run.
|
||||
|
||||
##### @ self aliases
|
||||
```lua
|
||||
a = {
|
||||
foo = "Hoi"
|
||||
}
|
||||
|
||||
function a:hey()
|
||||
print(@foo) -- Hoi
|
||||
print(@["foo"]) -- also works
|
||||
print(@ == self) -- true
|
||||
end
|
||||
```
|
||||
When a variable name is prefied with ```@```, the name will be accessed in ```self```.
|
||||
|
||||
When used by itself, ```@``` is an alias for ```self```.
|
||||
|
||||
##### Short anonymous function declaration
|
||||
```lua
|
||||
a = (arg1, arg2)
|
||||
print(arg1)
|
||||
end
|
||||
|
||||
b = :(hop)
|
||||
print(self, hop)
|
||||
end
|
||||
```
|
||||
Anonymous function (functions values) can be created in a more concise way by omitting the ```function``` keyword.
|
||||
|
||||
A ```:``` can prefix the parameters paranthesis to automatically add a ```self``` parameter.
|
||||
|
||||
Compile targets
|
||||
---------------
|
||||
Candran is based on the Lua 5.3 syntax, but can be compiled to both Lua 5.3 and Lua 5.1/LuaJit.
|
||||
|
||||
To chose a compile target, either explicitly give ```lua53``` or ```luajit``` as a second argument to ```candran.compile```, or set the ```target``` preprocessor argument when using ```candran.make``` or the command line tools.
|
||||
To chose a compile target, set the ```target``` option to ```lua53``` (default) or ```luajit``` in the option table when using the library or the command line tools.
|
||||
|
||||
Lua 5.3 specific syntax (bitwise operators, integer division) will automatically be translated in valid Lua 5.1 code, using LuaJit's ```bit``` library if necessary.
|
||||
Lua 5.3 specific syntax (bitwise operators, integer division) will automatically be translated in valid Lua 5.1 code, using LuaJIT's ```bit``` library if necessary.
|
||||
|
||||
The library
|
||||
-----------
|
||||
### Command-line usage
|
||||
The library can be used standalone through the ```canc``` utility:
|
||||
The library can be used standalone through the ```canc``` and ```can``` utility:
|
||||
|
||||
* ````lua canc.lua````
|
||||
* ````canc````
|
||||
|
||||
Display the information text (version and basic command-line usage).
|
||||
|
||||
* ````lua canc.lua [arguments] filename...````
|
||||
* ````canc [options] filename...````
|
||||
|
||||
Preprocess and compile each _filename_ Candran files, and creates the assiociated ```.lua``` files in the same directories.
|
||||
|
||||
_arguments_ is of type ````-somearg -anotherarg thing=somestring other=5 ...````, which will generate a Lua table ```{ somearg = true, anotherarg = true, thing = "somestring", other = 5 }```.
|
||||
_options_ is of type ````-somearg -anotherarg thing=somestring other=5 ...````, which will generate a Lua table ```{ somearg = true, anotherarg = true, thing = "somestring", other = 5 }```.
|
||||
|
||||
You can choose to use another directory where files should be written using the ```dest=destinationDirectory``` argument.
|
||||
|
||||
```canc``` can write to the standard output instead of creating files using the ```-print``` argument.
|
||||
|
||||
You can choosed to run only the preprocessor or compile using the ```-preprocess``` and ```-compile``` flags.
|
||||
|
||||
* example uses :
|
||||
|
||||
````lua canc.lua foo.can````
|
||||
````canc foo.can````
|
||||
|
||||
preprocess and compile _foo.can_ and write the result in _foo.lua_.
|
||||
|
||||
````lua canc.lua foo.can -verbose -print | lua````
|
||||
````canc foo.can -verbose -print | lua````
|
||||
|
||||
preprocess _foo.can_ with _verbose_ set to _true_, compile it and execute it.
|
||||
|
||||
* ```can```
|
||||
|
||||
Start a simplisitic Candran REPL.
|
||||
|
||||
* ````canc [options] filename````
|
||||
|
||||
Preprocess, compile and run _filename_ using the options provided.
|
||||
|
||||
This will automatically register the Candran package searcher so required file will be compiled as they are needed.
|
||||
|
||||
This command will use error rewriting if enabled.
|
||||
|
||||
### Library usage
|
||||
Candran can also be used as a Lua library. For example,
|
||||
````lua
|
||||
|
|
@ -132,14 +190,70 @@ f:close()
|
|||
local compiled = candran.make(contents, { lang = "fr" })
|
||||
|
||||
load(compiled)()
|
||||
|
||||
-- or simpler...
|
||||
candran.dofile("foo.can")
|
||||
````
|
||||
Will load Candran, read the file _foo.can_, compile its contents with the argument _lang_ set to _"fr"_, and then execute the result.
|
||||
|
||||
The table returned by _require("candran")_ gives you access to :
|
||||
|
||||
##### Compiler & preprocessor API
|
||||
* ````candran.VERSION```` : Candran's version string.
|
||||
* ````candran.preprocess(code[, args])```` : return the Candran code _code_, preprocessed with _args_ as argument table.
|
||||
* ````candran.compile(code[, target])```` : return the Candran code compiled to Lua.
|
||||
* ````candran.make(code[, args])```` : return the Candran code, preprocessed with _args_ as argument table and compilled to Lua.
|
||||
* ````candran.preprocess(code[, options])```` : return the Candran code _code_, preprocessed with _options_ as options table.
|
||||
* ````candran.compile(code[, options])```` : return the Candran code compiled to Lua with _options_ as the option table.
|
||||
* ````candran.make(code[, options])```` : return the Candran code, preprocessed and compiled with _options_ as options table.
|
||||
|
||||
##### Code loading helpers
|
||||
* ```candran.loadfile(filepath, env, options)``` : Candran equivalent to the Lua 5.3's loadfile funtion. Will rewrite errors by default.
|
||||
* ```candran.load(chunk, chunkname, env, options)``` : Candran equivalent to the Lua 5.3's load funtion. Will rewrite errors by default.
|
||||
* ```candran.dofile(filepath, options)``` : Candran equivalent to the Lua 5.3's dofile funtion. Will rewrite errors by default.
|
||||
|
||||
#### Error rewriting
|
||||
When using the command-line tools or the code loading helpers, Candran will automatically setup error rewriting: because the code is reformated when
|
||||
compiled and preprocessed, lines numbers given by Lua in case of error are hardly usable. To fix that, Candran map each line from the compiled file to
|
||||
the lines from the original file(s), inspired by MoonScript. Errors will be displayed as:
|
||||
|
||||
```
|
||||
example.can:12(5): attempt to call a nil value (global 'iWantAnError')
|
||||
```
|
||||
|
||||
12 is the line number in the original Candran file, and 5 is the line number in the compiled file.
|
||||
|
||||
If you are using the preprocessor ```import()``` function, the source Candran file and destination Lua file might not have the same name. In this case, the error will be:
|
||||
|
||||
```
|
||||
example.can:12(final.lua:5): attempt to call a nil value (global 'iWantAnError')
|
||||
```
|
||||
|
||||
* ```candran.messageHandler(message)``` : The error message handler used by Candran. Use it in xpcall to rewrite stacktraces to display Candran source file lines instead of compiled Lua lines.
|
||||
|
||||
##### Package searching helpers
|
||||
Candran comes with a custom package searcher which will automatically find, preprocesses and compile ```.can``` files. If you want to use Candran in your project without worrying about
|
||||
compiling the files, you can simply call
|
||||
|
||||
```lua
|
||||
require("candran").setup()
|
||||
```
|
||||
|
||||
at the top of your main Lua file. If a Candran is found when you call ```require()```, it will be automatically compiled and loaded. If both a Lua and Candran file match a module name, the Candran
|
||||
file will be loaded.
|
||||
|
||||
* ```candran.searcher(modpath)``` : Candran package searcher function. Use the existing package.path.
|
||||
* ```candran.setup()``` : Register the Candran package searcher.
|
||||
|
||||
##### Available compiler & preprocessor options
|
||||
You can give arbitrary options which will be gived to the preprocessor, but Candran already provide and uses these with their associated default values:
|
||||
|
||||
```lua
|
||||
target = "lua53" -- Compiler target. "lua53" or "luajit".
|
||||
indentation = "" -- Character(s) used for indentation in the compiled file.
|
||||
newline = "\n" -- Character(s) used for newlines in the compiled file.
|
||||
requirePrefix = "CANDRAN_" -- Prefix used when Candran needs to require an external library to provide some functionality (example: LuaJIT's bit lib when using bitwise operators).
|
||||
mapLines = true -- If true, compiled files will contain comments at the end of each line indicating the associated line and source file. Needed for error rewriting.
|
||||
chunkname = "nil" -- The chunkname used when running code using the helper functions and writing the line origin comments. Candran will try to set it to the original filename if it knows it.
|
||||
rewriteErrors = true -- True to enable error rewriting when loading code using the helper functions. Will wrap the whole code in a xpcall().
|
||||
```
|
||||
|
||||
### Compiling the library
|
||||
The Candran library itself is written is Candran, so you have to compile it with an already compiled Candran library.
|
||||
|
|
@ -149,9 +263,11 @@ The compiled _candran.lua_ should include every Lua library needed to run it. Yo
|
|||
This command will use the precompilled version of this repository (candran.lua) to compile _candran.can_ and write the result in _candran.lua_ :
|
||||
|
||||
````
|
||||
lua canc.lua candran.can
|
||||
canc candran.can
|
||||
````
|
||||
|
||||
The Candran build included in this repository were made using the ```mapLines=false``` options.
|
||||
|
||||
You can then run the tests on your build :
|
||||
|
||||
````
|
||||
|
|
|
|||
43
bin/can
Normal file
43
bin/can
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/lua
|
||||
local candran = require("candran")
|
||||
local cmdline = require("lib.cmdline")
|
||||
|
||||
local args = cmdline(arg)
|
||||
|
||||
if args.help or args.h then
|
||||
print("Candran interpreter version "..candran.VERSION.." by Reuh")
|
||||
print("Usage: "..arg[0].." [target=<target>] [options] filename")
|
||||
return
|
||||
end
|
||||
|
||||
if #args >= 1 then
|
||||
candran.dofile(args[1], args)
|
||||
else -- REPL
|
||||
print("Candran " .. candran.VERSION)
|
||||
candran.setup()
|
||||
while true do
|
||||
io.write("> ")
|
||||
local line = io.read()
|
||||
if line:match("^=") then
|
||||
line = line:gsub("^=", "return tostring(") .. ")"
|
||||
end
|
||||
|
||||
local p = dofile("lib/lua-parser/parser.lua")
|
||||
local d = dofile("lib/lua-parser/pp.lua")
|
||||
print(p.parse(line))
|
||||
print(d.dump(p.parse(line)))
|
||||
print(require"compiler.lua53"(p.parse(line)))
|
||||
|
||||
local t = { pcall(candran.load, line, "stdin") }
|
||||
if t[1] == false then
|
||||
print(t[2])
|
||||
else
|
||||
t = { pcall(t[2]) }
|
||||
if t[1] == false then
|
||||
print(t[2])
|
||||
elseif #t > 1 then
|
||||
print(unpack(t, 2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#!/bin/lua
|
||||
local candran = require("candran")
|
||||
local cmdline = require("cmdline")
|
||||
local cmdline = require("lib.cmdline")
|
||||
|
||||
if #arg < 1 then
|
||||
print("Candran compiler version "..candran.VERSION.." by Reuh")
|
||||
print("Usage: "..arg[0].." [target=<target>] [dest=<destination directory>] [-print] [preprocessor arguments] filename...")
|
||||
print("Usage: "..arg[0].." [target=<target>] [dest=<destination directory>] [-print] [-preprocess] [-compile] [options] filename...")
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -25,7 +25,20 @@ for _, file in ipairs(args) do
|
|||
local input = inputFile:read("*a")
|
||||
inputFile:close()
|
||||
|
||||
local out = candran.make(input, args)
|
||||
if args.chunkname == nil then
|
||||
args.chunkname = file
|
||||
end
|
||||
|
||||
local out = input
|
||||
if args.preprocess then
|
||||
out = candran.preprocess(out, args)
|
||||
end
|
||||
if args.compile then
|
||||
out = candran.compile(out, args)
|
||||
end
|
||||
if args.compile == nil and args.preprocess == nil then
|
||||
out = candran.make(input, args)
|
||||
end
|
||||
|
||||
if args.print then
|
||||
print(out)
|
||||
192
candran.can
192
candran.can
|
|
@ -1,28 +1,45 @@
|
|||
#import("util")
|
||||
#import("lib.util")
|
||||
#import("lib.cmdline")
|
||||
|
||||
#import("compiler.lua53")
|
||||
#import("compiler.luajit")
|
||||
#import("lua-parser.scope")
|
||||
#import("lua-parser.validator")
|
||||
#import("lua-parser.pp")
|
||||
#import("lua-parser.parser")
|
||||
#import("cmdline")
|
||||
|
||||
local util = require("util")
|
||||
#import("lib.lua-parser.scope")
|
||||
#import("lib.lua-parser.validator")
|
||||
#import("lib.lua-parser.pp")
|
||||
#import("lib.lua-parser.parser")
|
||||
|
||||
local candran = {
|
||||
VERSION = "0.2.0"
|
||||
VERSION = "0.3.0"
|
||||
}
|
||||
|
||||
--- Default options.
|
||||
local default = {
|
||||
target = "lua53",
|
||||
indentation = "",
|
||||
newline = "\n",
|
||||
requirePrefix = "CANDRAN_",
|
||||
mapLines = true,
|
||||
chunkname = "nil",
|
||||
rewriteErrors = true
|
||||
}
|
||||
|
||||
--- Run the preprocessor
|
||||
-- @tparam input string input code
|
||||
-- @tparam args table arguments for the preprocessor. They will be inserted into the preprocessor environement.
|
||||
-- @tparam options table arguments for the preprocessor. They will be inserted into the preprocessor environement.
|
||||
-- @treturn output string output code
|
||||
function candran.preprocess(input, args={})
|
||||
function candran.preprocess(input, options={})
|
||||
options = util.merge(default, options)
|
||||
|
||||
-- generate preprocessor code
|
||||
local preprocessor = ""
|
||||
local i = 0
|
||||
for line in (input.."\n"):gmatch("(.-\n)") do
|
||||
i += 1
|
||||
if line:match("^%s*#") and not line:match("^#!") then -- exclude shebang
|
||||
preprocessor ..= line:gsub("^%s*#", "")
|
||||
elseif options.mapLines then
|
||||
preprocessor ..= ("write(%q)"):format(line:sub(1, -2) .. " -- "..options.chunkname..":" .. i) .. "\n"
|
||||
else
|
||||
preprocessor ..= ("write(%q)"):format(line:sub(1, -2)) .. "\n"
|
||||
end
|
||||
|
|
@ -30,13 +47,7 @@ function candran.preprocess(input, args={})
|
|||
preprocessor ..= "return output"
|
||||
|
||||
-- make preprocessor environement
|
||||
local env = {}
|
||||
for k,v in pairs(_G) do
|
||||
env[k] = v
|
||||
end
|
||||
for k, v in pairs(args) do
|
||||
env[k] = v
|
||||
end
|
||||
local env = util.merge(_G, options)
|
||||
--- Candran library table
|
||||
env.candran = candran
|
||||
--- Current preprocessor output
|
||||
|
|
@ -45,15 +56,14 @@ function candran.preprocess(input, args={})
|
|||
-- @tparam modpath string module path
|
||||
-- @tparam margs table preprocessor arguments to use when preprocessessing the module
|
||||
-- @tparam autoRequire[opt=true] boolean true to automatically load the module into a local variable
|
||||
env.import = function(modpath, margs=args, autoRequire=true)
|
||||
env.import = function(modpath, margs={}, autoRequire=true)
|
||||
local filepath = assert(util.search(modpath), "No module named \""..modpath.."\"")
|
||||
|
||||
-- open module file
|
||||
local f = io.open(filepath)
|
||||
if not f then error("Can't open the module file to import") end
|
||||
for k, v in pairs(args) do
|
||||
if margs[k] == nil then margs[k] = v end
|
||||
end
|
||||
|
||||
margs = util.merge(options, { chunkname = filepath }, margs)
|
||||
local modcontent = candran.preprocess(f:read("*a"), margs)
|
||||
f:close()
|
||||
|
||||
|
|
@ -92,57 +102,143 @@ function candran.preprocess(input, args={})
|
|||
end
|
||||
|
||||
-- compile & load preprocessor
|
||||
local preprocess, err = util.loadenv(candran.compile(preprocessor, args.target), "candran preprocessor", env)
|
||||
local preprocess, err = util.load(candran.compile(preprocessor, args), "candran preprocessor", env)
|
||||
if not preprocess then error("Error while creating Candran preprocessor: " .. err) end
|
||||
|
||||
-- execute preprocessor
|
||||
local success, output = pcall(preprocess)
|
||||
if not success then error("Error while preprocessing file: " .. output .. "\nWith preprocessor : \n" .. preprocessor) end
|
||||
if not success then error("Error while preprocessing file: " .. output) end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
-- Compiler
|
||||
function candran.compile(input, target="lua53")
|
||||
local parse = require("lua-parser.parser").parse
|
||||
--- Run the compiler
|
||||
-- @tparam input string input code
|
||||
-- @tparam options table options for the compiler
|
||||
-- @treturn output string output code
|
||||
function candran.compile(input, options={})
|
||||
options = util.merge(default, options)
|
||||
|
||||
local ast, errmsg = parse(input, "candran")
|
||||
local ast, errmsg = parser.parse(input, "candran")
|
||||
|
||||
if not ast then
|
||||
error("Compiler: error while parsing file: "..errmsg)
|
||||
end
|
||||
|
||||
return require("compiler."..target)(ast)
|
||||
return require("compiler."..options.target)(input, ast, options)
|
||||
end
|
||||
|
||||
-- Preprocess & compile
|
||||
function candran.make(code, args={})
|
||||
return candran.compile(candran.preprocess(code, args), args.target)
|
||||
--- Preprocess & compile code
|
||||
-- @tparam code string input code
|
||||
-- @tparam options table arguments for the preprocessor and compiler
|
||||
-- @treturn output string output code
|
||||
function candran.make(code, options)
|
||||
return candran.compile(candran.preprocess(code, options), options)
|
||||
end
|
||||
|
||||
function candran.searcher(modpath)
|
||||
-- get module filepath
|
||||
local notfound = ""
|
||||
local filepath
|
||||
for path in package.path:gsub("%.lua", ".can"):gmatch("[^;]+") do
|
||||
local path = path:gsub("%?", (modpath:gsub("%.", "/")))
|
||||
local f = io.open(path)
|
||||
if f then
|
||||
local errorRewritingActive = false
|
||||
local codeCache = {}
|
||||
--- Candran equivalent to the Lua 5.3's loadfile funtion.
|
||||
-- Will rewrite errors by default.
|
||||
function candran.loadfile(filepath, env, options)
|
||||
local f, err = io.open(filepath)
|
||||
if not f then error("can't open the file: "..err) end
|
||||
local content = f:read("*a")
|
||||
f:close()
|
||||
filepath = path
|
||||
|
||||
return candran.load(content, filepath, env, options)
|
||||
end
|
||||
|
||||
--- Candran equivalent to the Lua 5.3's load funtion.
|
||||
-- Will rewrite errors by default.
|
||||
function candran.load(chunk, chunkname, env, options={})
|
||||
options = util.merge({ chunkname = tostring(chunkname or chunk) }, options)
|
||||
|
||||
codeCache[options.chunkname] = candran.make(chunk, options)
|
||||
local f = util.load(codeCache[options.chunkname], options.chunkname, env)
|
||||
|
||||
if options.rewriteErrors == false then
|
||||
return f
|
||||
else
|
||||
notfound = notfound .. "\n\tno Candran file '"..path.."'"
|
||||
return function()
|
||||
if not errorRewritingActive then
|
||||
errorRewritingActive = true
|
||||
local t = { xpcall(f, candran.messageHandler) }
|
||||
errorRewritingActive = false
|
||||
if t[1] == false then
|
||||
error(t[2], 0)
|
||||
end
|
||||
return unpack(t, 2)
|
||||
else
|
||||
return f()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not filepath then return notfound end
|
||||
|
||||
-- open module file
|
||||
local f = io.open(filepath)
|
||||
if not f then error("Can't open the module file to import") end
|
||||
local modcontent = f:read("*a")
|
||||
f:close()
|
||||
--- Candran equivalent to the Lua 5.3's dofile funtion.
|
||||
-- Will rewrite errors by default.
|
||||
function candran.dofile(filename, options)
|
||||
return candran.loadfile(filename, nil, options)()
|
||||
end
|
||||
|
||||
return load(candran.make(modcontent))
|
||||
--- Candran error message handler.
|
||||
-- Use it in xpcall to rewrite stacktraces to display Candran source file lines instead of compiled Lua lines.
|
||||
function candran.messageHandler(message)
|
||||
return debug.traceback(message, 2):gsub("(\n?%s*)([^\n]-)%:(%d+)%:", function(indentation, source, line)
|
||||
line = tonumber(line)
|
||||
|
||||
local originalFile
|
||||
local strName = source:match("%[string \"(.-)\"%]")
|
||||
if strName then
|
||||
if codeCache[strName] then
|
||||
originalFile = codeCache[strName]
|
||||
source = strName
|
||||
end
|
||||
else
|
||||
local fi = io.open(source, "r")
|
||||
if fi then
|
||||
originalFile = fi:read("*a")
|
||||
end
|
||||
fi:close()
|
||||
end
|
||||
|
||||
if originalFile then
|
||||
local i = 0
|
||||
for l in originalFile:gmatch("([^\n]*)") do
|
||||
i = i +1
|
||||
if i == line then
|
||||
local extSource, lineMap = l:match("%-%- (.-)%:(%d+)$")
|
||||
if lineMap then
|
||||
if extSource ~= source then
|
||||
return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):"
|
||||
else
|
||||
return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):"
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Candran package searcher function. Use the existing package.path.
|
||||
function candran.searcher(modpath)
|
||||
local filepath = util.search(modpath)
|
||||
if not filepath then
|
||||
return "\n\tno candran file in package.path"
|
||||
end
|
||||
return candran.loadfile(filepath)
|
||||
end
|
||||
|
||||
--- Register the Candran package searcher.
|
||||
function candran.setup()
|
||||
if _VERSION == "Lua 5.1" then
|
||||
table.insert(package.loaders, 2, candran.searcher)
|
||||
else
|
||||
table.insert(package.searchers, 2, candran.searcher)
|
||||
end
|
||||
end
|
||||
|
||||
return candran
|
||||
|
|
|
|||
522
candran.lua
522
candran.lua
|
|
@ -15,7 +15,7 @@ local function _()
|
|||
end
|
||||
end
|
||||
end
|
||||
util["loadenv"] = function(str, name, env)
|
||||
util["load"] = function(str, name, env)
|
||||
if _VERSION == "Lua 5.1" then
|
||||
local fn, err = loadstring(str, name)
|
||||
if not fn then
|
||||
|
|
@ -23,22 +23,140 @@ local function _()
|
|||
end
|
||||
return env ~= nil and setfenv(fn, env) or fn
|
||||
else
|
||||
if env then
|
||||
return load(str, name, nil, env)
|
||||
else
|
||||
return load(str, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
util["merge"] = function(...)
|
||||
local r = {}
|
||||
for _, t in ipairs({ ... }) do
|
||||
for k, v in pairs(t) do
|
||||
r[k] = v
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
return util
|
||||
end
|
||||
local util = _() or util
|
||||
package["loaded"]["util"] = util or true
|
||||
package["loaded"]["lib.util"] = util or true
|
||||
local function _()
|
||||
return function(ast, opts)
|
||||
local options = {
|
||||
["indentation"] = "\9", ["newline"] = "\
|
||||
", ["requirePrefix"] = "CANDRAN_"
|
||||
}
|
||||
local ipairs, pairs, setfenv, tonumber, loadstring, type = ipairs, pairs, setfenv, tonumber, loadstring, type
|
||||
local tinsert, tconcat = table["insert"], table["concat"]
|
||||
local function commonerror(msg)
|
||||
return nil, ("[cmdline]: " .. msg)
|
||||
end
|
||||
local function argerror(msg, numarg)
|
||||
msg = msg and (": " .. msg) or ""
|
||||
return nil, ("[cmdline]: bad argument #" .. numarg .. msg)
|
||||
end
|
||||
local function iderror(numarg)
|
||||
return argerror("ID not valid", numarg)
|
||||
end
|
||||
local function idcheck(id)
|
||||
return id:match("^[%a_][%w_]*$") and true
|
||||
end
|
||||
return function(t_in, options, params)
|
||||
local t_out = {}
|
||||
for i, v in ipairs(t_in) do
|
||||
local prefix, command = v:sub(1, 1), v:sub(2)
|
||||
if prefix == "$" then
|
||||
tinsert(t_out, command)
|
||||
elseif prefix == "-" then
|
||||
for id in command:gmatch("[^,;]+") do
|
||||
if not idcheck(id) then
|
||||
return iderror(i)
|
||||
end
|
||||
t_out[id] = true
|
||||
end
|
||||
elseif prefix == "!" then
|
||||
local f, err = loadstring(command)
|
||||
if not f then
|
||||
return argerror(err, i)
|
||||
end
|
||||
setfenv(f, t_out)()
|
||||
elseif v:find("=") then
|
||||
local ids, val = v:match("^([^=]+)%=(.*)")
|
||||
if not ids then
|
||||
return argerror("invalid assignment syntax", i)
|
||||
end
|
||||
if val == "false" then
|
||||
val = false
|
||||
elseif val == "true" then
|
||||
val = true
|
||||
else
|
||||
val = val:sub(1, 1) == "$" and val:sub(2) or tonumber(val) or val
|
||||
end
|
||||
for id in ids:gmatch("[^,;]+") do
|
||||
if not idcheck(id) then
|
||||
return iderror(i)
|
||||
end
|
||||
t_out[id] = val
|
||||
end
|
||||
else
|
||||
tinsert(t_out, v)
|
||||
end
|
||||
end
|
||||
if options then
|
||||
local lookup, unknown = {}, {}
|
||||
for _, v in ipairs(options) do
|
||||
lookup[v] = true
|
||||
end
|
||||
for k, _ in pairs(t_out) do
|
||||
if lookup[k] == nil and type(k) == "string" then
|
||||
tinsert(unknown, k)
|
||||
end
|
||||
end
|
||||
if # unknown > 0 then
|
||||
return commonerror("unknown options: " .. tconcat(unknown, ", "))
|
||||
end
|
||||
end
|
||||
if params then
|
||||
local missing = {}
|
||||
for _, v in ipairs(params) do
|
||||
if t_out[v] == nil then
|
||||
tinsert(missing, v)
|
||||
end
|
||||
end
|
||||
if # missing > 0 then
|
||||
return commonerror("missing parameters: " .. tconcat(missing, ", "))
|
||||
end
|
||||
end
|
||||
return t_out
|
||||
end
|
||||
end
|
||||
local cmdline = _() or cmdline
|
||||
package["loaded"]["lib.cmdline"] = cmdline or true
|
||||
local function _()
|
||||
return function(code, ast, options)
|
||||
local lastInputPos = 1
|
||||
local prevLinePos = 1
|
||||
local lastSource = "nil"
|
||||
local lastLine = 1
|
||||
local indentLevel = 0
|
||||
local function newline()
|
||||
return options["newline"] .. string["rep"](options["indentation"], indentLevel)
|
||||
local r = options["newline"] .. string["rep"](options["indentation"], indentLevel)
|
||||
if options["mapLines"] then
|
||||
local sub = code:sub(lastInputPos)
|
||||
local source, line = sub:sub(1, sub:find("\
|
||||
")):match("%-%- (.-)%:(%d+)\
|
||||
")
|
||||
if source and line then
|
||||
lastSource = source
|
||||
lastLine = tonumber(line)
|
||||
else
|
||||
for _ in code:sub(prevLinePos, lastInputPos):gmatch("\
|
||||
") do
|
||||
lastLine = lastLine + 1
|
||||
end
|
||||
end
|
||||
prevLinePos = lastInputPos
|
||||
r = " -- " .. lastSource .. ":" .. lastLine .. r
|
||||
end
|
||||
return r
|
||||
end
|
||||
local function indent()
|
||||
indentLevel = indentLevel + 1
|
||||
|
|
@ -61,6 +179,9 @@ local function _()
|
|||
end
|
||||
local tags
|
||||
local function lua(ast, forceTag, ...)
|
||||
if options["mapLines"] and ast["pos"] then
|
||||
lastInputPos = ast["pos"]
|
||||
end
|
||||
return tags[forceTag or ast["tag"]](ast, ...)
|
||||
end
|
||||
tags = setmetatable({
|
||||
|
|
@ -78,13 +199,41 @@ local function _()
|
|||
end, ["Set"] = function(t)
|
||||
if # t == 2 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs")
|
||||
elseif # t == 3 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs")
|
||||
elseif # t == 4 then
|
||||
if t[3] == "=" then
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({
|
||||
t[2], t[1][1], t[4][1]
|
||||
}, "Op")
|
||||
for i = 2, math["min"](# t[4], # t[1]), 1 do
|
||||
r = r .. ", " .. lua({
|
||||
t[2], t[1][i], t[4][i]
|
||||
}, "Op")
|
||||
end
|
||||
return r
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({
|
||||
t[2], t[1][1], t[3][1]
|
||||
t[3], t[4][1], t[1][1]
|
||||
}, "Op")
|
||||
for i = 2, math["min"](# t[3], # t[1]), 1 do
|
||||
for i = 2, math["min"](# t[4], # t[1]), 1 do
|
||||
r = r .. ", " .. lua({
|
||||
t[2], t[1][i], t[3][i]
|
||||
t[3], t[4][i], t[1][i]
|
||||
}, "Op")
|
||||
end
|
||||
return r
|
||||
end
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({
|
||||
t[2], t[1][1], {
|
||||
["tag"] = "Op", t[4], t[5][1], t[1][1]
|
||||
}
|
||||
}, "Op")
|
||||
for i = 2, math["min"](# t[5], # t[1]), 1 do
|
||||
r = r .. ", " .. lua({
|
||||
t[2], t[1][i], {
|
||||
["tag"] = "Op", t[4], t[5][i], t[1][i]
|
||||
}
|
||||
}, "Op")
|
||||
end
|
||||
return r
|
||||
|
|
@ -223,27 +372,39 @@ local function _()
|
|||
}, { ["__index"] = function(self, key)
|
||||
error("don't know how to compile a " .. tostring(key) .. " to Lua 5.3")
|
||||
end })
|
||||
if opts then
|
||||
for k, v in pairs(opts) do
|
||||
options[k] = v
|
||||
end
|
||||
end
|
||||
local r = lua(ast)
|
||||
return requireStr .. r
|
||||
return requireStr .. lua(ast) .. newline()
|
||||
end
|
||||
end
|
||||
local lua53 = _() or lua53
|
||||
package["loaded"]["compiler.lua53"] = lua53 or true
|
||||
local function _()
|
||||
local function _()
|
||||
return function(ast, opts)
|
||||
local options = {
|
||||
["indentation"] = "\9", ["newline"] = "\
|
||||
", ["requirePrefix"] = "CANDRAN_"
|
||||
}
|
||||
return function(code, ast, options)
|
||||
local lastInputPos = 1
|
||||
local prevLinePos = 1
|
||||
local lastSource = "nil"
|
||||
local lastLine = 1
|
||||
local indentLevel = 0
|
||||
local function newline()
|
||||
return options["newline"] .. string["rep"](options["indentation"], indentLevel)
|
||||
local r = options["newline"] .. string["rep"](options["indentation"], indentLevel)
|
||||
if options["mapLines"] then
|
||||
local sub = code:sub(lastInputPos)
|
||||
local source, line = sub:sub(1, sub:find("\
|
||||
")):match("%-%- (.-)%:(%d+)\
|
||||
")
|
||||
if source and line then
|
||||
lastSource = source
|
||||
lastLine = tonumber(line)
|
||||
else
|
||||
for _ in code:sub(prevLinePos, lastInputPos):gmatch("\
|
||||
") do
|
||||
lastLine = lastLine + 1
|
||||
end
|
||||
end
|
||||
prevLinePos = lastInputPos
|
||||
r = " -- " .. lastSource .. ":" .. lastLine .. r
|
||||
end
|
||||
return r
|
||||
end
|
||||
local function indent()
|
||||
indentLevel = indentLevel + 1
|
||||
|
|
@ -266,6 +427,9 @@ local function _()
|
|||
end
|
||||
local tags
|
||||
local function lua(ast, forceTag, ...)
|
||||
if options["mapLines"] and ast["pos"] then
|
||||
lastInputPos = ast["pos"]
|
||||
end
|
||||
return tags[forceTag or ast["tag"]](ast, ...)
|
||||
end
|
||||
tags = setmetatable({
|
||||
|
|
@ -283,13 +447,41 @@ local function _()
|
|||
end, ["Set"] = function(t)
|
||||
if # t == 2 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs")
|
||||
elseif # t == 3 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs")
|
||||
elseif # t == 4 then
|
||||
if t[3] == "=" then
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({
|
||||
t[2], t[1][1], t[4][1]
|
||||
}, "Op")
|
||||
for i = 2, math["min"](# t[4], # t[1]), 1 do
|
||||
r = r .. ", " .. lua({
|
||||
t[2], t[1][i], t[4][i]
|
||||
}, "Op")
|
||||
end
|
||||
return r
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({
|
||||
t[2], t[1][1], t[3][1]
|
||||
t[3], t[4][1], t[1][1]
|
||||
}, "Op")
|
||||
for i = 2, math["min"](# t[3], # t[1]), 1 do
|
||||
for i = 2, math["min"](# t[4], # t[1]), 1 do
|
||||
r = r .. ", " .. lua({
|
||||
t[2], t[1][i], t[3][i]
|
||||
t[3], t[4][i], t[1][i]
|
||||
}, "Op")
|
||||
end
|
||||
return r
|
||||
end
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({
|
||||
t[2], t[1][1], {
|
||||
["tag"] = "Op", t[4], t[5][1], t[1][1]
|
||||
}
|
||||
}, "Op")
|
||||
for i = 2, math["min"](# t[5], # t[1]), 1 do
|
||||
r = r .. ", " .. lua({
|
||||
t[2], t[1][i], {
|
||||
["tag"] = "Op", t[4], t[5][i], t[1][i]
|
||||
}
|
||||
}, "Op")
|
||||
end
|
||||
return r
|
||||
|
|
@ -455,13 +647,7 @@ local function _()
|
|||
addRequire("bit", "bnot", "bnot")
|
||||
return getRequire("bnot") .. "(" .. lua(right) .. ")"
|
||||
end
|
||||
if opts then
|
||||
for k, v in pairs(opts) do
|
||||
options[k] = v
|
||||
end
|
||||
end
|
||||
local r = lua(ast)
|
||||
return requireStr .. r
|
||||
return requireStr .. lua(ast) .. newline()
|
||||
end
|
||||
end
|
||||
local lua53 = _() or lua53
|
||||
|
|
@ -538,9 +724,9 @@ local function _()
|
|||
return scope
|
||||
end
|
||||
local scope = _() or scope
|
||||
package["loaded"]["lua-parser.scope"] = scope or true
|
||||
package["loaded"]["lib.lua-parser.scope"] = scope or true
|
||||
local function _()
|
||||
local scope = require("lua-parser.scope")
|
||||
local scope = require("lib.lua-parser.scope")
|
||||
local lineno = scope["lineno"]
|
||||
local new_scope, end_scope = scope["new_scope"], scope["end_scope"]
|
||||
local new_function, end_function = scope["new_function"], scope["end_function"]
|
||||
|
|
@ -983,7 +1169,7 @@ local function _()
|
|||
}
|
||||
end
|
||||
local validator = _() or validator
|
||||
package["loaded"]["lua-parser.validator"] = validator or true
|
||||
package["loaded"]["lib.lua-parser.validator"] = validator or true
|
||||
local function _()
|
||||
local pp = {}
|
||||
local block2str, stm2str, exp2str, var2str
|
||||
|
|
@ -1308,7 +1494,7 @@ local function _()
|
|||
return pp
|
||||
end
|
||||
local pp = _() or pp
|
||||
package["loaded"]["lua-parser.pp"] = pp or true
|
||||
package["loaded"]["lib.lua-parser.pp"] = pp or true
|
||||
local function _()
|
||||
local lpeg = require("lpeglabel")
|
||||
lpeg["locale"](lpeg)
|
||||
|
|
@ -1570,13 +1756,22 @@ local function _()
|
|||
["tag"] = "Index", ["pos"] = t1["pos"], [1] = t1, [2] = t2[1]
|
||||
}
|
||||
end
|
||||
local function fixAnonymousMethodParams(t1, t2)
|
||||
if t1 == ":" then
|
||||
t1 = t2
|
||||
table["insert"](t1, 1, {
|
||||
["tag"] = "Id", "self"
|
||||
})
|
||||
end
|
||||
return t1
|
||||
end
|
||||
local G = {
|
||||
V("Lua"), ["Lua"] = V("Shebang") ^ - 1 * V("Skip") * V("Block") * expect(P(- 1), "Extra"), ["Shebang"] = P("#!") * (P(1) - P("\
|
||||
")) ^ 0, ["Block"] = tagC("Block", V("Stat") ^ 0 * V("RetStat") ^ - 1), ["Stat"] = V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat") + V("LocalStat") + V("FuncStat") + V("BreakStat") + V("LabelStat") + V("GoToStat") + V("FuncCall") + V("Assignment") + sym(";") + - V("BlockEnd") * throw("InvalidStat"), ["BlockEnd"] = P("return") + "end" + "elseif" + "else" + "until" + - 1, ["IfStat"] = tagC("If", V("IfPart") * V("ElseIfPart") ^ 0 * V("ElsePart") ^ - 1 * expect(kw("end"), "EndIf")), ["IfPart"] = kw("if") * expect(V("Expr"), "ExprIf") * expect(kw("then"), "ThenIf") * V("Block"), ["ElseIfPart"] = kw("elseif") * expect(V("Expr"), "ExprEIf") * expect(kw("then"), "ThenEIf") * V("Block"), ["ElsePart"] = kw("else") * V("Block"), ["DoStat"] = kw("do") * V("Block") * expect(kw("end"), "EndDo") / tagDo, ["WhileStat"] = tagC("While", kw("while") * expect(V("Expr"), "ExprWhile") * V("WhileBody")), ["WhileBody"] = expect(kw("do"), "DoWhile") * V("Block") * expect(kw("end"), "EndWhile"), ["RepeatStat"] = tagC("Repeat", kw("repeat") * V("Block") * expect(kw("until"), "UntilRep") * expect(V("Expr"), "ExprRep")), ["ForStat"] = kw("for") * expect(V("ForNum") + V("ForIn"), "ForRange") * expect(kw("end"), "EndFor"), ["ForNum"] = tagC("Fornum", V("Id") * sym("=") * V("NumRange") * V("ForBody")), ["NumRange"] = expect(V("Expr"), "ExprFor1") * expect(sym(","), "CommaFor") * expect(V("Expr"), "ExprFor2") * (sym(",") * expect(V("Expr"), "ExprFor3")) ^ - 1, ["ForIn"] = tagC("Forin", V("NameList") * expect(kw("in"), "InFor") * expect(V("ExprList"), "EListFor") * V("ForBody")), ["ForBody"] = expect(kw("do"), "DoFor") * V("Block"), ["LocalStat"] = kw("local") * expect(V("LocalFunc") + V("LocalAssign"), "DefLocal"), ["LocalFunc"] = tagC("Localrec", kw("function") * expect(V("Id"), "NameLFunc") * V("FuncBody")) / fixFuncStat, ["LocalAssign"] = tagC("Local", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))), ["Assignment"] = tagC("Set", V("VarList") * V("AssignmentOp") * expect(V("ExprList"), "EListAssign")), ["FuncStat"] = tagC("Set", kw("function") * expect(V("FuncName"), "FuncName") * V("FuncBody")) / fixFuncStat, ["FuncName"] = Cf(V("Id") * (sym(".") * expect(V("StrId"), "NameFunc1")) ^ 0, insertIndex) * (sym(":") * expect(V("StrId"), "NameFunc2")) ^ - 1 / markMethod, ["FuncBody"] = tagC("Function", V("FuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["FuncParams"] = expect(sym("("), "OParenPList") * V("ParList") * expect(sym(")"), "CParenPList"), ["ParList"] = V("NamedParList") * (sym(",") * expect(tagC("Dots", sym("...")), "ParList")) ^ - 1 / addDots + Ct(tagC("Dots", sym("..."))) + Ct(Cc()), ["NamedParList"] = tagC("NamedParList", commaSep(V("NamedPar"))), ["NamedPar"] = tagC("ParPair", V("ParKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Id"), ["ParKey"] = V("Id") * # ("=" * - P("=")), ["LabelStat"] = tagC("Label", sym("::") * expect(V("Name"), "Label") * expect(sym("::"), "CloseLabel")), ["GoToStat"] = tagC("Goto", kw("goto") * expect(V("Name"), "Goto")), ["BreakStat"] = tagC("Break", kw("break")), ["RetStat"] = tagC("Return", kw("return") * commaSep(V("Expr"), "RetList") ^ - 1 * sym(";") ^ - 1), ["NameList"] = tagC("NameList", commaSep(V("Id"))), ["VarList"] = tagC("VarList", commaSep(V("VarExpr"), "VarList")), ["ExprList"] = tagC("ExpList", commaSep(V("Expr"), "ExprList")), ["Expr"] = V("OrExpr"), ["OrExpr"] = chainOp(V("AndExpr"), V("OrOp"), "OrExpr"), ["AndExpr"] = chainOp(V("RelExpr"), V("AndOp"), "AndExpr"), ["RelExpr"] = chainOp(V("BOrExpr"), V("RelOp"), "RelExpr"), ["BOrExpr"] = chainOp(V("BXorExpr"), V("BOrOp"), "BOrExpr"), ["BXorExpr"] = chainOp(V("BAndExpr"), V("BXorOp"), "BXorExpr"), ["BAndExpr"] = chainOp(V("ShiftExpr"), V("BAndOp"), "BAndExpr"), ["ShiftExpr"] = chainOp(V("ConcatExpr"), V("ShiftOp"), "ShiftExpr"), ["ConcatExpr"] = V("AddExpr") * (V("ConcatOp") * expect(V("ConcatExpr"), "ConcatExpr")) ^ - 1 / binaryOp, ["AddExpr"] = chainOp(V("MulExpr"), V("AddOp"), "AddExpr"), ["MulExpr"] = chainOp(V("UnaryExpr"), V("MulOp"), "MulExpr"), ["UnaryExpr"] = V("UnaryOp") * expect(V("UnaryExpr"), "UnaryExpr") / unaryOp + V("PowExpr"), ["PowExpr"] = V("SimpleExpr") * (V("PowOp") * expect(V("UnaryExpr"), "PowExpr")) ^ - 1 / binaryOp, ["SimpleExpr"] = tagC("Number", V("Number")) + tagC("String", V("String")) + tagC("Nil", kw("nil")) + tagC("Boolean", kw("false") * Cc(false)) + tagC("Boolean", kw("true") * Cc(true)) + tagC("Dots", sym("...")) + V("FuncDef") + V("Table") + V("SuffixedExpr"), ["FuncCall"] = Cmt(V("SuffixedExpr"), function(s, i, exp)
|
||||
")) ^ 0, ["Block"] = tagC("Block", V("Stat") ^ 0 * V("RetStat") ^ - 1), ["Stat"] = V("IfStat") + V("DoStat") + V("WhileStat") + V("RepeatStat") + V("ForStat") + V("LocalStat") + V("FuncStat") + V("BreakStat") + V("LabelStat") + V("GoToStat") + V("FuncCall") + V("Assignment") + sym(";") + - V("BlockEnd") * throw("InvalidStat"), ["BlockEnd"] = P("return") + "end" + "elseif" + "else" + "until" + - 1, ["IfStat"] = tagC("If", V("IfPart") * V("ElseIfPart") ^ 0 * V("ElsePart") ^ - 1 * expect(kw("end"), "EndIf")), ["IfPart"] = kw("if") * expect(V("Expr"), "ExprIf") * expect(kw("then"), "ThenIf") * V("Block"), ["ElseIfPart"] = kw("elseif") * expect(V("Expr"), "ExprEIf") * expect(kw("then"), "ThenEIf") * V("Block"), ["ElsePart"] = kw("else") * V("Block"), ["DoStat"] = kw("do") * V("Block") * expect(kw("end"), "EndDo") / tagDo, ["WhileStat"] = tagC("While", kw("while") * expect(V("Expr"), "ExprWhile") * V("WhileBody")), ["WhileBody"] = expect(kw("do"), "DoWhile") * V("Block") * expect(kw("end"), "EndWhile"), ["RepeatStat"] = tagC("Repeat", kw("repeat") * V("Block") * expect(kw("until"), "UntilRep") * expect(V("Expr"), "ExprRep")), ["ForStat"] = kw("for") * expect(V("ForNum") + V("ForIn"), "ForRange") * expect(kw("end"), "EndFor"), ["ForNum"] = tagC("Fornum", V("Id") * sym("=") * V("NumRange") * V("ForBody")), ["NumRange"] = expect(V("Expr"), "ExprFor1") * expect(sym(","), "CommaFor") * expect(V("Expr"), "ExprFor2") * (sym(",") * expect(V("Expr"), "ExprFor3")) ^ - 1, ["ForIn"] = tagC("Forin", V("NameList") * expect(kw("in"), "InFor") * expect(V("ExprList"), "EListFor") * V("ForBody")), ["ForBody"] = expect(kw("do"), "DoFor") * V("Block"), ["LocalStat"] = kw("local") * expect(V("LocalFunc") + V("LocalAssign"), "DefLocal"), ["LocalFunc"] = tagC("Localrec", kw("function") * expect(V("Id"), "NameLFunc") * V("FuncBody")) / fixFuncStat, ["LocalAssign"] = tagC("Local", V("NameList") * (sym("=") * expect(V("ExprList"), "EListLAssign") + Ct(Cc()))), ["Assignment"] = tagC("Set", V("VarList") * V("BinOp") ^ - 1 * (sym("=") / "=") * V("BinOp") ^ - 1 * expect(V("ExprList"), "EListAssign")), ["FuncStat"] = tagC("Set", kw("function") * expect(V("FuncName"), "FuncName") * V("FuncBody")) / fixFuncStat, ["FuncName"] = Cf(V("Id") * (sym(".") * expect(V("StrId"), "NameFunc1")) ^ 0, insertIndex) * (sym(":") * expect(V("StrId"), "NameFunc2")) ^ - 1 / markMethod, ["FuncBody"] = tagC("Function", V("FuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["FuncParams"] = expect(sym("("), "OParenPList") * V("ParList") * expect(sym(")"), "CParenPList"), ["ParList"] = V("NamedParList") * (sym(",") * expect(tagC("Dots", sym("...")), "ParList")) ^ - 1 / addDots + Ct(tagC("Dots", sym("..."))) + Ct(Cc()), ["NamedParList"] = tagC("NamedParList", commaSep(V("NamedPar"))), ["NamedPar"] = tagC("ParPair", V("ParKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Id"), ["ParKey"] = V("Id") * # ("=" * - P("=")), ["LabelStat"] = tagC("Label", sym("::") * expect(V("Name"), "Label") * expect(sym("::"), "CloseLabel")), ["GoToStat"] = tagC("Goto", kw("goto") * expect(V("Name"), "Goto")), ["BreakStat"] = tagC("Break", kw("break")), ["RetStat"] = tagC("Return", kw("return") * commaSep(V("Expr"), "RetList") ^ - 1 * sym(";") ^ - 1), ["NameList"] = tagC("NameList", commaSep(V("Id"))), ["VarList"] = tagC("VarList", commaSep(V("VarExpr"), "VarList")), ["ExprList"] = tagC("ExpList", commaSep(V("Expr"), "ExprList")), ["Expr"] = V("OrExpr"), ["OrExpr"] = chainOp(V("AndExpr"), V("OrOp"), "OrExpr"), ["AndExpr"] = chainOp(V("RelExpr"), V("AndOp"), "AndExpr"), ["RelExpr"] = chainOp(V("BOrExpr"), V("RelOp"), "RelExpr"), ["BOrExpr"] = chainOp(V("BXorExpr"), V("BOrOp"), "BOrExpr"), ["BXorExpr"] = chainOp(V("BAndExpr"), V("BXorOp"), "BXorExpr"), ["BAndExpr"] = chainOp(V("ShiftExpr"), V("BAndOp"), "BAndExpr"), ["ShiftExpr"] = chainOp(V("ConcatExpr"), V("ShiftOp"), "ShiftExpr"), ["ConcatExpr"] = V("AddExpr") * (V("ConcatOp") * expect(V("ConcatExpr"), "ConcatExpr")) ^ - 1 / binaryOp, ["AddExpr"] = chainOp(V("MulExpr"), V("AddOp"), "AddExpr"), ["MulExpr"] = chainOp(V("UnaryExpr"), V("MulOp"), "MulExpr"), ["UnaryExpr"] = V("UnaryOp") * expect(V("UnaryExpr"), "UnaryExpr") / unaryOp + V("PowExpr"), ["PowExpr"] = V("SimpleExpr") * (V("PowOp") * expect(V("UnaryExpr"), "PowExpr")) ^ - 1 / binaryOp, ["SimpleExpr"] = tagC("Number", V("Number")) + tagC("String", V("String")) + tagC("Nil", kw("nil")) + tagC("Boolean", kw("false") * Cc(false)) + tagC("Boolean", kw("true") * Cc(true)) + tagC("Dots", sym("...")) + V("FuncDef") + V("Table") + V("SuffixedExpr"), ["FuncCall"] = Cmt(V("SuffixedExpr"), function(s, i, exp)
|
||||
return exp["tag"] == "Call" or exp["tag"] == "Invoke", exp
|
||||
end), ["VarExpr"] = Cmt(V("SuffixedExpr"), function(s, i, exp)
|
||||
return exp["tag"] == "Id" or exp["tag"] == "Index", exp
|
||||
end), ["SuffixedExpr"] = Cf(V("PrimaryExpr") * (V("Index") + V("Call")) ^ 0, makeIndexOrCall), ["PrimaryExpr"] = V("Id") + tagC("Paren", sym("(") * expect(V("Expr"), "ExprParen") * expect(sym(")"), "CParenExpr")), ["Index"] = tagC("DotIndex", sym("." * - P(".")) * expect(V("StrId"), "NameIndex")) + tagC("ArrayIndex", sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprIndex") * expect(sym("]"), "CBracketIndex")), ["Call"] = tagC("Invoke", Cg(sym(":" * - P(":")) * expect(V("StrId"), "NameMeth") * expect(V("FuncArgs"), "MethArgs"))) + tagC("Call", V("FuncArgs")), ["FuncDef"] = kw("function") * V("FuncBody"), ["FuncArgs"] = sym("(") * commaSep(V("Expr"), "ArgList") ^ - 1 * expect(sym(")"), "CParenArgs") + V("Table") + tagC("String", V("String")), ["Table"] = tagC("Table", sym("{") * V("FieldList") ^ - 1 * expect(sym("}"), "CBraceTable")), ["FieldList"] = sepBy(V("Field"), V("FieldSep")) * V("FieldSep") ^ - 1, ["Field"] = tagC("Pair", V("FieldKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Expr"), ["FieldKey"] = sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprFKey") * expect(sym("]"), "CBracketFKey") + V("StrId") * # ("=" * - P("=")), ["FieldSep"] = sym(",") + sym(";"), ["Id"] = tagC("Id", V("Name")), ["StrId"] = tagC("String", V("Name")), ["Skip"] = (V("Space") + V("Comment")) ^ 0, ["Space"] = space ^ 1, ["Comment"] = P("--") * V("LongStr") / function()
|
||||
end), ["SuffixedExpr"] = Cf(V("PrimaryExpr") * (V("Index") + V("Call")) ^ 0, makeIndexOrCall), ["PrimaryExpr"] = V("SelfId") * (V("SelfCall") + V("SelfIndex")) + V("Id") + tagC("Paren", sym("(") * expect(V("Expr"), "ExprParen") * expect(sym(")"), "CParenExpr")), ["Index"] = tagC("DotIndex", sym("." * - P(".")) * expect(V("StrId"), "NameIndex")) + tagC("ArrayIndex", sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprIndex") * expect(sym("]"), "CBracketIndex")), ["Call"] = tagC("Invoke", Cg(sym(":" * - P(":")) * expect(V("StrId"), "NameMeth") * expect(V("FuncArgs"), "MethArgs"))) + tagC("Call", V("FuncArgs")), ["SelfIndex"] = tagC("DotIndex", V("StrId")), ["SelfCall"] = tagC("Invoke", Cg(V("StrId") * V("FuncArgs"))), ["ShortFuncDef"] = tagC("Function", V("ShortFuncParams") * V("Block") * expect(kw("end"), "EndFunc")), ["ShortFuncParams"] = (sym(":") / ":") ^ - 1 * sym("(") * V("ParList") * sym(")") / fixAnonymousMethodParams, ["FuncDef"] = (kw("function") * V("FuncBody")) + V("ShortFuncDef"), ["FuncArgs"] = sym("(") * commaSep(V("Expr"), "ArgList") ^ - 1 * expect(sym(")"), "CParenArgs") + V("Table") + tagC("String", V("String")), ["Table"] = tagC("Table", sym("{") * V("FieldList") ^ - 1 * expect(sym("}"), "CBraceTable")), ["FieldList"] = sepBy(V("Field"), V("FieldSep")) * V("FieldSep") ^ - 1, ["Field"] = tagC("Pair", V("FieldKey") * expect(sym("="), "EqField") * expect(V("Expr"), "ExprField")) + V("Expr"), ["FieldKey"] = sym("[" * - P(S("=["))) * expect(V("Expr"), "ExprFKey") * expect(sym("]"), "CBracketFKey") + V("StrId") * # ("=" * - P("=")), ["FieldSep"] = sym(",") + sym(";"), ["SelfId"] = tagC("Id", sym("@") / "self"), ["Id"] = tagC("Id", V("Name")) + V("SelfId"), ["StrId"] = tagC("String", V("Name")), ["Skip"] = (V("Space") + V("Comment")) ^ 0, ["Space"] = space ^ 1, ["Comment"] = P("--") * V("LongStr") / function()
|
||||
return
|
||||
end + P("--") * (P(1) - P("\
|
||||
")) ^ 0, ["Name"] = token(- V("Reserved") * C(V("Ident"))), ["Reserved"] = V("Keywords") * - V("IdRest"), ["Keywords"] = P("and") + "break" + "do" + "elseif" + "else" + "end" + "false" + "for" + "function" + "goto" + "if" + "in" + "local" + "nil" + "not" + "or" + "repeat" + "return" + "then" + "true" + "until" + "while", ["Ident"] = V("IdStart") * V("IdRest") ^ 0, ["IdStart"] = alpha + P("_"), ["IdRest"] = alnum + P("_"), ["Number"] = token((V("Hex") + V("Float") + V("Int")) / tonumber), ["Hex"] = (P("0x") + "0X") * expect(xdigit ^ 1, "DigitHex"), ["Float"] = V("Decimal") * V("Expo") ^ - 1 + V("Int") * V("Expo"), ["Decimal"] = digit ^ 1 * "." * digit ^ 0 + P(".") * - P(".") * expect(digit ^ 1, "DigitDeci"), ["Expo"] = S("eE") * S("+-") ^ - 1 * expect(digit ^ 1, "DigitExpo"), ["Int"] = digit ^ 1, ["String"] = token(V("ShortStr") + V("LongStr")), ["ShortStr"] = P("\"") * Cs((V("EscSeq") + (P(1) - S("\"\
|
||||
|
|
@ -1590,10 +1785,10 @@ local function _()
|
|||
end, ["Open"] = "[" * Cg(V("Equals"), "openEq") * "[" * P("\
|
||||
") ^ - 1, ["Close"] = "]" * C(V("Equals")) * "]", ["Equals"] = P("=") ^ 0, ["CloseEq"] = Cmt(V("Close") * Cb("openEq"), function(s, i, closeEq, openEq)
|
||||
return # openEq == # closeEq
|
||||
end), ["OrOp"] = kw("or") / "or", ["AndOp"] = kw("and") / "and", ["RelOp"] = sym("~=") / "ne" + sym("==") / "eq" + sym("<=") / "le" + sym(">=") / "ge" + sym("<") / "lt" + sym(">") / "gt", ["BOrOp"] = sym("|") / "bor", ["BXorOp"] = sym("~" * - P("=")) / "bxor", ["BAndOp"] = sym("&") / "band", ["ShiftOp"] = sym("<<") / "shl" + sym(">>") / "shr", ["ConcatOp"] = sym("..") / "concat", ["AddOp"] = sym("+") / "add" + sym("-") / "sub", ["MulOp"] = sym("*") / "mul" + sym("//") / "idiv" + sym("/") / "div" + sym("%") / "mod", ["UnaryOp"] = kw("not") / "not" + sym("-") / "unm" + sym("#") / "len" + sym("~") / "bnot", ["PowOp"] = sym("^") / "pow", ["AssignmentOp"] = (V("OrOp") + V("AndOp") + V("BOrOp") + V("BXorOp") + V("BAndOp") + V("ShiftOp") + V("ConcatOp") + V("AddOp") + V("MulOp") + V("PowOp")) ^ - 1 * sym("=")
|
||||
end), ["OrOp"] = kw("or") / "or", ["AndOp"] = kw("and") / "and", ["RelOp"] = sym("~=") / "ne" + sym("==") / "eq" + sym("<=") / "le" + sym(">=") / "ge" + sym("<") / "lt" + sym(">") / "gt", ["BOrOp"] = sym("|") / "bor", ["BXorOp"] = sym("~" * - P("=")) / "bxor", ["BAndOp"] = sym("&") / "band", ["ShiftOp"] = sym("<<") / "shl" + sym(">>") / "shr", ["ConcatOp"] = sym("..") / "concat", ["AddOp"] = sym("+") / "add" + sym("-") / "sub", ["MulOp"] = sym("*") / "mul" + sym("//") / "idiv" + sym("/") / "div" + sym("%") / "mod", ["UnaryOp"] = kw("not") / "not" + sym("-") / "unm" + sym("#") / "len" + sym("~") / "bnot", ["PowOp"] = sym("^") / "pow", ["BinOp"] = V("OrOp") + V("AndOp") + V("BOrOp") + V("BXorOp") + V("BAndOp") + V("ShiftOp") + V("ConcatOp") + V("AddOp") + V("MulOp") + V("PowOp")
|
||||
}
|
||||
local parser = {}
|
||||
local validator = require("lua-parser.validator")
|
||||
local validator = require("lib.lua-parser.validator")
|
||||
local validate = validator["validate"]
|
||||
local syntaxerror = validator["syntaxerror"]
|
||||
parser["parse"] = function(subject, filename)
|
||||
|
|
@ -1612,126 +1807,44 @@ local function _()
|
|||
return parser
|
||||
end
|
||||
local parser = _() or parser
|
||||
package["loaded"]["lua-parser.parser"] = parser or true
|
||||
local function _()
|
||||
local ipairs, pairs, setfenv, tonumber, loadstring, type = ipairs, pairs, setfenv, tonumber, loadstring, type
|
||||
local tinsert, tconcat = table["insert"], table["concat"]
|
||||
local function commonerror(msg)
|
||||
return nil, ("[cmdline]: " .. msg)
|
||||
end
|
||||
local function argerror(msg, numarg)
|
||||
msg = msg and (": " .. msg) or ""
|
||||
return nil, ("[cmdline]: bad argument #" .. numarg .. msg)
|
||||
end
|
||||
local function iderror(numarg)
|
||||
return argerror("ID not valid", numarg)
|
||||
end
|
||||
local function idcheck(id)
|
||||
return id:match("^[%a_][%w_]*$") and true
|
||||
end
|
||||
return function(t_in, options, params)
|
||||
local t_out = {}
|
||||
for i, v in ipairs(t_in) do
|
||||
local prefix, command = v:sub(1, 1), v:sub(2)
|
||||
if prefix == "$" then
|
||||
tinsert(t_out, command)
|
||||
elseif prefix == "-" then
|
||||
for id in command:gmatch("[^,;]+") do
|
||||
if not idcheck(id) then
|
||||
return iderror(i)
|
||||
end
|
||||
t_out[id] = true
|
||||
end
|
||||
elseif prefix == "!" then
|
||||
local f, err = loadstring(command)
|
||||
if not f then
|
||||
return argerror(err, i)
|
||||
end
|
||||
setfenv(f, t_out)()
|
||||
elseif v:find("=") then
|
||||
local ids, val = v:match("^([^=]+)%=(.*)")
|
||||
if not ids then
|
||||
return argerror("invalid assignment syntax", i)
|
||||
end
|
||||
val = val:sub(1, 1) == "$" and val:sub(2) or tonumber(val) or val
|
||||
for id in ids:gmatch("[^,;]+") do
|
||||
if not idcheck(id) then
|
||||
return iderror(i)
|
||||
end
|
||||
t_out[id] = val
|
||||
end
|
||||
else
|
||||
tinsert(t_out, v)
|
||||
end
|
||||
end
|
||||
if options then
|
||||
local lookup, unknown = {}, {}
|
||||
for _, v in ipairs(options) do
|
||||
lookup[v] = true
|
||||
end
|
||||
for k, _ in pairs(t_out) do
|
||||
if lookup[k] == nil and type(k) == "string" then
|
||||
tinsert(unknown, k)
|
||||
end
|
||||
end
|
||||
if # unknown > 0 then
|
||||
return commonerror("unknown options: " .. tconcat(unknown, ", "))
|
||||
end
|
||||
end
|
||||
if params then
|
||||
local missing = {}
|
||||
for _, v in ipairs(params) do
|
||||
if t_out[v] == nil then
|
||||
tinsert(missing, v)
|
||||
end
|
||||
end
|
||||
if # missing > 0 then
|
||||
return commonerror("missing parameters: " .. tconcat(missing, ", "))
|
||||
end
|
||||
end
|
||||
return t_out
|
||||
end
|
||||
end
|
||||
local cmdline = _() or cmdline
|
||||
package["loaded"]["cmdline"] = cmdline or true
|
||||
local util = require("util")
|
||||
local candran = { ["VERSION"] = "0.2.0" }
|
||||
candran["preprocess"] = function(input, args)
|
||||
if args == nil then args = {} end
|
||||
package["loaded"]["lib.lua-parser.parser"] = parser or true
|
||||
local candran = { ["VERSION"] = "0.3.0" }
|
||||
local default = {
|
||||
["target"] = "lua53", ["indentation"] = "", ["newline"] = "\
|
||||
", ["requirePrefix"] = "CANDRAN_", ["mapLines"] = true, ["chunkname"] = "nil", ["rewriteErrors"] = true
|
||||
}
|
||||
candran["preprocess"] = function(input, options)
|
||||
if options == nil then options = {} end
|
||||
options = util["merge"](default, options)
|
||||
local preprocessor = ""
|
||||
local i = 0
|
||||
for line in (input .. "\
|
||||
"):gmatch("(.-\
|
||||
)") do
|
||||
i = i + 1
|
||||
if line:match("^%s*#") and not line:match("^#!") then
|
||||
preprocessor = preprocessor .. line:gsub("^%s*#", "")
|
||||
elseif options["mapLines"] then
|
||||
preprocessor = preprocessor .. ("write(%q)"):format(line:sub(1, - 2) .. " -- " .. options["chunkname"] .. ":" .. i) .. "\
|
||||
"
|
||||
else
|
||||
preprocessor = preprocessor .. ("write(%q)"):format(line:sub(1, - 2)) .. "\
|
||||
"
|
||||
end
|
||||
end
|
||||
preprocessor = preprocessor .. "return output"
|
||||
local env = {}
|
||||
for k, v in pairs(_G) do
|
||||
env[k] = v
|
||||
end
|
||||
for k, v in pairs(args) do
|
||||
env[k] = v
|
||||
end
|
||||
local env = util["merge"](_G, options)
|
||||
env["candran"] = candran
|
||||
env["output"] = ""
|
||||
env["import"] = function(modpath, margs, autoRequire)
|
||||
if margs == nil then margs = args end
|
||||
if margs == nil then margs = {} end
|
||||
if autoRequire == nil then autoRequire = true end
|
||||
local filepath = assert(util["search"](modpath), "No module named \"" .. modpath .. "\"")
|
||||
local f = io["open"](filepath)
|
||||
if not f then
|
||||
error("Can't open the module file to import")
|
||||
end
|
||||
for k, v in pairs(args) do
|
||||
if margs[k] == nil then
|
||||
margs[k] = v
|
||||
end
|
||||
end
|
||||
margs = util["merge"](options, { ["chunkname"] = filepath }, margs)
|
||||
local modcontent = candran["preprocess"](f:read("*a"), margs)
|
||||
f:close()
|
||||
local modname = modpath:match("[^%.]+$")
|
||||
|
|
@ -1760,54 +1873,117 @@ candran["preprocess"] = function(input, args)
|
|||
env["write"](env[name])
|
||||
end
|
||||
end
|
||||
local preprocess, err = util["loadenv"](candran["compile"](preprocessor, args["target"]), "candran preprocessor", env)
|
||||
local preprocess, err = util["load"](candran["compile"](preprocessor, args), "candran preprocessor", env)
|
||||
if not preprocess then
|
||||
error("Error while creating Candran preprocessor: " .. err)
|
||||
end
|
||||
local success, output = pcall(preprocess)
|
||||
if not success then
|
||||
error("Error while preprocessing file: " .. output .. "\
|
||||
With preprocessor : \
|
||||
" .. preprocessor)
|
||||
error("Error while preprocessing file: " .. output)
|
||||
end
|
||||
return output
|
||||
end
|
||||
candran["compile"] = function(input, target)
|
||||
if target == nil then target = "lua53" end
|
||||
local parse = require("lua-parser.parser")["parse"]
|
||||
local ast, errmsg = parse(input, "candran")
|
||||
candran["compile"] = function(input, options)
|
||||
if options == nil then options = {} end
|
||||
options = util["merge"](default, options)
|
||||
local ast, errmsg = parser["parse"](input, "candran")
|
||||
if not ast then
|
||||
error("Compiler: error while parsing file: " .. errmsg)
|
||||
end
|
||||
return require("compiler." .. target)(ast)
|
||||
return require("compiler." .. options["target"])(input, ast, options)
|
||||
end
|
||||
candran["make"] = function(code, args)
|
||||
if args == nil then args = {} end
|
||||
return candran["compile"](candran["preprocess"](code, args), args["target"])
|
||||
candran["make"] = function(code, options)
|
||||
return candran["compile"](candran["preprocess"](code, options), options)
|
||||
end
|
||||
local errorRewritingActive = false
|
||||
local codeCache = {}
|
||||
candran["loadfile"] = function(filepath, env, options)
|
||||
local f, err = io["open"](filepath)
|
||||
if not f then
|
||||
error("can't open the file: " .. err)
|
||||
end
|
||||
local content = f:read("*a")
|
||||
f:close()
|
||||
return candran["load"](content, filepath, env, options)
|
||||
end
|
||||
candran["load"] = function(chunk, chunkname, env, options)
|
||||
if options == nil then options = {} end
|
||||
options = util["merge"]({ ["chunkname"] = tostring(chunkname or chunk) }, options)
|
||||
codeCache[options["chunkname"]] = candran["make"](chunk, options)
|
||||
local f = util["load"](codeCache[options["chunkname"]], options["chunkname"], env)
|
||||
if options["rewriteErrors"] == false then
|
||||
return f
|
||||
else
|
||||
return function()
|
||||
if not errorRewritingActive then
|
||||
errorRewritingActive = true
|
||||
local t = { xpcall(f, candran["messageHandler"]) }
|
||||
errorRewritingActive = false
|
||||
if t[1] == false then
|
||||
error(t[2], 0)
|
||||
end
|
||||
return unpack(t, 2)
|
||||
else
|
||||
return f()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
candran["dofile"] = function(filename, options)
|
||||
return candran["loadfile"](filename, nil, options)()
|
||||
end
|
||||
candran["messageHandler"] = function(message)
|
||||
return debug["traceback"](message, 2):gsub("(\
|
||||
?%s*)([^\
|
||||
]-)%:(%d+)%:", function(indentation, source, line)
|
||||
line = tonumber(line)
|
||||
local originalFile
|
||||
local strName = source:match("%[string \"(.-)\"%]")
|
||||
if strName then
|
||||
if codeCache[strName] then
|
||||
originalFile = codeCache[strName]
|
||||
source = strName
|
||||
end
|
||||
else
|
||||
local fi = io["open"](source, "r")
|
||||
if fi then
|
||||
originalFile = fi:read("*a")
|
||||
end
|
||||
fi:close()
|
||||
end
|
||||
if originalFile then
|
||||
local i = 0
|
||||
for l in originalFile:gmatch("([^\
|
||||
]*)") do
|
||||
i = i + 1
|
||||
if i == line then
|
||||
local extSource, lineMap = l:match("%-%- (.-)%:(%d+)$")
|
||||
if lineMap then
|
||||
if extSource ~= source then
|
||||
return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):"
|
||||
else
|
||||
return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):"
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
candran["searcher"] = function(modpath)
|
||||
local notfound = ""
|
||||
local filepath
|
||||
for path in package["path"]:gsub("%.lua", ".can"):gmatch("[^;]+") do
|
||||
local path = path:gsub("%?", (modpath:gsub("%.", "/")))
|
||||
local f = io["open"](path)
|
||||
if f then
|
||||
f:close()
|
||||
filepath = path
|
||||
else
|
||||
notfound = notfound .. "\
|
||||
\9no Candran file '" .. path .. "'"
|
||||
end
|
||||
end
|
||||
local filepath = util["search"](modpath)
|
||||
if not filepath then
|
||||
return notfound
|
||||
return "\
|
||||
\9no candran file in package.path"
|
||||
end
|
||||
local f = io["open"](filepath)
|
||||
if not f then
|
||||
error("Can't open the module file to import")
|
||||
return candran["loadfile"](filepath)
|
||||
end
|
||||
candran["setup"] = function()
|
||||
if _VERSION == "Lua 5.1" then
|
||||
table["insert"](package["loaders"], 2, candran["searcher"])
|
||||
else
|
||||
table["insert"](package["searchers"], 2, candran["searcher"])
|
||||
end
|
||||
local modcontent = f:read("*a")
|
||||
f:close()
|
||||
return load(candran["make"](modcontent))
|
||||
end
|
||||
return candran
|
||||
|
|
@ -1,13 +1,30 @@
|
|||
return function(ast, opts)
|
||||
local options = {
|
||||
indentation = "\t",
|
||||
newline = "\n",
|
||||
requirePrefix = "CANDRAN_"
|
||||
}
|
||||
return function(code, ast, options)
|
||||
local lastInputPos = 1
|
||||
local prevLinePos = 1
|
||||
local lastSource = "nil"
|
||||
local lastLine = 1
|
||||
|
||||
local indentLevel = 0
|
||||
local function newline()
|
||||
return options.newline .. string.rep(options.indentation, indentLevel)
|
||||
local r = options.newline .. string.rep(options.indentation, indentLevel)
|
||||
if options.mapLines then
|
||||
local sub = code:sub(lastInputPos)
|
||||
local source, line = sub:sub(1, sub:find("\n")):match("%-%- (.-)%:(%d+)\n")
|
||||
|
||||
if source and line then
|
||||
lastSource = source
|
||||
lastLine = tonumber(line)
|
||||
else
|
||||
for _ in code:sub(prevLinePos, lastInputPos):gmatch("\n") do
|
||||
lastLine += 1
|
||||
end
|
||||
end
|
||||
|
||||
prevLinePos = lastInputPos
|
||||
|
||||
r = " -- " .. lastSource .. ":" .. lastLine .. r
|
||||
end
|
||||
return r
|
||||
end
|
||||
local function indent()
|
||||
indentLevel += 1
|
||||
|
|
@ -32,6 +49,9 @@ return function(ast, opts)
|
|||
|
||||
local tags
|
||||
local function lua(ast, forceTag, ...)
|
||||
if options.mapLines and ast.pos then
|
||||
lastInputPos = ast.pos
|
||||
end
|
||||
return tags[forceTag or ast.tag](ast, ...)
|
||||
end
|
||||
|
||||
|
|
@ -54,14 +74,30 @@ return function(ast, opts)
|
|||
Do = function(t)
|
||||
return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end"
|
||||
end,
|
||||
-- Set{ {lhs+} opid? {expr+} }
|
||||
-- Set{ {lhs+} (opid? = opid?)? {expr+} }
|
||||
Set = function(t)
|
||||
if #t == 2 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs")
|
||||
elseif #t == 3 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs")
|
||||
elseif #t == 4 then
|
||||
if t[3] == "=" then
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], t[4][1] }, "Op")
|
||||
for i=2, math.min(#t[4], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[2], t[1][i], t[4][i] }, "Op")
|
||||
end
|
||||
return r
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[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")
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[3], t[4][1], t[1][1] }, "Op")
|
||||
for i=2, math.min(#t[4], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[3], t[4][i], t[1][i] }, "Op")
|
||||
end
|
||||
return r
|
||||
end
|
||||
else -- You are mad.
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], { tag = "Op", t[4], t[5][1], t[1][1] } }, "Op")
|
||||
for i=2, math.min(#t[5], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[2], t[1][i], { tag = "Op", t[4], t[5][i], t[1][i] } }, "Op")
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
|
@ -274,12 +310,5 @@ return function(ast, opts)
|
|||
|
||||
#placeholder("patch")
|
||||
|
||||
if opts then
|
||||
for k, v in pairs(opts) do
|
||||
options[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local r = lua(ast)
|
||||
return requireStr .. r
|
||||
return requireStr .. lua(ast) .. newline()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ Parameters:
|
|||
example: var1,var2=$$125 --> var1,var2 = "$125","$125"
|
||||
* if value is convertible to number, it is a number
|
||||
example: var1,var2=125 --> var1,var2 = 125,125
|
||||
* if value is true of false, it is a boolean
|
||||
example: var1=false --> var1 = false
|
||||
* otherwise it is a string
|
||||
example: name=John --> name = "John"
|
||||
|
||||
|
|
@ -86,7 +88,13 @@ return function(t_in, options, params)
|
|||
elseif v:find("=") then
|
||||
local ids, val = v:match("^([^=]+)%=(.*)") -- no space around =
|
||||
if not ids then return argerror("invalid assignment syntax", i) end
|
||||
if val == "false" then
|
||||
val = false
|
||||
elseif val == "true" then
|
||||
val = true
|
||||
else
|
||||
val = val:sub(1,1)=="$" and val:sub(2) or tonumber(val) or val
|
||||
end
|
||||
for id in ids:gmatch"[^,;]+" do
|
||||
if not idcheck(id) then return iderror(i) end
|
||||
t_out[id] = val
|
||||
|
|
@ -8,7 +8,7 @@ block: { stat* }
|
|||
|
||||
stat:
|
||||
`Do{ stat* }
|
||||
| `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2...
|
||||
| `Set{ {lhs+} (opid? = opid?)? {expr+} } -- lhs1, lhs2... op=op e1, e2...
|
||||
| `While{ expr block } -- while e do b end
|
||||
| `Repeat{ block expr } -- repeat b until e
|
||||
| `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end
|
||||
|
|
@ -28,7 +28,7 @@ expr:
|
|||
| `Boolean{ <boolean> }
|
||||
| `Number{ <number> }
|
||||
| `String{ <string> }
|
||||
| `Function{ { `Id{ <string> }* `Dots? } block }
|
||||
| `Function{ { ( `ParPair{ Id expr } | `Id{ <string> } )* `Dots? } block }
|
||||
| `Table{ ( `Pair{ expr expr } | expr )* }
|
||||
| `Op{ opid expr expr? }
|
||||
| `Paren{ expr } -- significant to cut multiple values returns
|
||||
|
|
@ -257,6 +257,14 @@ local function makeIndexOrCall (t1, t2)
|
|||
return { tag = "Index", pos = t1.pos, [1] = t1, [2] = t2[1] }
|
||||
end
|
||||
|
||||
local function fixAnonymousMethodParams(t1, t2)
|
||||
if t1 == ":" then
|
||||
t1 = t2
|
||||
table.insert(t1, 1, { tag = "Id", "self" })
|
||||
end
|
||||
return t1
|
||||
end
|
||||
|
||||
-- grammar
|
||||
local G = { V"Lua",
|
||||
Lua = V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra");
|
||||
|
|
@ -288,7 +296,7 @@ local G = { V"Lua",
|
|||
LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal");
|
||||
LocalFunc = tagC("Localrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat;
|
||||
LocalAssign = tagC("Local", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())));
|
||||
Assignment = tagC("Set", V"VarList" * V"AssignmentOp" * expect(V"ExprList", "EListAssign"));
|
||||
Assignment = tagC("Set", V"VarList" * V"BinOp"^-1 * (sym("=") / "=") * V"BinOp"^-1 * expect(V"ExprList", "EListAssign"));
|
||||
|
||||
FuncStat = tagC("Set", kw("function") * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat;
|
||||
FuncName = Cf(V"Id" * (sym(".") * expect(V"StrId", "NameFunc1"))^0, insertIndex)
|
||||
|
|
@ -342,13 +350,20 @@ local G = { V"Lua",
|
|||
VarExpr = Cmt(V"SuffixedExpr", function(s, i, exp) return exp.tag == "Id" or exp.tag == "Index", exp end);
|
||||
|
||||
SuffixedExpr = Cf(V"PrimaryExpr" * (V"Index" + V"Call")^0, makeIndexOrCall);
|
||||
PrimaryExpr = V"Id" + tagC("Paren", sym("(") * expect(V"Expr", "ExprParen") * expect(sym(")"), "CParenExpr"));
|
||||
PrimaryExpr = V"SelfId" * (V"SelfCall" + V"SelfIndex")
|
||||
+ V"Id"
|
||||
+ tagC("Paren", sym("(") * expect(V"Expr", "ExprParen") * expect(sym(")"), "CParenExpr"));
|
||||
Index = tagC("DotIndex", sym("." * -P".") * expect(V"StrId", "NameIndex"))
|
||||
+ tagC("ArrayIndex", sym("[" * -P(S"=[")) * expect(V"Expr", "ExprIndex") * expect(sym("]"), "CBracketIndex"));
|
||||
Call = tagC("Invoke", Cg(sym(":" * -P":") * expect(V"StrId", "NameMeth") * expect(V"FuncArgs", "MethArgs")))
|
||||
+ tagC("Call", V"FuncArgs");
|
||||
SelfIndex = tagC("DotIndex", V"StrId");
|
||||
SelfCall = tagC("Invoke", Cg(V"StrId" * V"FuncArgs"));
|
||||
|
||||
FuncDef = kw("function") * V"FuncBody";
|
||||
ShortFuncDef = tagC("Function", V"ShortFuncParams" * V"Block" * expect(kw("end"), "EndFunc"));
|
||||
ShortFuncParams = (sym(":") / ":")^-1 * sym("(") * V"ParList" * sym(")") / fixAnonymousMethodParams;
|
||||
|
||||
FuncDef = (kw("function") * V"FuncBody") + V"ShortFuncDef";
|
||||
FuncArgs = sym("(") * commaSep(V"Expr", "ArgList")^-1 * expect(sym(")"), "CParenArgs")
|
||||
+ V"Table"
|
||||
+ tagC("String", V"String");
|
||||
|
|
@ -361,7 +376,8 @@ local G = { V"Lua",
|
|||
+ V"StrId" * #("=" * -P"=");
|
||||
FieldSep = sym(",") + sym(";");
|
||||
|
||||
Id = tagC("Id", V"Name");
|
||||
SelfId = tagC("Id", sym"@" / "self");
|
||||
Id = tagC("Id", V"Name") + V"SelfId";
|
||||
StrId = tagC("String", V"Name");
|
||||
|
||||
-- lexer
|
||||
|
|
@ -455,12 +471,12 @@ local G = { V"Lua",
|
|||
+ sym("#") / "len"
|
||||
+ sym("~") / "bnot";
|
||||
PowOp = sym("^") / "pow";
|
||||
AssignmentOp = (V"OrOp" + V"AndOp" + V"BOrOp" + V"BXorOp" + V"BAndOp" + V"ShiftOp" + V"ConcatOp" + V"AddOp" + V"MulOp" + V"PowOp")^-1 * sym("=")
|
||||
BinOp = V"OrOp" + V"AndOp" + V"BOrOp" + V"BXorOp" + V"BAndOp" + V"ShiftOp" + V"ConcatOp" + V"AddOp" + V"MulOp" + V"PowOp";
|
||||
}
|
||||
|
||||
local parser = {}
|
||||
|
||||
local validator = require("lua-parser.validator")
|
||||
local validator = require("lib.lua-parser.validator")
|
||||
local validate = validator.validate
|
||||
local syntaxerror = validator.syntaxerror
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
--[[
|
||||
This module impements a validator for the AST
|
||||
]]
|
||||
local scope = require "lua-parser.scope"
|
||||
local scope = require "lib.lua-parser.scope"
|
||||
|
||||
local lineno = scope.lineno
|
||||
local new_scope, end_scope = scope.new_scope, scope.end_scope
|
||||
|
|
@ -13,14 +13,28 @@ function util.search(modpath, exts={"can", "lua"})
|
|||
end
|
||||
end
|
||||
|
||||
function util.loadenv(str, name, env)
|
||||
function util.load(str, name, env)
|
||||
if _VERSION == "Lua 5.1" then
|
||||
local fn, err = loadstring(str, name)
|
||||
if not fn then return fn, err end
|
||||
return env ~= nil and setfenv(fn, env) or fn
|
||||
else
|
||||
if env then
|
||||
return load(str, name, nil, env)
|
||||
else
|
||||
return load(str, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function util.merge(...)
|
||||
local r = {}
|
||||
for _, t in ipairs({...}) do
|
||||
for k, v in pairs(t) do
|
||||
r[k] = v
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
return util
|
||||
37
rockspec/candran-0.3.0-1.rockspec
Normal file
37
rockspec/candran-0.3.0-1.rockspec
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package = "Candran"
|
||||
|
||||
version = "0.3.0-1"
|
||||
|
||||
description = {
|
||||
summary = "A simple Lua dialect and preprocessor.",
|
||||
detailed = [[
|
||||
Candran is a dialect of the Lua 5.3 programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds a preprocessor and several useful syntax additions.
|
||||
Unlike Moonscript, Candran tries to stay close to the Lua syntax.
|
||||
]],
|
||||
license = "MIT",
|
||||
homepage = "https://github.com/Reuh/Candran",
|
||||
issues_url = "https://github.com/Reuh/Candran",
|
||||
maintener = "Étienne 'Reuh' Fildadut <fildadut@reuh.eu>",
|
||||
--labels = {}
|
||||
}
|
||||
|
||||
source = {
|
||||
url = "git://github.com/Reuh/Candran",
|
||||
tag = "v0.3.0"
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"lpeglabel >= 1.0.0"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
candran = "candran.lua"
|
||||
},
|
||||
install = {
|
||||
bin = { "bin/can", "bin/canc" }
|
||||
}
|
||||
--copy_directories = { "doc", "test" }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue