1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2025-10-27 17:59:30 +00:00
Fixed plenty of bugs.

Tests are lacking.
This commit is contained in:
Étienne Fildadut 2017-08-25 19:20:29 +02:00
parent 6b95bfb698
commit d249c353c5
7 changed files with 315 additions and 95 deletions

184
README.md
View file

@ -1,27 +1,31 @@
Candran
=======
Candran is a dialect of the [Lua 5.3](http://www.lua.org) programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds a preprocessor and several useful syntax additions.
Candran is a dialect of the [Lua 5.3](http://www.lua.org) programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds several useful syntax additions which aims to make Lua faster and easier to write, and a simple preprocessor.
Unlike Moonscript, Candran tries to stay close to the Lua syntax.
Unlike Moonscript, Candran tries to stay close to the Lua syntax, and existing Lua code can run on Candran unmodified.
````lua
#import("lib.thing")
#import("lib.thing") -- static import
#local debug or= false
local function calculate(toadd=25)
local function calculate(toadd=25) -- default parameters
local result = thing.do()
result += toadd
#if debug then
#if debug then -- preprocessor conditionals
print("Did something")
#end
return result
end
local a = {
let a = {
hey = true,
newHop = :(foo, thing)
@hey = thing(foo)
newHop = :(foo, thing) -- short function declaration, with self
@hey = thing(foo) -- @ as an alias for self
end,
selfReference = () -- short function declaration, without self
return a -- no need for a prior local declaration using let
end
}
@ -29,47 +33,36 @@ a:newHop(42, (foo)
return "something " .. foo
end)
local list = [ -- table comprehension (kind of)
for i=1, 10 do
if i%2 == 0 then
continue -- continue keyword
end
i
end
]
local a = if condition then "one" else "two" end -- statement as expressions
````
Candran is released under the MIT License (see ```LICENSE``` for details).
#### Quick setup
Install Candran automatically using LuaRocks: ```sudo luarocks install rockspec/candran-0.4.0-1.rockspec```.
Install Candran automatically using LuaRocks: ```sudo luarocks install rockspec/candran-0.5.0-1.rockspec```.
Or manually 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```.
You can register the Candran package searcher in your main Lua file (`require("candran").setup()`) and any subsequent `require` call in your project will automatically search for Candran modules.
#### Editor support
Most editors should be able to use their existing Lua support for Candran code. If you want full support for the additional syntax in your editor:
* **Atom**: [language-candran](https://atom.io/packages/language-candran) support the full Candran syntax
The language
------------
### Preprocessor
Before compiling, Candran's preprocessor is run. It execute every line starting with a _#_ (ignoring whitespace) as Candran code.
For example,
````lua
#if lang == "fr" then
print("Bonjour")
#else
print("Hello")
#end
````
Will output ````print("Bonjour")```` or ````print("Hello")```` depending of the "lang" argument passed to the preprocessor.
The preprocessor has access to the following variables :
* ````candran```` : the Candran library table.
* ````output```` : the current preprocessor output string.
* ````import(module[, [options])```` : a function which import a module. This should be equivalent to using _require(module)_ in the Candran code, except the module will be embedded in the current file. _options_ is an optional preprocessor arguments table for the imported module (current preprocessor arguments will be inherited). Options specific to this function: ```loadLocal``` (default ```true```): ```true``` to automatically load the module into a local variable (i.e. ```local thing = require("module.thing")```); ```loadPackage``` (default ```true```): ```true``` to automatically load the module into the loaded packages table (so it will be available for following ```require("module")``` calls).
* ````include(filename)```` : a function which copy the contents of the file _filename_ to the output.
* ````write(...)```` : write to the preprocessor output. For example, ````#print("hello()")```` will output ````hello()```` in the final file.
* ```placeholder(name)``` : if the variable _name_ is defined in the preprocessor environement, its content will be inserted here.
* ````...```` : each arguments passed to the preprocessor is directly available.
* and every standard Lua library.
### Syntax additions
After the preprocessor is run the Candran code is compiled to Lua. The Candran code adds the folowing syntax to Lua :
After the preprocessor is run the Candran code is compiled to Lua. Candran code adds the folowing syntax to Lua:
##### Assignment operators
* ````var += nb````
@ -105,7 +98,7 @@ 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
##### `@` self aliases
```lua
a = {
foo = "Hoi"
@ -135,7 +128,7 @@ Anonymous function (functions values) can be created in a more concise way by om
A ```:``` can prefix the parameters paranthesis to automatically add a ```self``` parameter.
##### let variable declaration
##### `let` variable declaration
```lua
let a = {
foo = function()
@ -148,7 +141,7 @@ Similar to ```local```, but the variable will be declared *before* the assignemn
Can also be used as a shorter name for ```local```.
##### continue keyword
##### `continue` keyword
```lua
for i=1, 10 do
if i % 2 == 0 then
@ -160,16 +153,119 @@ end
Will skip the current loop iteration.
##### `push` keyword
```lua
function a()
for i=1, 5 do
push i, "next"
end
return "done"
end
print(a()) -- 1, next, 2, next, 3, next, 4, next, 5, next, done
push "hey" -- Does *not* work, because it is a valid Lua syntax for push("hey")
```
Add one or more value to the returned value list. If you use a `return` afterwards, the pushed values will be placed *before* the `return` values, otherwise the function will only return what was pushed.
This keyword is mainly useful when used through implicit `push` with table comprehension and statement expressions.
**Please note** that, in order to stay compatible with vanilla Lua syntax, any `push` immediatly followed by a `"string expression"`, `{table expression}` or `(paranthesis)` will be interpreted as a function call. Preferably use implicit `push` in these cases.
##### Implicit `push`
```lua
function a()
for i=1, 5 do
i, next
end
return "done"
end
print(a()) -- 1, next, 2, next, 3, next, 4, next, 5, next, done
-- or probably more useful...
local square = (x) x*x end -- function(x) return x*x end
```
Any list of expressions placed *at the end of a block* will be converted into a `push` automatically.
**Please note** that this doesn't work with `v()` function calls, because these are already valid statements. Use `push v()` instead.
##### Statement expressions
```lua
a = if false then
"foo" -- i.e. push "foo", i.e. return "foo"
else
"bar"
end
print(a) -- bar
a, b, c = for i=1,2 do i end
print(a, b, c) -- 1, 2, nil
```
Candran allows to use `if`, `do`, `while`, `repeat` and `for` statements as expressions. Their content will be run as if they were run in a separate function which is immediatly run.
##### Table comprehension
```lua
a = [
for i=1, 10 do
i
end
] -- { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
a = [
for i=1, 10 do
if i%2 == 0 then
@[i] = true
end
end
] -- { [2] = true, [4] = true, [6] = true, [8] = true, [10] = true }
a = [push unpack(t1); push unpack(t2)] -- concatenate t1 and t2
```
Comprehensions provide a shorter syntax for defining and initializing tables based on a block of code.
You can write *any* code you want between `[` and `]`, this code will be run as if it was a separate function which is immediadtly run.
Values returned by the function will be inserted in the generated table in the order they were returned. This way, each time you `push` value(s), they will be added to the table.
The table generation function also have access to the `self` (or its alias `@`) variable, which is the table which is being created, so you can set arbitrary fields of the table.
### Preprocessor
Before compiling, Candran's preprocessor is run. It execute every line starting with a _#_ (ignoring whitespace) as Candran code.
For example,
````lua
#if lang == "fr" then
print("Bonjour")
#else
print("Hello")
#end
````
Will output ````print("Bonjour")```` or ````print("Hello")```` depending of the "lang" argument passed to the preprocessor.
The preprocessor has access to the following variables :
* ````candran```` : the Candran library table.
* ````output```` : the current preprocessor output string.
* ````import(module[, [options])```` : a function which import a module. This should be equivalent to using _require(module)_ in the Candran code, except the module will be embedded in the current file. _options_ is an optional preprocessor arguments table for the imported module (current preprocessor arguments will be inherited). Options specific to this function: ```loadLocal``` (default ```true```): ```true``` to automatically load the module into a local variable (i.e. ```local thing = require("module.thing")```); ```loadPackage``` (default ```true```): ```true``` to automatically load the module into the loaded packages table (so it will be available for following ```require("module")``` calls).
* ````include(filename)```` : a function which copy the contents of the file _filename_ to the output.
* ````write(...)```` : write to the preprocessor output. For example, ````#print("hello()")```` will output ````hello()```` in the final file.
* ```placeholder(name)``` : if the variable _name_ is defined in the preprocessor environement, its content will be inserted here.
* ````...```` : each arguments passed to the preprocessor is directly available.
* and every standard Lua library.
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, 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. Unless you require LuaJIT's library, you won't be able to use bitwise operators with simple Lua 5.1.
The library
-----------
Usage
-----
### Command-line usage
The library can be used standalone through the ```canc``` and ```can``` utility:
@ -197,6 +293,10 @@ The library can be used standalone through the ```canc``` and ```can``` utility:
preprocess and compile _foo.can_ and write the result in _foo.lua_.
````canc indentation=" " foo.can````
preprocess and compile _foo.can_ with 2-space indentation (readable code!) and write the result in _foo.lua_.
````canc foo.can -verbose -print | lua````
preprocess _foo.can_ with _verbose_ set to _true_, compile it and execute it.
@ -284,7 +384,7 @@ You can give arbitrary options which will be gived to the preprocessor, but Cand
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).
variablePrefix = "__CAN_" -- Prefix used when Candran needs to set a local variable to provide some functionality (example: to load 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().
@ -303,7 +403,7 @@ This command will use the precompilled version of this repository (candran.lua)
canc candran.can
````
The Candran build included in this repository were made using the ```mapLines=false``` options.
The Candran build included in this repository was made using the ```mapLines=false``` option.
You can then run the tests on your build :

View file

@ -10,7 +10,7 @@
#import("lib.lua-parser.parser")
local candran = {
VERSION = "0.4.0"
VERSION = "0.5.0"
}
--- Default options.
@ -18,7 +18,7 @@ local default = {
target = "lua53",
indentation = "",
newline = "\n",
requirePrefix = "CANDRAN_",
variablePrefix = "__CAN_",
mapLines = true,
chunkname = "nil",
rewriteErrors = true

View file

@ -171,12 +171,12 @@ local required = {}
local requireStr = ""
local function addRequire(mod, name, field)
if not required[mod] then
requireStr = requireStr .. ("local " .. options["requirePrefix"] .. name .. (" = require(%q)"):format(mod) .. (field and "." .. field or "") .. options["newline"])
requireStr = requireStr .. ("local " .. options["variablePrefix"] .. name .. (" = require(%q)"):format(mod) .. (field and "." .. field or "") .. options["newline"])
required[mod] = true
end
end
local function getRequire(name)
return options["requirePrefix"] .. name
local function var(name)
return options["variablePrefix"] .. name
end
local loop = {
"While",
@ -238,16 +238,22 @@ lastInputPos = ast["pos"]
end
return tags[forceTag or ast["tag"]](ast, ...)
end
local UNPACK = function(list, i, j)
return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
end
local APPEND = function(t, toAppend)
return "do" .. indent() .. "local a = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(a, 1, a.n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end"
end
tags = setmetatable({
["Block"] = function(t)
local hasPush = not peek("push") and any(t, { "Push" }, func)
local hasPush = peek("push") == nil and any(t, { "Push" }, func)
if hasPush and hasPush == t[# t] then
hasPush["tag"] = "Return"
hasPush = false
end
local r = ""
if hasPush then
r = r .. (push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline())
r = r .. (push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline())
end
for i = 1, # t - 1, 1 do
r = r .. (lua(t[i]) .. newline())
@ -256,7 +262,7 @@ if t[# t] then
r = r .. (lua(t[# t]))
end
if hasPush and (t[# t] and t[# t]["tag"] ~= "Return") then
r = r .. (newline() .. "return unpack(__PUSH__)" .. pop("push"))
r = r .. (newline() .. "return " .. UNPACK(var("push")) .. pop("push"))
end
return r
end,
@ -455,14 +461,25 @@ local r = ""
for _, val in ipairs(t) do
r = r .. (push .. "[#" .. push .. "+1] = " .. lua(val) .. newline())
end
return r .. "return unpack(" .. push .. ")"
return r .. "return " .. UNPACK(push)
else
return "return " .. lua(t, "_lhs")
end
end,
["Push"] = function(t)
local var = assert(peek("push"), "no context given for push")
return var .. "[#" .. var .. "+1] = " .. lua(t, "_lhs")
r = ""
for i = 1, # t - 1, 1 do
r = r .. (var .. "[#" .. var .. "+1] = " .. lua(t[i]) .. newline())
end
if t[# t] then
if t[# t]["tag"] == "Call" or t[# t]["tag"] == "Invoke" then
r = r .. (APPEND(var, lua(t[# t])))
else
r = r .. (var .. "[#" .. var .. "+1] = " .. lua(t[# t]))
end
end
return r
end,
["Break"] = function()
return "break"
@ -514,7 +531,21 @@ r = r .. (")" .. indent())
for _, d in ipairs(decl) do
r = r .. (d .. newline())
end
return r .. lua(t[2]) .. unindent() .. "end"
if t[2][# t[2]] and t[2][# t[2]]["tag"] == "Push" then
t[2][# t[2]]["tag"] = "Return"
end
local hasPush = any(t[2], { "Push" }, func)
if hasPush then
r = r .. (push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline())
else
push("push", false)
end
r = r .. (lua(t[2]))
if hasPush then
r = r .. (newline() .. "return " .. UNPACK(var("push")))
end
pop("push")
return r .. unindent() .. "end"
end,
["Function"] = function(t)
return "function" .. lua(t, "_functionWithoutKeyword")
@ -558,12 +589,15 @@ end,
local hasPush = any(t, { "Push" }, func)
local r = "(function()" .. indent()
if hasPush then
r = r .. (push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline())
r = r .. (push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline())
else
push("push", false)
end
r = r .. (lua(t, stat))
if hasPush then
r = r .. (newline() .. "return unpack(__PUSH__)" .. pop("push"))
r = r .. (newline() .. "return " .. UNPACK(var("push")))
end
pop("push")
r = r .. (unindent() .. "end)()")
return r
end,
@ -696,12 +730,12 @@ local required = {}
local requireStr = ""
local function addRequire(mod, name, field)
if not required[mod] then
requireStr = requireStr .. ("local " .. options["requirePrefix"] .. name .. (" = require(%q)"):format(mod) .. (field and "." .. field or "") .. options["newline"])
requireStr = requireStr .. ("local " .. options["variablePrefix"] .. name .. (" = require(%q)"):format(mod) .. (field and "." .. field or "") .. options["newline"])
required[mod] = true
end
end
local function getRequire(name)
return options["requirePrefix"] .. name
local function var(name)
return options["variablePrefix"] .. name
end
local loop = {
"While",
@ -763,16 +797,22 @@ lastInputPos = ast["pos"]
end
return tags[forceTag or ast["tag"]](ast, ...)
end
local UNPACK = function(list, i, j)
return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
end
local APPEND = function(t, toAppend)
return "do" .. indent() .. "local a = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(a, 1, a.n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end"
end
tags = setmetatable({
["Block"] = function(t)
local hasPush = not peek("push") and any(t, { "Push" }, func)
local hasPush = peek("push") == nil and any(t, { "Push" }, func)
if hasPush and hasPush == t[# t] then
hasPush["tag"] = "Return"
hasPush = false
end
local r = ""
if hasPush then
r = r .. (push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline())
r = r .. (push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline())
end
for i = 1, # t - 1, 1 do
r = r .. (lua(t[i]) .. newline())
@ -781,7 +821,7 @@ if t[# t] then
r = r .. (lua(t[# t]))
end
if hasPush and (t[# t] and t[# t]["tag"] ~= "Return") then
r = r .. (newline() .. "return unpack(__PUSH__)" .. pop("push"))
r = r .. (newline() .. "return " .. UNPACK(var("push")) .. pop("push"))
end
return r
end,
@ -980,14 +1020,25 @@ local r = ""
for _, val in ipairs(t) do
r = r .. (push .. "[#" .. push .. "+1] = " .. lua(val) .. newline())
end
return r .. "return unpack(" .. push .. ")"
return r .. "return " .. UNPACK(push)
else
return "return " .. lua(t, "_lhs")
end
end,
["Push"] = function(t)
local var = assert(peek("push"), "no context given for push")
return var .. "[#" .. var .. "+1] = " .. lua(t, "_lhs")
r = ""
for i = 1, # t - 1, 1 do
r = r .. (var .. "[#" .. var .. "+1] = " .. lua(t[i]) .. newline())
end
if t[# t] then
if t[# t]["tag"] == "Call" or t[# t]["tag"] == "Invoke" then
r = r .. (APPEND(var, lua(t[# t])))
else
r = r .. (var .. "[#" .. var .. "+1] = " .. lua(t[# t]))
end
end
return r
end,
["Break"] = function()
return "break"
@ -1039,7 +1090,21 @@ r = r .. (")" .. indent())
for _, d in ipairs(decl) do
r = r .. (d .. newline())
end
return r .. lua(t[2]) .. unindent() .. "end"
if t[2][# t[2]] and t[2][# t[2]]["tag"] == "Push" then
t[2][# t[2]]["tag"] = "Return"
end
local hasPush = any(t[2], { "Push" }, func)
if hasPush then
r = r .. (push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline())
else
push("push", false)
end
r = r .. (lua(t[2]))
if hasPush then
r = r .. (newline() .. "return " .. UNPACK(var("push")))
end
pop("push")
return r .. unindent() .. "end"
end,
["Function"] = function(t)
return "function" .. lua(t, "_functionWithoutKeyword")
@ -1083,12 +1148,15 @@ end,
local hasPush = any(t, { "Push" }, func)
local r = "(function()" .. indent()
if hasPush then
r = r .. (push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline())
r = r .. (push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline())
else
push("push", false)
end
r = r .. (lua(t, stat))
if hasPush then
r = r .. (newline() .. "return unpack(__PUSH__)" .. pop("push"))
r = r .. (newline() .. "return " .. UNPACK(var("push")))
end
pop("push")
r = r .. (unindent() .. "end)()")
return r
end,
@ -1174,6 +1242,12 @@ end,
}, { ["__index"] = function(self, key)
error("don't know how to compile a " .. tostring(key) .. " to Lua 5.3")
end })
UNPACK = function(list, i, j)
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
end
APPEND = function(t, toAppend)
return "do" .. indent() .. "local a, p = { " .. toAppend .. " }, #" .. t .. "+1" .. newline() .. "for i=1, #a do" .. indent() .. t .. "[p] = a[i]" .. newline() .. "p = p + 1" .. unindent() .. "end" .. unindent() .. "end"
end
tags["_opid"]["idiv"] = function(left, right)
return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")"
end
@ -1465,7 +1539,7 @@ local status, msg = traverse_varlist(env, stm[1])
if not status then
return status, msg
end
status, msg = traverse_explist(env, stm[2])
status, msg = traverse_explist(env, stm[# stm])
if not status then
return status, msg
end
@ -2708,13 +2782,13 @@ return parser
end
local parser = _() or parser
package["loaded"]["lib.lua-parser.parser"] = parser or true
local candran = { ["VERSION"] = "0.4.0" }
local candran = { ["VERSION"] = "0.5.0" }
local default = {
["target"] = "lua53",
["indentation"] = "",
["newline"] = "\
",
["requirePrefix"] = "CANDRAN_",
["variablePrefix"] = "__CAN_",
["mapLines"] = true,
["chunkname"] = "nil",
["rewriteErrors"] = true

View file

@ -46,13 +46,15 @@ return function(code, ast, options)
-- Add the module "mod" to the list of modules to require, and load its field "field" (or the whole module if nil) into the variable "name".
local function addRequire(mod, name, field)
if not required[mod] then
requireStr ..= "local " .. options.requirePrefix .. name .. (" = require(%q)"):format(mod) .. (field and "."..field or "") .. options.newline
requireStr ..= "local " .. options.variablePrefix .. name .. (" = require(%q)"):format(mod) .. (field and "."..field or "") .. options.newline
required[mod] = true
end
end
-- Returns the required module variable name.
local function getRequire(name)
return options.requirePrefix .. name
--- Variable management
-- Returns the prefixed variable name.
local function var(name)
return options.variablePrefix .. name
end
--- AST traversal helpers
@ -112,18 +114,27 @@ return function(code, ast, options)
end
return tags[forceTag or ast.tag](ast, ...)
end
-- Tag constructors
--- Lua function calls writer
local UNPACK = (list, i, j) -- table.unpack
return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
end
local APPEND = (t, toAppend) -- append values "toAppend" (multiple values possible) to t
return "do" .. indent() .. "local a = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(a, 1, a.n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end"
end
--- Tag constructors
tags = setmetatable({
-- block: { stat* } --
Block = (t)
local hasPush = not peek("push") and any(t, { "Push" }, func) -- push in block and push context not yet defined
local hasPush = peek("push") == nil and any(t, { "Push" }, func) -- push in block and push context not yet defined
if hasPush and hasPush == t[#t] then -- if the first push is the last statement, it's just a return
hasPush.tag = "Return"
hasPush = false
end
local r = ""
if hasPush then
r ..= push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline()
r ..= push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline()
end
for i=1, #t-1, 1 do
r ..= lua(t[i]) .. newline()
@ -132,7 +143,7 @@ return function(code, ast, options)
r ..= lua(t[#t])
end
if hasPush and (t[#t] and t[#t].tag ~= "Return") then -- add return only if needed
r ..= newline() .. "return unpack(__PUSH__)" .. pop("push")
r ..= newline() .. "return " .. UNPACK(var("push")) .. pop("push")
end
return r
end,
@ -291,7 +302,7 @@ return function(code, ast, options)
for _, val in ipairs(t) do
r ..= push .. "[#" .. push .. "+1] = " .. lua(val) .. newline()
end
return r .. "return unpack(" .. push .. ")"
return r .. "return " .. UNPACK(push)
else
return "return "..lua(t, "_lhs")
end
@ -299,7 +310,18 @@ return function(code, ast, options)
-- Push{ <expr*> }
Push = (t)
local var = assert(peek("push"), "no context given for push")
return var .. "[#" .. var .. "+1] = " .. lua(t, "_lhs")
r = ""
for i=1, #t-1, 1 do
r ..= var .. "[#" .. var .. "+1] = " .. lua(t[i]) .. newline()
end
if t[#t] then
if t[#t].tag == "Call" or t[#t].tag == "Invoke" then
r ..= APPEND(var, lua(t[#t]))
else
r ..= var .. "[#" .. var .. "+1] = " .. lua(t[#t])
end
end
return r
end,
-- Break
Break = ()
@ -363,7 +385,21 @@ return function(code, ast, options)
for _, d in ipairs(decl) do
r ..= d .. newline()
end
return r .. lua(t[2]) .. unindent() .. "end"
if t[2][#t[2]] and t[2][#t[2]].tag == "Push" then -- convert final push to return
t[2][#t[2]].tag = "Return"
end
local hasPush = any(t[2], { "Push" }, func)
if hasPush then
r ..= push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline()
else
push("push", false) -- no push here (make sure higher push don't affect us)
end
r ..= lua(t[2])
if hasPush then
r ..= newline() .. "return " .. UNPACK(var("push"))
end
pop("push")
return r .. unindent() .. "end"
end,
Function = (t)
return "function" .. lua(t, "_functionWithoutKeyword")
@ -416,12 +452,15 @@ return function(code, ast, options)
local hasPush = any(t, { "Push" }, func)
local r = "(function()" .. indent()
if hasPush then
r ..= push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline()
r ..= push("push", var("push")) .. "local " .. var("push") .. " = {}" .. newline()
else
push("push", false) -- no push here (make sure higher push don't affect us)
end
r ..= lua(t, stat)
if hasPush then
r ..= newline() .. "return unpack(__PUSH__)" .. pop("push")
r ..= newline() .. "return " .. UNPACK(var("push"))
end
pop("push")
r ..= unindent() .. "end)()"
return r
end,

View file

@ -1,27 +1,34 @@
tags._opid.idiv = function(left, right)
UNPACK = (list, i, j)
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
end
APPEND = (t, toAppend)
return "do" .. indent() .. "local a, p = { " .. toAppend .. " }, #" .. t .. "+1" .. newline() .. "for i=1, #a do" .. indent() .. t .. "[p] = a[i]" .. newline() .. "p = p + 1" .. unindent() .. "end" .. unindent() .. "end"
end
tags._opid.idiv = (left, right)
return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")"
end
tags._opid.band = function(left, right)
tags._opid.band = (left, right)
addRequire("bit", "band", "band")
return getRequire("band") .. "(" .. lua(left) .. ", " .. lua(right) .. ")"
end
tags._opid.bor = function(left, right)
tags._opid.bor = (left, right)
addRequire("bit", "bor", "bor")
return getRequire("bor") .. "(" .. lua(left) .. ", " .. lua(right) .. ")"
end
tags._opid.bxor = function(left, right)
tags._opid.bxor = (left, right)
addRequire("bit", "bxor", "bxor")
return getRequire("bxor") .. "(" .. lua(left) .. ", " .. lua(right) .. ")"
end
tags._opid.shl = function(left, right)
tags._opid.shl = (left, right)
addRequire("bit", "lshift", "lshift")
return getRequire("lshift") .. "(" .. lua(left) .. ", " .. lua(right) .. ")"
end
tags._opid.shr = function(left, right)
tags._opid.shr = (left, right)
addRequire("bit", "rshift", "rshift")
return getRequire("rshift") .. "(" .. lua(left) .. ", " .. lua(right) .. ")"
end
tags._opid.bnot = function(right)
tags._opid.bnot = (right)
addRequire("bit", "bnot", "bnot")
return getRequire("bnot") .. "(" .. lua(right) .. ")"
end

View file

@ -171,7 +171,7 @@ end
local function traverse_assignment (env, stm)
local status, msg = traverse_varlist(env, stm[1])
if not status then return status, msg end
status, msg = traverse_explist(env, stm[2])
status, msg = traverse_explist(env, stm[#stm])
if not status then return status, msg end
return true
end
@ -372,7 +372,7 @@ function traverse_stm (env, stm)
local tag = stm.tag
if tag == "Do" then -- `Do{ stat* }
return traverse_block(env, stm)
elseif tag == "Set" then -- `Set{ {lhs+} {expr+} }
elseif tag == "Set" then -- `Set{ {lhs+} (opid? = opid?)? {expr+} }
return traverse_assignment(env, stm)
elseif tag == "While" then -- `While{ expr block }
return traverse_while(env, stm)

View file

@ -1,12 +1,12 @@
package = "candran"
version = "0.4.0-1"
version = "0.5.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.
Candran is a dialect of the Lua 5.3 programming language which compiles to Lua 5.3 and Lua 5.1/LuaJit. It adds several useful syntax additions which aims to make Lua faster and easier to write, and a simple preprocessor.
Unlike Moonscript, Candran tries to stay close to the Lua syntax, and existing Lua code can run on Candran unmodified.
]],
license = "MIT",
homepage = "https://github.com/Reuh/candran",
@ -17,7 +17,7 @@ description = {
source = {
url = "git://github.com/Reuh/candran",
tag = "v0.4.0"
tag = "v0.5.0"
}
dependencies = {