diff --git a/README.md b/README.md index fdd8804..3172e98 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Will output ````print("Bonjour")```` or ````print("Hello")```` depending of the The preprocessor has access to the following variables : * ````candran```` : the Candran library table. * ````output```` : the current preprocessor output string. -* ````import(module[, [args, autoRequire]])```` : a function which import a module. This is equivalent to use _require(module)_ in the Candran code, except the module will be embedded in the current file. _args_ is an optional preprocessor arguments table for the imported module (current preprocessor arguments will be inherited). _autoRequire_ (boolean, default true) indicate if the module should be automaticaly loaded in a local variable or not. If true, the local variable will have the name of the module. +* ````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. @@ -255,6 +255,8 @@ chunkname = "nil" -- The chunkname used when running code using the helper funct rewriteErrors = true -- True to enable error rewriting when loading code using the helper functions. Will wrap the whole code in a xpcall(). ``` +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. diff --git a/candran.can b/candran.can index 5e9ef68..a824673 100644 --- a/candran.can +++ b/candran.can @@ -10,7 +10,7 @@ #import("lib.lua-parser.parser") local candran = { - VERSION = "0.3.0" + VERSION = "0.3.1" } --- Default options. @@ -53,17 +53,19 @@ function candran.preprocess(input, options={}) --- Current preprocessor output env.output = "" --- Import an external Candran/Lua module into the generated file + -- Notable options: + -- * loadLocal (true): true to automatically load the module into a local variable + -- * loadPackage (true): true to automatically load the module into the loaded packages table -- @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={}, autoRequire=true) + -- @tparam margs table preprocessor options to use when preprocessessing the module + env.import = function(modpath, margs={}) 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 - margs = util.merge(options, { chunkname = filepath }, margs) + margs = util.merge(options, { chunkname = filepath, loadLocal = true, loadPackage = true }, margs) local modcontent = candran.preprocess(f:read("*a"), margs) f:close() @@ -71,13 +73,13 @@ function candran.preprocess(input, options={}) local modname = modpath:match("[^%.]+$") env.write( - "-- MODULE \""..modpath.."\" --\n".. + "-- MODULE "..modpath.." --\n".. "local function _()\n".. modcontent.."\n".. "end\n".. - (autoRequire and "local "..modname.." = _() or "..modname.."\n" or "").. -- auto require - "package.loaded[\""..modpath.."\"] = "..(autoRequire and modname or "_()").." or true\n".. -- add to package.loaded - "-- END OF MODULE \""..modpath.."\" --" + (margs.loadLocal and ("local %s = _() or %s\n"):format(modname, modname) or "").. -- auto require + (margs.loadPackage and ("package.loaded[%q] = %s or true\n"):format(modpath, margs.loadLocal and modname or "_()") or "").. -- add to package.loaded + "-- END OF MODULE "..modpath.." --" ) end --- Include another file content in the preprocessor output. diff --git a/candran.lua b/candran.lua index 7bf6777..59277ab 100644 --- a/candran.lua +++ b/candran.lua @@ -651,7 +651,6 @@ return requireStr .. lua(ast) .. newline() end end local lua53 = _() or lua53 -package["loaded"]["compiler.lua53"] = lua53 or true return lua53 end local luajit = _() or luajit @@ -1808,7 +1807,7 @@ return parser end local parser = _() or parser package["loaded"]["lib.lua-parser.parser"] = parser or true -local candran = { ["VERSION"] = "0.3.0" } +local candran = { ["VERSION"] = "0.3.1" } local default = { ["target"] = "lua53", ["indentation"] = "", ["newline"] = "\ ", ["requirePrefix"] = "CANDRAN_", ["mapLines"] = true, ["chunkname"] = "nil", ["rewriteErrors"] = true @@ -1836,25 +1835,26 @@ preprocessor = preprocessor .. "return output" local env = util["merge"](_G, options) env["candran"] = candran env["output"] = "" -env["import"] = function(modpath, margs, autoRequire) +env["import"] = function(modpath, margs) if margs == nil then margs = {} end -if autoRequire == nil then autoRequire = true end local filepath = assert(util["search"](modpath), "No module named \"" .. modpath .. "\"") local f = io["open"](filepath) if not f then error("Can't open the module file to import") end -margs = util["merge"](options, { ["chunkname"] = filepath }, margs) +margs = util["merge"](options, { +["chunkname"] = filepath, ["loadLocal"] = true, ["loadPackage"] = true +}, margs) local modcontent = candran["preprocess"](f:read("*a"), margs) f:close() local modname = modpath:match("[^%.]+$") -env["write"]("-- MODULE \"" .. modpath .. "\" --\ +env["write"]("-- MODULE " .. modpath .. " --\ " .. "local function _()\ " .. modcontent .. "\ " .. "end\ -" .. (autoRequire and "local " .. modname .. " = _() or " .. modname .. "\ -" or "") .. "package.loaded[\"" .. modpath .. "\"] = " .. (autoRequire and modname or "_()") .. " or true\ -" .. "-- END OF MODULE \"" .. modpath .. "\" --") +" .. (margs["loadLocal"] and ("local %s = _() or %s\ +"):format(modname, modname) or "") .. (margs["loadPackage"] and ("package.loaded[%q] = %s or true\ +"):format(modpath, margs["loadLocal"] and modname or "_()") or "") .. "-- END OF MODULE " .. modpath .. " --") end env["include"] = function(file) local f = io["open"](file) diff --git a/compiler/luajit.can b/compiler/luajit.can index 150891a..e8929cc 100644 --- a/compiler/luajit.can +++ b/compiler/luajit.can @@ -28,6 +28,6 @@ end #local patch = output #output = "" -#import("compiler.lua53", { patch = patch }) +#import("compiler.lua53", { patch = patch, loadPackage = false }) return lua53 diff --git a/rockspec/candran-0.3.0-1.rockspec b/rockspec/candran-0.3.1-1.rockspec similarity index 95% rename from rockspec/candran-0.3.0-1.rockspec rename to rockspec/candran-0.3.1-1.rockspec index 7cb7929..296bf54 100644 --- a/rockspec/candran-0.3.0-1.rockspec +++ b/rockspec/candran-0.3.1-1.rockspec @@ -1,6 +1,6 @@ package = "Candran" -version = "0.3.0-1" +version = "0.3.1-1" description = { summary = "A simple Lua dialect and preprocessor.", @@ -17,7 +17,7 @@ description = { source = { url = "git://github.com/Reuh/Candran", - tag = "v0.3.0" + tag = "v0.3.1" } dependencies = { diff --git a/test/test.lua b/test/test.lua index 4653ea2..8534519 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1,20 +1,16 @@ -print("========================") -print("|| CANDRAN TESTS ||") -print("========================") - local candran = dofile(arg[1] or "../candran.lua") -- test helper local results = {} -- tests result -local function test(name, candranCode, result, args) +local function test(name, candranCode, expectedResult, options) results[name] = { result = "not finished", message = "no info" } local self = results[name] -- make code - local success, code = pcall(candran.make, candranCode, args) + local success, code = pcall(candran.make, candranCode, options) if not success then self.result = "error" - self.message = "error while making code :\n"..code + self.message = "error while making code:\n"..code return end @@ -22,7 +18,7 @@ local function test(name, candranCode, result, args) local success, func = pcall(loadstring or load, code) if not success then self.result = "error" - self.message = "error while loading code :\n"..func + self.message = "error while loading code:\n"..func return end @@ -30,14 +26,14 @@ local function test(name, candranCode, result, args) local success, output = pcall(func) if not success then self.result = "error" - self.message = "error while running code :\n"..output + self.message = "error while running code:\n"..output return end -- check result - if output ~= result then + if output ~= expectedResult then self.result = "fail" - self.message = "invalid result from the code; it returned "..tostring(output).." instead of "..tostring(result) + self.message = "invalid result from the code; it returned "..tostring(output).." instead of "..tostring(expectedResult) return else self.result = "success" @@ -48,10 +44,15 @@ end -- tests print("Running tests...") +------------------ +-- PREPROCESSOR -- +------------------ + test("preprocessor", [[ #local foo = true return true ]], true) + test("preprocessor condition", [[ #local foo = true #if not foo then @@ -60,23 +61,45 @@ test("preprocessor condition", [[ return true #end ]], true) -test("preprocessor args table", [[ + +test("preprocessor candran table", [[ +#write(("return %q"):format(candran.VERSION)) +]], candran.VERSION) + +test("preprocessor output variable", [[ +#output = "return 5" +]], 5) + +test("preprocessor import function", [[ +#import("toInclude") +return toInclude +]], 5) + +test("preprocessor include function", [[ +#include('toInclude.lua') +]], 5) + +test("preprocessor write function", [[ +#write("local a = true") +return a +]], true) + +test("preprocessor placeholder function", [[ +#placeholder('foo') +]], 5, { foo = "return 5" }) + +test("preprocessor options", [[ #if not foo == "sky" then # error("Invalid foo argument") #end return true ]], true, { foo = "sky" }) -test("preprocessor write function", [[ -#write("local a = true") -return a -]], true) -test("preprocessor import function", [[ -#import("toInclude") -return toInclude -]], 5) -test("preprocessor include function", "a = [[\n#include('toInclude.lua')\n]]\nreturn a", - "local a = 5\nreturn a\n\n") +---------------------- +-- SYNTAX ADDITIONS -- +---------------------- + +-- Assignement operators test("+=", [[ local a = 5 a += 2 @@ -97,6 +120,11 @@ local a = 5 a /= 2 return a ]], 5/2) +test("//=", [[ +local a = 5 +a //= 2 +return a +]], 2) test("^=", [[ local a = 5 a ^= 2 @@ -112,6 +140,72 @@ local a = "hello" a ..= " world" return a ]], "hello world") +test("and=", [[ +local a = true +a and= "world" +return a +]], "world") +test("or=", [[ +local a = false +a or= "world" +return a +]], "world") +test("&=", [[ +local a = 5 +a &= 3 +return a +]], 1) +test("|=", [[ +local a = 5 +a |= 3 +return a +]], 7) +test("<<=", [[ +local a = 23 +a <<= 2 +return a +]], 92) +test(">>=", [[ +local a = 23 +a >>= 2 +return a +]], 5) + +test("right assigments operators", [[ +local a = 5 +a =+ 2 assert(a == 7, "=+") +a =- 2 assert(a == -5, "=-") +a =* -2 assert(a == 10, "=*") +a =/ 2 assert(a == 0.2, "=/") +a =// 2 assert(a == 10, "=//") +a =^ 2 assert(a == 1024, "=^") +a =% 2000 assert(a == 976, "=%") + +a = "world" +a =.. "hello " assert(a == "hello world", "=..") +a =and true assert(a == "hello world", "=and") + +a = false +a =or nil assert(a == false, "=or") + +a = 3 +a =& 5 assert(a == 1, '=&') +a =| 20 assert(a == 21, "=|") +a =<< 1 assert(a == 2097152, "=<<") + +a = 2 +a =>> 23 assert(a == 5, "=>>") +]], nil) + +test("some left+right assigments operators", [[ +local a = 5 +a -=+ 2 assert(a == 8, "-=+") + +a = "hello" +a ..=.. " world " assert(a == "hello world hello", "..=..") +]], nil) + +-- Default function parameters test("default parameters", [[ local function test(hey, def="re", no, foo=("bar"):gsub("bar", "batru")) return def..foo @@ -119,22 +213,68 @@ end return test(78, "SANDWICH", true) ]], "SANDWICHbatru") --- results -print("=====================") -print("|| RESULTS ||") -print("=====================") +-- @ self alias +test("@ as self alias", [[ +local a = {} +function a:hey() + return @ == self +end +return a:hey() +]], true) +test("@ as self alias and indexation", [[ +local a = { + foo = "Hoi" +} +function a:hey() + return @.foo +end +return a:hey() +]], "Hoi") +test("@name indexation", [[ +local a = { + foo = "Hoi" +} +function a:hey() + return @foo +end +return a:hey() +]], "Hoi") +test("@[expt] indexation", [[ +local a = { + foo = "Hoi" +} +function a:hey() + return @["foo"] +end +return a:hey() +]], "Hoi") +-- Short anonymous functions declaration +test("short anonymous function declaration", [[ +local a = (arg1) + return arg1 +end +return a(5) +]], 5) +test("short anonymous method declaration", [[ +local a = :(arg1) + return self + arg1 +end +return a(2, 3) +]], 5) + +-- results local resultCounter = {} local testCounter = 0 -for name, test in pairs(results) do +for name, t in pairs(results) do -- print errors & fails - if test.result ~= "success" then - print("Test \""..name.."\" : "..test.result) - if test.message then print(test.message) end + if t.result ~= "success" then + print(name.." test: "..t.result) + if t.message then print(t.message) end print("----------") end -- count tests results - resultCounter[test.result] = (resultCounter[test.result] or 0) + 1 + resultCounter[t.result] = (resultCounter[t.result] or 0) + 1 testCounter = testCounter + 1 end