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)
|
||||
200
candran.can
200
candran.can
|
|
@ -1,28 +1,45 @@
|
|||
#import("util")
|
||||
#import("lib.util")
|
||||
#import("lib.cmdline")
|
||||
|
||||
#import("compiler.lua53")
|
||||
#import("compiler.luajit")
|
||||
#import("lua-parser.scope")
|
||||
#import("lua-parser.validator")
|
||||
#import("lua-parser.pp")
|
||||
#import("lua-parser.parser")
|
||||
#import("cmdline")
|
||||
|
||||
local util = require("util")
|
||||
#import("lib.lua-parser.scope")
|
||||
#import("lib.lua-parser.validator")
|
||||
#import("lib.lua-parser.pp")
|
||||
#import("lib.lua-parser.parser")
|
||||
|
||||
local candran = {
|
||||
VERSION = "0.2.0"
|
||||
VERSION = "0.3.0"
|
||||
}
|
||||
|
||||
--- Default options.
|
||||
local default = {
|
||||
target = "lua53",
|
||||
indentation = "",
|
||||
newline = "\n",
|
||||
requirePrefix = "CANDRAN_",
|
||||
mapLines = true,
|
||||
chunkname = "nil",
|
||||
rewriteErrors = true
|
||||
}
|
||||
|
||||
--- Run the preprocessor
|
||||
-- @tparam input string input code
|
||||
-- @tparam args table arguments for the preprocessor. They will be inserted into the preprocessor environement.
|
||||
-- @tparam options table arguments for the preprocessor. They will be inserted into the preprocessor environement.
|
||||
-- @treturn output string output code
|
||||
function candran.preprocess(input, args={})
|
||||
function candran.preprocess(input, options={})
|
||||
options = util.merge(default, options)
|
||||
|
||||
-- generate preprocessor code
|
||||
local preprocessor = ""
|
||||
local i = 0
|
||||
for line in (input.."\n"):gmatch("(.-\n)") do
|
||||
i += 1
|
||||
if line:match("^%s*#") and not line:match("^#!") then -- exclude shebang
|
||||
preprocessor ..= line:gsub("^%s*#", "")
|
||||
elseif options.mapLines then
|
||||
preprocessor ..= ("write(%q)"):format(line:sub(1, -2) .. " -- "..options.chunkname..":" .. i) .. "\n"
|
||||
else
|
||||
preprocessor ..= ("write(%q)"):format(line:sub(1, -2)) .. "\n"
|
||||
end
|
||||
|
|
@ -30,13 +47,7 @@ function candran.preprocess(input, args={})
|
|||
preprocessor ..= "return output"
|
||||
|
||||
-- make preprocessor environement
|
||||
local env = {}
|
||||
for k,v in pairs(_G) do
|
||||
env[k] = v
|
||||
end
|
||||
for k, v in pairs(args) do
|
||||
env[k] = v
|
||||
end
|
||||
local env = util.merge(_G, options)
|
||||
--- Candran library table
|
||||
env.candran = candran
|
||||
--- Current preprocessor output
|
||||
|
|
@ -45,15 +56,14 @@ function candran.preprocess(input, args={})
|
|||
-- @tparam modpath string module path
|
||||
-- @tparam margs table preprocessor arguments to use when preprocessessing the module
|
||||
-- @tparam autoRequire[opt=true] boolean true to automatically load the module into a local variable
|
||||
env.import = function(modpath, margs=args, autoRequire=true)
|
||||
env.import = function(modpath, margs={}, autoRequire=true)
|
||||
local filepath = assert(util.search(modpath), "No module named \""..modpath.."\"")
|
||||
|
||||
-- open module file
|
||||
local f = io.open(filepath)
|
||||
if not f then error("Can't open the module file to import") end
|
||||
for k, v in pairs(args) do
|
||||
if margs[k] == nil then margs[k] = v end
|
||||
end
|
||||
|
||||
margs = util.merge(options, { chunkname = filepath }, margs)
|
||||
local modcontent = candran.preprocess(f:read("*a"), margs)
|
||||
f:close()
|
||||
|
||||
|
|
@ -92,57 +102,143 @@ function candran.preprocess(input, args={})
|
|||
end
|
||||
|
||||
-- compile & load preprocessor
|
||||
local preprocess, err = util.loadenv(candran.compile(preprocessor, args.target), "candran preprocessor", env)
|
||||
local preprocess, err = util.load(candran.compile(preprocessor, args), "candran preprocessor", env)
|
||||
if not preprocess then error("Error while creating Candran preprocessor: " .. err) end
|
||||
|
||||
-- execute preprocessor
|
||||
local success, output = pcall(preprocess)
|
||||
if not success then error("Error while preprocessing file: " .. output .. "\nWith preprocessor : \n" .. preprocessor) end
|
||||
if not success then error("Error while preprocessing file: " .. output) end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
-- Compiler
|
||||
function candran.compile(input, target="lua53")
|
||||
local parse = require("lua-parser.parser").parse
|
||||
--- Run the compiler
|
||||
-- @tparam input string input code
|
||||
-- @tparam options table options for the compiler
|
||||
-- @treturn output string output code
|
||||
function candran.compile(input, options={})
|
||||
options = util.merge(default, options)
|
||||
|
||||
local ast, errmsg = parse(input, "candran")
|
||||
local ast, errmsg = parser.parse(input, "candran")
|
||||
|
||||
if not ast then
|
||||
error("Compiler: error while parsing file: "..errmsg)
|
||||
end
|
||||
|
||||
return require("compiler."..target)(ast)
|
||||
return require("compiler."..options.target)(input, ast, options)
|
||||
end
|
||||
|
||||
-- Preprocess & compile
|
||||
function candran.make(code, args={})
|
||||
return candran.compile(candran.preprocess(code, args), args.target)
|
||||
--- Preprocess & compile code
|
||||
-- @tparam code string input code
|
||||
-- @tparam options table arguments for the preprocessor and compiler
|
||||
-- @treturn output string output code
|
||||
function candran.make(code, options)
|
||||
return candran.compile(candran.preprocess(code, options), options)
|
||||
end
|
||||
|
||||
function candran.searcher(modpath)
|
||||
-- get module filepath
|
||||
local notfound = ""
|
||||
local filepath
|
||||
for path in package.path:gsub("%.lua", ".can"):gmatch("[^;]+") do
|
||||
local path = path:gsub("%?", (modpath:gsub("%.", "/")))
|
||||
local f = io.open(path)
|
||||
if f then
|
||||
f:close()
|
||||
filepath = path
|
||||
else
|
||||
notfound = notfound .. "\n\tno Candran file '"..path.."'"
|
||||
end
|
||||
end
|
||||
if not filepath then return notfound end
|
||||
|
||||
-- open module file
|
||||
local f = io.open(filepath)
|
||||
if not f then error("Can't open the module file to import") end
|
||||
local modcontent = f:read("*a")
|
||||
local errorRewritingActive = false
|
||||
local codeCache = {}
|
||||
--- Candran equivalent to the Lua 5.3's loadfile funtion.
|
||||
-- Will rewrite errors by default.
|
||||
function candran.loadfile(filepath, env, options)
|
||||
local f, err = io.open(filepath)
|
||||
if not f then error("can't open the file: "..err) end
|
||||
local content = f:read("*a")
|
||||
f:close()
|
||||
|
||||
return load(candran.make(modcontent))
|
||||
return candran.load(content, filepath, env, options)
|
||||
end
|
||||
|
||||
--- Candran equivalent to the Lua 5.3's load funtion.
|
||||
-- Will rewrite errors by default.
|
||||
function candran.load(chunk, chunkname, env, options={})
|
||||
options = util.merge({ chunkname = tostring(chunkname or chunk) }, options)
|
||||
|
||||
codeCache[options.chunkname] = candran.make(chunk, options)
|
||||
local f = util.load(codeCache[options.chunkname], options.chunkname, env)
|
||||
|
||||
if options.rewriteErrors == false then
|
||||
return f
|
||||
else
|
||||
return function()
|
||||
if not errorRewritingActive then
|
||||
errorRewritingActive = true
|
||||
local t = { xpcall(f, candran.messageHandler) }
|
||||
errorRewritingActive = false
|
||||
if t[1] == false then
|
||||
error(t[2], 0)
|
||||
end
|
||||
return unpack(t, 2)
|
||||
else
|
||||
return f()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Candran equivalent to the Lua 5.3's dofile funtion.
|
||||
-- Will rewrite errors by default.
|
||||
function candran.dofile(filename, options)
|
||||
return candran.loadfile(filename, nil, options)()
|
||||
end
|
||||
|
||||
--- Candran error message handler.
|
||||
-- Use it in xpcall to rewrite stacktraces to display Candran source file lines instead of compiled Lua lines.
|
||||
function candran.messageHandler(message)
|
||||
return debug.traceback(message, 2):gsub("(\n?%s*)([^\n]-)%:(%d+)%:", function(indentation, source, line)
|
||||
line = tonumber(line)
|
||||
|
||||
local originalFile
|
||||
local strName = source:match("%[string \"(.-)\"%]")
|
||||
if strName then
|
||||
if codeCache[strName] then
|
||||
originalFile = codeCache[strName]
|
||||
source = strName
|
||||
end
|
||||
else
|
||||
local fi = io.open(source, "r")
|
||||
if fi then
|
||||
originalFile = fi:read("*a")
|
||||
end
|
||||
fi:close()
|
||||
end
|
||||
|
||||
if originalFile then
|
||||
local i = 0
|
||||
for l in originalFile:gmatch("([^\n]*)") do
|
||||
i = i +1
|
||||
if i == line then
|
||||
local extSource, lineMap = l:match("%-%- (.-)%:(%d+)$")
|
||||
if lineMap then
|
||||
if extSource ~= source then
|
||||
return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):"
|
||||
else
|
||||
return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):"
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Candran package searcher function. Use the existing package.path.
|
||||
function candran.searcher(modpath)
|
||||
local filepath = util.search(modpath)
|
||||
if not filepath then
|
||||
return "\n\tno candran file in package.path"
|
||||
end
|
||||
return candran.loadfile(filepath)
|
||||
end
|
||||
|
||||
--- Register the Candran package searcher.
|
||||
function candran.setup()
|
||||
if _VERSION == "Lua 5.1" then
|
||||
table.insert(package.loaders, 2, candran.searcher)
|
||||
else
|
||||
table.insert(package.searchers, 2, candran.searcher)
|
||||
end
|
||||
end
|
||||
|
||||
return candran
|
||||
|
|
|
|||
3694
candran.lua
3694
candran.lua
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,30 @@
|
|||
return function(ast, opts)
|
||||
local options = {
|
||||
indentation = "\t",
|
||||
newline = "\n",
|
||||
requirePrefix = "CANDRAN_"
|
||||
}
|
||||
return function(code, ast, options)
|
||||
local lastInputPos = 1
|
||||
local prevLinePos = 1
|
||||
local lastSource = "nil"
|
||||
local lastLine = 1
|
||||
|
||||
local indentLevel = 0
|
||||
local function newline()
|
||||
return options.newline .. string.rep(options.indentation, indentLevel)
|
||||
local r = options.newline .. string.rep(options.indentation, indentLevel)
|
||||
if options.mapLines then
|
||||
local sub = code:sub(lastInputPos)
|
||||
local source, line = sub:sub(1, sub:find("\n")):match("%-%- (.-)%:(%d+)\n")
|
||||
|
||||
if source and line then
|
||||
lastSource = source
|
||||
lastLine = tonumber(line)
|
||||
else
|
||||
for _ in code:sub(prevLinePos, lastInputPos):gmatch("\n") do
|
||||
lastLine += 1
|
||||
end
|
||||
end
|
||||
|
||||
prevLinePos = lastInputPos
|
||||
|
||||
r = " -- " .. lastSource .. ":" .. lastLine .. r
|
||||
end
|
||||
return r
|
||||
end
|
||||
local function indent()
|
||||
indentLevel += 1
|
||||
|
|
@ -32,6 +49,9 @@ return function(ast, opts)
|
|||
|
||||
local tags
|
||||
local function lua(ast, forceTag, ...)
|
||||
if options.mapLines and ast.pos then
|
||||
lastInputPos = ast.pos
|
||||
end
|
||||
return tags[forceTag or ast.tag](ast, ...)
|
||||
end
|
||||
|
||||
|
|
@ -54,14 +74,30 @@ return function(ast, opts)
|
|||
Do = function(t)
|
||||
return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end"
|
||||
end,
|
||||
-- Set{ {lhs+} opid? {expr+} }
|
||||
-- Set{ {lhs+} (opid? = opid?)? {expr+} }
|
||||
Set = function(t)
|
||||
if #t == 2 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs")
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], t[3][1] }, "Op")
|
||||
for i=2, math.min(#t[3], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[2], t[1][i], t[3][i] }, "Op")
|
||||
elseif #t == 3 then
|
||||
return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs")
|
||||
elseif #t == 4 then
|
||||
if t[3] == "=" then
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], t[4][1] }, "Op")
|
||||
for i=2, math.min(#t[4], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[2], t[1][i], t[4][i] }, "Op")
|
||||
end
|
||||
return r
|
||||
else
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[3], t[4][1], t[1][1] }, "Op")
|
||||
for i=2, math.min(#t[4], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[3], t[4][i], t[1][i] }, "Op")
|
||||
end
|
||||
return r
|
||||
end
|
||||
else -- You are mad.
|
||||
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], { tag = "Op", t[4], t[5][1], t[1][1] } }, "Op")
|
||||
for i=2, math.min(#t[5], #t[1]), 1 do
|
||||
r = r .. ", " .. lua({ t[2], t[1][i], { tag = "Op", t[4], t[5][i], t[1][i] } }, "Op")
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
|
@ -274,12 +310,5 @@ return function(ast, opts)
|
|||
|
||||
#placeholder("patch")
|
||||
|
||||
if opts then
|
||||
for k, v in pairs(opts) do
|
||||
options[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local r = lua(ast)
|
||||
return requireStr .. r
|
||||
return requireStr .. lua(ast) .. newline()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ Parameters:
|
|||
example: var1,var2=$$125 --> var1,var2 = "$125","$125"
|
||||
* if value is convertible to number, it is a number
|
||||
example: var1,var2=125 --> var1,var2 = 125,125
|
||||
* if value is true of false, it is a boolean
|
||||
example: var1=false --> var1 = false
|
||||
* otherwise it is a string
|
||||
example: name=John --> name = "John"
|
||||
|
||||
|
|
@ -86,7 +88,13 @@ return function(t_in, options, params)
|
|||
elseif v:find("=") then
|
||||
local ids, val = v:match("^([^=]+)%=(.*)") -- no space around =
|
||||
if not ids then return argerror("invalid assignment syntax", i) end
|
||||
val = val:sub(1,1)=="$" and val:sub(2) or tonumber(val) or val
|
||||
if val == "false" then
|
||||
val = false
|
||||
elseif val == "true" then
|
||||
val = true
|
||||
else
|
||||
val = val:sub(1,1)=="$" and val:sub(2) or tonumber(val) or val
|
||||
end
|
||||
for id in ids:gmatch"[^,;]+" do
|
||||
if not idcheck(id) then return iderror(i) end
|
||||
t_out[id] = val
|
||||
|
|
@ -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
|
||||
return load(str, name, nil, env)
|
||||
if env then
|
||||
return load(str, name, nil, env)
|
||||
else
|
||||
return load(str, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function util.merge(...)
|
||||
local r = {}
|
||||
for _, t in ipairs({...}) do
|
||||
for k, v in pairs(t) do
|
||||
r[k] = v
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
return util
|
||||
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