|
|
||
|---|---|---|
| bin | ||
| compiler | ||
| lib | ||
| rockspec | ||
| test | ||
| candran.can | ||
| candran.lua | ||
| ideas.txt | ||
| LICENSE | ||
| README.md | ||
Candran
Candran is a dialect of the Lua 5.3 programming language which compiles to Lua 5.3, LuaJIT and Lua 5.1. 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.
#import("lib.thing") -- static import
#local debug or= false
local function calculate(toadd=25) -- default parameters
local result = thing.do()
result += toadd
#if debug then -- preprocessor conditionals
print("Did something")
#end
return result
end
let a = {
hey = true,
method = :(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
}
a:method(42, (foo)
return "something " .. foo
end)
local odd = [ -- table comprehension (kind of)
for i=1, 10 do
if i%2 == 0 then
continue -- continue keyword
end
i -- implicit push
end
]
local count = [for i=1,10 i] -- single line statements
local a = if condition then "one" else "two" end -- statement as expressions
print("Hello %s":format("world")) -- methods calls on strings (and tables) litterals without enclosing parantheses
Candran is released under the MIT License (see LICENSE for details).
Quick setup
Install Candran automatically using LuaRocks: sudo luarocks install rockspec/candran-0.8.0-1.rockspec.
Or manually install LPegLabel (luarocks install LPegLabel, version 1.5 or above), 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:
- Sublime Text 3:
- sublime-candran support the full Candran syntax
- SublimeLinter-candran-contrib SublimeLinter plugin for Candran
- Atom: language-candran support the full Candran syntax
The language
Syntax additions
After the preprocessor is run the Candran code is compiled to Lua. Candran code adds the folowing syntax to Lua:
Assignment operators
var += nbvar -= nbvar *= nbvar /= nbvar //= nbvar ^= nbvar %= nbvar ..= strvar and= strvar or= strvar &= nbvar |= nbvar <<= nbvar >>= nb
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.
Please note that the Lua code a=-1 will be compiled into a = 1 - a and not a = -1! Write good code, write spaced code: a = -1 works as expected.
Default function parameters
function foo(bar = "default", other = thing.do())
-- stuff
end
If an argument isn't provided or nil when the function is called, it will be automatically set to a default value.
It is equivalent to doing if arg == nil then arg = default end for each argument at the start of the function.
The default values can be complete Lua expressions, and will be evaluated each time the function is run.
Short anonymous function declaration
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.
@ self aliases
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.
let variable declaration
let a = {
foo = function()
print(type(a)) -- table
end
}
Similar to local, but the variable will be declared before the assignemnt (i.e. it will compile into local a; a = value), so you can access it from functions defined in the value.
Can also be used as a shorter name for local.
continue keyword
for i=1, 10 do
if i % 2 == 0 then
continue
end
print(i) -- 1, 3, 5, 7, 9
end
Will skip the current loop iteration.
push keyword
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. It's recommended to use the implicit push instead (when possible).
Implicit push
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.
Table comprehension
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.
Suffixable string and table litterals
"some text":upper() -- same as ("some text"):upper() in Lua
"string".upper -- the actual string.upper function. "string"["upper"] also works.
{thing = 3}.thing -- 3. Also works with tables!
[for i=0,5 do i*i end][3] -- 9. And table comprehensions!
-- Functions calls have priority:
someFunction"thing":upper() -- same as (someFunction("thing")):upper() (i.e., the way it would be parsed by Lua)
String litterals, table litterals, and comprehensions can be suffixed with : method calls, . indexing, or [ indexing, without needing to be enclosed in parantheses.
Please note, that "normal" functions calls have priority over this syntax, in order to maintain Lua compatibility.
Statement expressions
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.
One line statements
if condition()
a()
elseif foo()
b()
if other()
a()
else -- "end" is always needed for else!
c()
end
if, elseif, for, and while statements can be writtent without do, then or end, in which case they contain a single statement.
Preprocessor
Before compiling, Candran's preprocessor is run. It execute every line starting with a # (ignoring prefixing whitespace, long strings and comments) as Candran code. For example,
#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(defaulttrue):trueto automatically load the module into a local variable (i.e.local thing = require("module.thing"));loadPackage(defaulttrue):trueto automatically load the module into the loaded packages table (so it will be available for followingrequire("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 outputhello()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 Lua 5.3, LuaJIT, and Lua 5.1.
To chose a compile target, set the target option to lua53, luajit, or lua51 in the option table when using the library or the command line tools. Candran will try detect the currently used Lua version and use it as a target by default.
For the luajit and lua51 targets, Lua 5.3 specific syntax (bitwise operators, integer division) will automatically be translated to valid Lua 5.1 syntax, 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 ("PUC Lua").
Please note that Candran only translates syntax, and will not try to do anything about changes in the Lua standard library (for example, the new utf8 module). If you need this, you should be able to use lua-compat-5.3 along with Candran.
Usage
Command-line usage
The library can be used standalone through the canc and can utility:
-
cancDisplay the information text (version and basic command-line usage).
-
canc [options] filename...Preprocess and compile each filename Candran files, and creates the assiociated
.luafiles in the same directories.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=destinationDirectoryargument.You can choose the output filename using
out=filename. By default, compiled files have the same name as their input file, but with a.luaextension.canccan write to the standard output instead of creating files using the-printargument.You can choose to run only the preprocessor or compile using the
-preprocessand-compileflags.You can choose to only parse the file and check it for syntaxic errors using the
-parseflag. Errors will be printed to stderr in a similar format toluac -p.The
-astflag is also available for debugging, and will disable preprocessing, compiling and file writing, and instead directly dump the AST generated from the input file(s) to stdout.Instead of providing filenames, you can use
-to read from standard input.-
example uses :
canc foo.canpreprocess and compile foo.can and write the result in foo.lua.
canc indentation=" " foo.canpreprocess and compile foo.can with 2-space indentation (readable code!) and write the result in foo.lua.
canc foo.can -verbose -print | luapreprocess foo.can with verbose set to true, compile it and execute it.
canc -parse foo.canchecks foo.can for syntaxic errors.
-
-
canStart a simplisitic Candran REPL.
-
can [options] filenamePreprocess, 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.
Instead of providing a filename, you can use
-to read from standard input.
Library usage
Candran can also be used as a Lua library. For example,
local candran = require("candran")
local f = io.open("foo.can")
local contents = f:read("*a")
f:close()
local compiled = candran.make(contents, { lang = "fr" })
load(compiled)()
-- or simpler...
candran.dofile("foo.can")
-- or, if you want to be able to directly load Candran files using require("module")
candran.setup()
local foo = require("foo")
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[, 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
require("candran").setup()
at the top of your main Lua file. If a Candran file 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, and return thecandrantable.
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:
target = "lua53" -- Compiler target. "lua53", "luajit" or "lua51" (default is automatically selected based on the Lua version used).
indentation = "" -- Character(s) used for indentation in the compiled file.
newline = "\n" -- Character(s) used for newlines in the compiled file.
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().
You can change these values in the table candran.default.
There are also a few function-specific options available, see the associated functions documentation for more information.
Compiling the library
The Candran library itself is written is Candran, so you have to compile it with an already compiled Candran library.
The compiled candran.lua should include every Lua library needed to run it. You will still need to install LPegLabel.
This command will use the precompilled version of this repository (candran.lua) to compile candran.can and write the result in candran.lua :
canc candran.can
You can then run the tests on your build :
cd test
lua test.lua ../candran.lua