--[[ Candran language, preprocessor and compiler by Thomas99. LICENSE : Copyright (c) 2015 Thomas99 This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ]] local candran = { VERSION = "0.1.0", syntax = { assignment = { "+=", "-=", "*=", "/=", "^=", "%=", "..=" }, decorator = "@" } } package.loaded["candran"] = candran #import("lib.table") #import("lib.LuaMinify.Util") #import("lib.LuaMinify.Scope") #import("lib.LuaMinify.ParseCandran") #import("lib.LuaMinify.FormatIdentityCandran") -- Preprocessor function candran.preprocess(input, args) -- generate preprocessor local preprocessor = "return function()\n" local lines = {} for line in (input.."\n"):gmatch("(.-)\n") do table.insert(lines, line) -- preprocessor instructions (exclude shebang) if line:match("^%s*#") and not (line:match("^#!") and #lines == 1) then preprocessor ..= line:gsub("^%s*#", "") .. "\n" else preprocessor ..= "output ..= lines[" .. #lines .. "] .. \"\\n\"\n" end end preprocessor ..= "return output\nend" -- make preprocessor environement local env = table.copy(_G) env.candran = candran env.output = "" env.import = function(modpath, autoRequire) local autoRequire = (autoRequire == nil) or autoRequire -- get module filepath local filepath for _,search in ipairs(package.searchers) do local loader, path = search(modpath) if type(loader) == "function" and type(path) == "string" then filepath = path break end end if not filepath then error("No module named \""..modpath.."\"") 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() -- get module name (ex: module name of path.to.module is module) local modname = modpath:match("[^%.]+$") -- env.output ..= "-- IMPORT OF 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 IMPORT OF MODULE \""..modpath.."\" --\n" end env.include = function(file) local f = io.open(file) if not f then error("Can't open the file to include") end env.output ..= f:read("*a").."\n" f:close() end env.print = function(...) env.output ..= table.concat({...}, "\t") .. "\n" end env.args = args or {} env.lines = lines -- load preprocessor local preprocess, err = load(candran.compile(preprocessor), "Preprocessor", nil, env) if not preprocess then error("Error while creating preprocessor :\n" .. err) end -- execute preprocessor local success, output = pcall(preprocess()) if not success then error("Error while preprocessing file :\n" .. output .. "\nWith preprocessor : \n" .. preprocessor) end return output end -- Compiler function candran.compile(input) local parse = require("lib.LuaMinify.ParseCandran") local format = require("lib.LuaMinify.FormatIdentityCandran") local success, ast = parse.ParseLua(input) if not success then error("Error while parsing the file :\n"..tostring(ast)) end local success, output = format(ast) if not success then error("Error while formating the file :\n"..tostring(output)) end return output end -- Preprocess & compile function candran.make(code, args) local preprocessed = candran.preprocess(code, args or {}) local output = candran.compile(preprocessed) return output end -- Standalone mode if debug.getinfo(3) == nil and arg then -- Check args if #arg < 1 then print("Candran version "..candran.VERSION.." by Thomas99") print("Command-line usage :") print("lua candran.lua [preprocessor arguments]") return candran end -- Parse args local inputFilepath = arg[1] local args = {} for i=2, #arg, 1 do if arg[i]:sub(1,2) == "--" then args[arg[i]:sub(3)] = arg[i+1] i = i + 1 -- skip argument value end end -- Open & read input file local inputFile, err = io.open(inputFilepath, "r") if not inputFile then error("Error while opening input file : "..err) end local input = inputFile:read("*a") inputFile:close() -- Make print(candran.make(input, args)) end return candran