#import("util") #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") local candran = { VERSION = "0.2.0" } --- Run the preprocessor -- @tparam input string input code -- @tparam args table arguments for the preprocessor. They will be inserted into the preprocessor environement. -- @treturn output string output code function candran.preprocess(input, args={}) -- generate preprocessor code local preprocessor = "" for line in (input.."\n"):gmatch("(.-\n)") do if line:match("^%s*#") and not line:match("^#!") then -- exclude shebang preprocessor ..= line:gsub("^%s*#", "") else preprocessor ..= ("write(%q)"):format(line:sub(1, -2)) .. "\n" end end 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 --- Candran library table env.candran = candran --- Current preprocessor output env.output = "" --- Import an external Candran/Lua module into the generated file -- @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) 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 local modcontent = candran.preprocess(f:read("*a"), margs) f:close() -- get module name (ex: module name of path.to.module is module) local modname = modpath:match("[^%.]+$") env.write( "-- 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.."\" --" ) end --- Include another file content in the preprocessor output. -- @tparam file string filepath env.include = function(file) local f = io.open(file) if not f then error("Can't open the file "..file.." to include") end env.write(f:read("*a")) f:close() end --- Write a line in the preprocessor output. -- @tparam ... string strings to write (similar to print) env.write = function(...) env.output ..= table.concat({...}, "\t") .. "\n" end --- Will be replaced with the content of the variable with the given name, if it exists. -- @tparam name string variable name env.placeholder = function(name) if env[name] then env.write(env[name]) end end -- compile & load preprocessor local preprocess, err = util.loadenv(candran.compile(preprocessor, args.target), "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 return output end -- Compiler function candran.compile(input, target="lua53") local parse = require("lua-parser.parser").parse local ast, errmsg = parse(input, "candran") if not ast then error("Compiler: error while parsing file: "..errmsg) end return require("compiler."..target)(ast) end -- Preprocess & compile function candran.make(code, args={}) return candran.compile(candran.preprocess(code, args), args.target) 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") f:close() return load(candran.make(modcontent)) end return candran