1
0
Fork 0
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:
Étienne Fildadut 2017-08-16 22:33:44 +02:00
parent 2a1e293aa5
commit 4af2b41a0d
17 changed files with 2413 additions and 1865 deletions

154
README.md
View file

@ -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
View 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

View file

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

View file

@ -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
if not filepath then return notfound end
end
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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View 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" }
}