1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2025-10-27 09:59:29 +00:00

Added cancheck; candran.compile, .make and .preprocess returns nil, err instead of throwing an error; can and canc error output should now be similar to Lua

This commit is contained in:
Étienne Fildadut 2020-04-06 21:30:57 +02:00
parent dc19ac56a9
commit 1de0aafa5b
9 changed files with 412 additions and 226 deletions

11
bin/can
View file

@ -1,4 +1,5 @@
#!/bin/lua #!/usr/bin/env lua
local candran = require("candran") local candran = require("candran")
local cmdline = require("candran.cmdline") local cmdline = require("candran.cmdline")
@ -29,7 +30,13 @@ if arg[#arg] == "-" then
f() f()
-- file -- file
elseif #args >= 1 then elseif #args >= 1 then
candran.dofile(args[1], args) local f, err = candran.loadfile(args[1], nil, args)
if not f then
print("can: "..err)
os.exit(1)
else
f()
end
-- REPL -- REPL
else else
-- Setup linenoise -- Setup linenoise

View file

@ -1,4 +1,5 @@
#!/bin/lua #!/usr/bin/env lua
local candran = require("candran") local candran = require("candran")
local cmdline = require("candran.cmdline") local cmdline = require("candran.cmdline")
local parse = require("candran.can-parser.parser").parse local parse = require("candran.can-parser.parser").parse
@ -78,13 +79,28 @@ for _, file in ipairs(args) do
local out = input local out = input
if args.preprocess then if args.preprocess then
out = candran.preprocess(out, args) local r, err = candran.preprocess(out, args)
if not r then
print("canc: "..err)
os.exit(1)
end
out = r
end end
if args.compile then if args.compile then
out = candran.compile(out, args) local r, err = candran.compile(out, args)
if not r then
print("canc: "..err)
os.exit(1)
end
out = r
end end
if args.compile == nil and args.preprocess == nil then if args.compile == nil and args.preprocess == nil then
out = candran.make(input, args) local r, err = candran.make(input, args)
if not r then
print("canc: "..err)
os.exit(1)
end
out = r
end end
if args.print then if args.print then

116
bin/cancheck Normal file
View file

@ -0,0 +1,116 @@
#!/usr/bin/env lua
-- Monkey patch Luacheck (tested against version 0.23.0) to support Candran files
local candran = require("candran")
local function pattern(token)
return "()"..token:gsub("[^%w]", "%%%0").."()"
end
local tokenAlias = {
["self"] = { "@", ":" }
}
-- Patch checker
local oldCheck = require("luacheck.check")
local function check(can)
local lua, err = candran.make(can)
if lua then
local r = oldCheck(lua)
-- Calculate Candran file position.
if #r.warnings > 0 then
local lua_lines = {}
for l in (lua.."\n"):gmatch("([^\n]*)\n") do
table.insert(lua_lines, l)
end
local can_lines = {}
for l in (can.."\n"):gmatch("([^\n]*)\n") do
table.insert(can_lines, l)
end
for _, warning in ipairs(r.warnings) do
-- candran line
local lua_line = lua_lines[warning.line]
warning.can_line = tonumber(lua_line:match(".*%-%- .-%:(%d+)$"))
-- candran column
local can_line = can_lines[warning.can_line]
local lua_token = lua_line:sub(warning.column, warning.end_column)
local token_pattern = pattern(lua_token) -- token finding pattern
-- the warning happens on the n-th instance of lua_token on this line
local lua_n = 1
for start in lua_line:gmatch(token_pattern) do
if start >= warning.column then
break
end
lua_n = lua_n + 1
end
-- Find associated candran token. If lua_n > can_nmax, the last found lua_token is used.
-- This approximation should work in like, 90% of cases.
local can_n = 1
local pos = 1
while can_n <= lua_n do
-- find first token or alias of this token
local start, stop = can_line:match(token_pattern, pos)
if tokenAlias[lua_token] then
for _, token in ipairs(tokenAlias[lua_token]) do
local nstart, nstop = can_line:match(pattern(token), pos)
if nstart and (not start or nstart < start) then
start, stop = nstart, nstop
end
end
end
-- found
if start then
pos = stop
warning.can_column, warning.can_end_column = start, stop
can_n = can_n + 1
else
break
end
end
end
end
return r
else
local line, column, msg = err:match(":(%d+):(%d+):%s*(.*)$")
local syntax_error = {
code = "011",
line = line,
column = column,
end_column = column,
msg = msg
}
return {
warnings = {syntax_error},
inline_options = {},
line_lengths = {},
line_endings = {}
}
end
end
package.loaded["luacheck.check"] = check
-- Patch formatter
local format = require("luacheck.format")
local function format_location(file, location, opts)
local res = ("%s:%d:%d"):format(file, location.can_line or location.line, location.can_column or location.column)
if opts.ranges then
res = ("%s-%d"):format(res, location.can_end_column or location.end_column)
end
return res
end
local function setupvalue(fn, val, name, ...)
for i=1, debug.getinfo(fn, "u").nups do
local n, v = debug.getupvalue(fn, i)
if n == name then
if not ... then
debug.setupvalue(fn, i, val)
else
setupvalue(v, val, ...)
end
end
end
end
setupvalue(format.builtin_formatters.plain, format_location, "format_event", "format_location")
require("luacheck.main")

View file

@ -11,7 +11,7 @@
#import("candran.can-parser.parser") #import("candran.can-parser.parser")
local candran = { local candran = {
VERSION = "0.11.0" VERSION = "0.12.0"
} }
--- Default options. --- Default options.
@ -37,7 +37,9 @@ end
--- Run the preprocessor --- Run the preprocessor
-- @tparam input string input code -- @tparam input string input code
-- @tparam options table arguments for the preprocessor. They will be inserted into the preprocessor environement. -- @tparam options table arguments for the preprocessor. They will be inserted into the preprocessor environement.
-- @treturn output string output code -- @treturn[1] output string output code
-- @treturn[2] nil nil if error
-- @treturn[2] error string error message
function candran.preprocess(input, options={}) function candran.preprocess(input, options={})
options = util.merge(candran.default, options) options = util.merge(candran.default, options)
@ -90,10 +92,10 @@ function candran.preprocess(input, options={})
-- open module file -- open module file
local f = io.open(filepath) local f = io.open(filepath)
if not f then error("Can't open the module file to import") end if not f then error("can't open the module file to import") end
margs = util.merge(options, { chunkname = filepath, loadLocal = true, loadPackage = true }, margs) margs = util.merge(options, { chunkname = filepath, loadLocal = true, loadPackage = true }, margs)
local modcontent = candran.preprocess(f:read("*a"), margs) local modcontent = assert(candran.preprocess(f:read("*a"), margs))
f:close() f:close()
-- get module name (ex: module name of path.to.module is module) -- get module name (ex: module name of path.to.module is module)
@ -113,7 +115,7 @@ function candran.preprocess(input, options={})
-- @tparam file string filepath -- @tparam file string filepath
env.include = function(file) env.include = function(file)
local f = io.open(file) local f = io.open(file)
if not f then error("Can't open the file "..file.." to include") end if not f then error("can't open the file "..file.." to include") end
env.write(f:read("*a")) env.write(f:read("*a"))
f:close() f:close()
end end
@ -131,12 +133,21 @@ function candran.preprocess(input, options={})
end end
-- compile & load preprocessor -- compile & load preprocessor
local preprocess, err = util.load(candran.compile(preprocessor, args), "candran preprocessor", env) local preprocess, err = candran.compile(preprocessor, options)
if not preprocess then error("Error while creating Candran preprocessor: " .. err) end if not preprocess then
return nil, "in preprocessor: "..err
end
preprocess, err = util.load(preprocessor, "candran preprocessor", env)
if not preprocess then
return nil, "in preprocessor: "..err
end
-- execute preprocessor -- execute preprocessor
local success, output = pcall(preprocess) local success, output = pcall(preprocess)
if not success then error("Error while preprocessing file: " .. output) end if not success then
return nil, "in preprocessor: "..output
end
return output return output
end end
@ -144,14 +155,16 @@ end
--- Run the compiler --- Run the compiler
-- @tparam input string input code -- @tparam input string input code
-- @tparam options table options for the compiler -- @tparam options table options for the compiler
-- @treturn output string output code -- @treturn[1] output string output code
-- @treturn[2] nil nil if error
-- @treturn[2] error string error message
function candran.compile(input, options={}) function candran.compile(input, options={})
options = util.merge(candran.default, options) options = util.merge(candran.default, options)
local ast, errmsg = parser.parse(input, options.chunkname) local ast, errmsg = parser.parse(input, options.chunkname)
if not ast then if not ast then
error("Compiler: error while parsing file: "..errmsg) return nil, errmsg
end end
return require("compiler."..options.target)(input, ast, options) return require("compiler."..options.target)(input, ast, options)
@ -160,9 +173,18 @@ end
--- Preprocess & compile code --- Preprocess & compile code
-- @tparam code string input code -- @tparam code string input code
-- @tparam options table arguments for the preprocessor and compiler -- @tparam options table arguments for the preprocessor and compiler
-- @treturn output string output code -- @treturn[1] output string output code
-- @treturn[2] nil nil if error
-- @treturn[2] error string error message
function candran.make(code, options) function candran.make(code, options)
return candran.compile(candran.preprocess(code, options), options) local r, err = candran.preprocess(code, options)
if r then
r, err = candran.compile(r, options)
if r then
return r
end
end
return r, err
end end
local errorRewritingActive = false local errorRewritingActive = false
@ -171,7 +193,9 @@ local codeCache = {}
-- Will rewrite errors by default. -- Will rewrite errors by default.
function candran.loadfile(filepath, env, options) function candran.loadfile(filepath, env, options)
local f, err = io.open(filepath) local f, err = io.open(filepath)
if not f then error("can't open the file: "..err) end if not f then
return nil, "cannot open %s":format(err)
end
local content = f:read("*a") local content = f:read("*a")
f:close() f:close()
@ -183,14 +207,19 @@ end
function candran.load(chunk, chunkname, env, options={}) function candran.load(chunk, chunkname, env, options={})
options = util.merge({ chunkname = tostring(chunkname or chunk) }, options) options = util.merge({ chunkname = tostring(chunkname or chunk) }, options)
codeCache[options.chunkname] = candran.make(chunk, options) local code, err = candran.make(chunk, options)
local f, err = util.load(codeCache[options.chunkname], options.chunkname, env) if not code then
return code, err
end
codeCache[options.chunkname] = code
local f, err = util.load(code, options.chunkname, env)
-- Um. Candran isn't supposed to generate invalid Lua code, so this is a major issue. -- Um. Candran isn't supposed to generate invalid Lua code, so this is a major issue.
-- This is not going to raise an error because this is supposed to behave similarly to Lua's load function. -- This is not going to raise an error because this is supposed to behave similarly to Lua's load function.
-- But the error message will likely be useless unless you know how Candran works. -- But the error message will likely be useless unless you know how Candran works.
if f == nil then if f == nil then
return f, "Candran unexpectedly generated invalid code: "..err return f, "candran unexpectedly generated invalid code: "..err
end end
if options.rewriteErrors == false then if options.rewriteErrors == false then
@ -248,7 +277,7 @@ function candran.messageHandler(message)
if originalFile then if originalFile then
local i = 0 local i = 0
for l in originalFile:gmatch("([^\n]*)\n") do for l in (originalFile.."\n"):gmatch("([^\n]*)\n") do
i = i +1 i = i +1
if i == line then if i == line then
local extSource, lineMap = l:match(".*%-%- (.-)%:(%d+)$") local extSource, lineMap = l:match(".*%-%- (.-)%:(%d+)$")

View file

@ -299,7 +299,7 @@ local UNPACK = function(list, i, j) -- ./compiler/lua53.can:182
return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/lua53.can:183 return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/lua53.can:183
end -- ./compiler/lua53.can:183 end -- ./compiler/lua53.can:183
local APPEND = function(t, toAppend) -- ./compiler/lua53.can:185 local APPEND = function(t, toAppend) -- ./compiler/lua53.can:185
return "do" .. indent() .. "local a = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(a, 1, a.n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end" -- ./compiler/lua53.can:186 return "do" .. indent() .. "local " .. var("a") .. " = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(" .. var("a") .. ", 1, " .. var("a") .. ".n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end" -- ./compiler/lua53.can:186
end -- ./compiler/lua53.can:186 end -- ./compiler/lua53.can:186
local CONTINUE_START = function() -- ./compiler/lua53.can:188 local CONTINUE_START = function() -- ./compiler/lua53.can:188
return "do" .. indent() -- ./compiler/lua53.can:189 return "do" .. indent() -- ./compiler/lua53.can:189
@ -1155,7 +1155,7 @@ local UNPACK = function(list, i, j) -- ./compiler/lua53.can:182
return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/lua53.can:183 return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/lua53.can:183
end -- ./compiler/lua53.can:183 end -- ./compiler/lua53.can:183
local APPEND = function(t, toAppend) -- ./compiler/lua53.can:185 local APPEND = function(t, toAppend) -- ./compiler/lua53.can:185
return "do" .. indent() .. "local a = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(a, 1, a.n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end" -- ./compiler/lua53.can:186 return "do" .. indent() .. "local " .. var("a") .. " = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(" .. var("a") .. ", 1, " .. var("a") .. ".n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end" -- ./compiler/lua53.can:186
end -- ./compiler/lua53.can:186 end -- ./compiler/lua53.can:186
local CONTINUE_START = function() -- ./compiler/lua53.can:188 local CONTINUE_START = function() -- ./compiler/lua53.can:188
return "do" .. indent() -- ./compiler/lua53.can:189 return "do" .. indent() -- ./compiler/lua53.can:189
@ -1838,7 +1838,7 @@ UNPACK = function(list, i, j) -- ./compiler/luajit.can:3
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/luajit.can:4 return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/luajit.can:4
end -- ./compiler/luajit.can:4 end -- ./compiler/luajit.can:4
APPEND = function(t, toAppend) -- ./compiler/luajit.can:6 APPEND = function(t, toAppend) -- ./compiler/luajit.can:6
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" -- ./compiler/luajit.can:7 return "do" .. indent() .. "local " .. var("a") .. ", " .. var("p") .. " = { " .. toAppend .. " }, #" .. t .. "+1" .. newline() .. "for i=1, #" .. var("a") .. " do" .. indent() .. t .. "[" .. var("p") .. "] = " .. var("a") .. "[i]" .. newline() .. "" .. var("p") .. " = " .. var("p") .. " + 1" .. unindent() .. "end" .. unindent() .. "end" -- ./compiler/luajit.can:7
end -- ./compiler/luajit.can:7 end -- ./compiler/luajit.can:7
tags["_opid"]["idiv"] = function(left, right) -- ./compiler/luajit.can:10 tags["_opid"]["idiv"] = function(left, right) -- ./compiler/luajit.can:10
return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")" -- ./compiler/luajit.can:11 return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")" -- ./compiler/luajit.can:11
@ -2049,7 +2049,7 @@ local UNPACK = function(list, i, j) -- ./compiler/lua53.can:182
return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/lua53.can:183 return "table.unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/lua53.can:183
end -- ./compiler/lua53.can:183 end -- ./compiler/lua53.can:183
local APPEND = function(t, toAppend) -- ./compiler/lua53.can:185 local APPEND = function(t, toAppend) -- ./compiler/lua53.can:185
return "do" .. indent() .. "local a = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(a, 1, a.n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end" -- ./compiler/lua53.can:186 return "do" .. indent() .. "local " .. var("a") .. " = table.pack(" .. toAppend .. ")" .. newline() .. "table.move(" .. var("a") .. ", 1, " .. var("a") .. ".n, #" .. t .. "+1, " .. t .. ")" .. unindent() .. "end" -- ./compiler/lua53.can:186
end -- ./compiler/lua53.can:186 end -- ./compiler/lua53.can:186
local CONTINUE_START = function() -- ./compiler/lua53.can:188 local CONTINUE_START = function() -- ./compiler/lua53.can:188
return "do" .. indent() -- ./compiler/lua53.can:189 return "do" .. indent() -- ./compiler/lua53.can:189
@ -2732,7 +2732,7 @@ UNPACK = function(list, i, j) -- ./compiler/luajit.can:3
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/luajit.can:4 return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" -- ./compiler/luajit.can:4
end -- ./compiler/luajit.can:4 end -- ./compiler/luajit.can:4
APPEND = function(t, toAppend) -- ./compiler/luajit.can:6 APPEND = function(t, toAppend) -- ./compiler/luajit.can:6
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" -- ./compiler/luajit.can:7 return "do" .. indent() .. "local " .. var("a") .. ", " .. var("p") .. " = { " .. toAppend .. " }, #" .. t .. "+1" .. newline() .. "for i=1, #" .. var("a") .. " do" .. indent() .. t .. "[" .. var("p") .. "] = " .. var("a") .. "[i]" .. newline() .. "" .. var("p") .. " = " .. var("p") .. " + 1" .. unindent() .. "end" .. unindent() .. "end" -- ./compiler/luajit.can:7
end -- ./compiler/luajit.can:7 end -- ./compiler/luajit.can:7
tags["_opid"]["idiv"] = function(left, right) -- ./compiler/luajit.can:10 tags["_opid"]["idiv"] = function(left, right) -- ./compiler/luajit.can:10
return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")" -- ./compiler/luajit.can:11 return "math.floor(" .. lua(left) .. " / " .. lua(right) .. ")" -- ./compiler/luajit.can:11
@ -4485,7 +4485,7 @@ return parser -- ./candran/can-parser/parser.lua:744
end -- ./candran/can-parser/parser.lua:744 end -- ./candran/can-parser/parser.lua:744
local parser = _() or parser -- ./candran/can-parser/parser.lua:748 local parser = _() or parser -- ./candran/can-parser/parser.lua:748
package["loaded"]["candran.can-parser.parser"] = parser or true -- ./candran/can-parser/parser.lua:749 package["loaded"]["candran.can-parser.parser"] = parser or true -- ./candran/can-parser/parser.lua:749
local candran = { ["VERSION"] = "0.11.0" } -- candran.can:14 local candran = { ["VERSION"] = "0.12.0" } -- candran.can:14
candran["default"] = { -- candran.can:18 candran["default"] = { -- candran.can:18
["target"] = "lua53", -- candran.can:19 ["target"] = "lua53", -- candran.can:19
["indentation"] = "", -- candran.can:20 ["indentation"] = "", -- candran.can:20
@ -4503,212 +4503,228 @@ else -- candran.can:31
candran["default"]["target"] = "lua51" -- candran.can:33 candran["default"]["target"] = "lua51" -- candran.can:33
end -- candran.can:33 end -- candran.can:33
end -- candran.can:33 end -- candran.can:33
candran["preprocess"] = function(input, options) -- candran.can:41 candran["preprocess"] = function(input, options) -- candran.can:43
if options == nil then options = {} end -- candran.can:41 if options == nil then options = {} end -- candran.can:43
options = util["merge"](candran["default"], options) -- candran.can:42 options = util["merge"](candran["default"], options) -- candran.can:44
local preprocessor = "" -- candran.can:45 local preprocessor = "" -- candran.can:47
local i = 0 -- candran.can:46 local i = 0 -- candran.can:48
local inLongString = false -- candran.can:47 local inLongString = false -- candran.can:49
local inComment = false -- candran.can:48 local inComment = false -- candran.can:50
for line in (input .. "\ for line in (input .. "\
"):gmatch("(.-\ "):gmatch("(.-\
)") do -- candran.can:49 )") do -- candran.can:51
i = i + (1) -- candran.can:50 i = i + (1) -- candran.can:52
if inComment then -- candran.can:52 if inComment then -- candran.can:54
inComment = not line:match("%]%]") -- candran.can:53 inComment = not line:match("%]%]") -- candran.can:55
elseif inLongString then -- candran.can:54 elseif inLongString then -- candran.can:56
inLongString = not line:match("%]%]") -- candran.can:55 inLongString = not line:match("%]%]") -- candran.can:57
else -- candran.can:55 else -- candran.can:57
if line:match("[^%-]%[%[") then -- candran.can:57 if line:match("[^%-]%[%[") then -- candran.can:59
inLongString = true -- candran.can:58 inLongString = true -- candran.can:60
elseif line:match("%-%-%[%[") then -- candran.can:59 elseif line:match("%-%-%[%[") then -- candran.can:61
inComment = true -- candran.can:60 inComment = true -- candran.can:62
end -- candran.can:60 end -- candran.can:62
end -- candran.can:60 end -- candran.can:62
if not inComment and not inLongString and line:match("^%s*#") and not line:match("^#!") then -- candran.can:63 if not inComment and not inLongString and line:match("^%s*#") and not line:match("^#!") then -- candran.can:65
preprocessor = preprocessor .. (line:gsub("^%s*#", "")) -- candran.can:64 preprocessor = preprocessor .. (line:gsub("^%s*#", "")) -- candran.can:66
else -- candran.can:64 else -- candran.can:66
local l = line:sub(1, - 2) -- candran.can:66 local l = line:sub(1, - 2) -- candran.can:68
if not inLongString and options["mapLines"] and not l:match("%-%- (.-)%:(%d+)$") then -- candran.can:67 if not inLongString and options["mapLines"] and not l:match("%-%- (.-)%:(%d+)$") then -- candran.can:69
preprocessor = preprocessor .. (("write(%q)"):format(l .. " -- " .. options["chunkname"] .. ":" .. i) .. "\ preprocessor = preprocessor .. (("write(%q)"):format(l .. " -- " .. options["chunkname"] .. ":" .. i) .. "\
") -- candran.can:68
else -- candran.can:68
preprocessor = preprocessor .. (("write(%q)"):format(line:sub(1, - 2)) .. "\
") -- candran.can:70 ") -- candran.can:70
end -- candran.can:70 else -- candran.can:70
end -- candran.can:70 preprocessor = preprocessor .. (("write(%q)"):format(line:sub(1, - 2)) .. "\
end -- candran.can:70 ") -- candran.can:72
preprocessor = preprocessor .. ("return output") -- candran.can:74 end -- candran.can:72
local env = util["merge"](_G, options) -- candran.can:77 end -- candran.can:72
env["candran"] = candran -- candran.can:79 end -- candran.can:72
env["output"] = "" -- candran.can:81 preprocessor = preprocessor .. ("return output") -- candran.can:76
env["import"] = function(modpath, margs) -- candran.can:88 local env = util["merge"](_G, options) -- candran.can:79
if margs == nil then margs = {} end -- candran.can:88 env["candran"] = candran -- candran.can:81
local filepath = assert(util["search"](modpath, { -- candran.can:89 env["output"] = "" -- candran.can:83
"can", -- candran.can:89 env["import"] = function(modpath, margs) -- candran.can:90
"lua" -- candran.can:89 if margs == nil then margs = {} end -- candran.can:90
}), "No module named \"" .. modpath .. "\"") -- candran.can:89 local filepath = assert(util["search"](modpath, { -- candran.can:91
local f = io["open"](filepath) -- candran.can:92 "can", -- candran.can:91
if not f then -- candran.can:93 "lua" -- candran.can:91
error("Can't open the module file to import") -- candran.can:93 }), "No module named \"" .. modpath .. "\"") -- candran.can:91
end -- candran.can:93 local f = io["open"](filepath) -- candran.can:94
margs = util["merge"](options, { -- candran.can:95 if not f then -- candran.can:95
["chunkname"] = filepath, -- candran.can:95 error("can't open the module file to import") -- candran.can:95
["loadLocal"] = true, -- candran.can:95 end -- candran.can:95
["loadPackage"] = true -- candran.can:95 margs = util["merge"](options, { -- candran.can:97
}, margs) -- candran.can:95 ["chunkname"] = filepath, -- candran.can:97
local modcontent = candran["preprocess"](f:read("*a"), margs) -- candran.can:96 ["loadLocal"] = true, -- candran.can:97
f:close() -- candran.can:97 ["loadPackage"] = true -- candran.can:97
local modname = modpath:match("[^%.]+$") -- candran.can:100 }, margs) -- candran.can:97
local modcontent = assert(candran["preprocess"](f:read("*a"), margs)) -- candran.can:98
f:close() -- candran.can:99
local modname = modpath:match("[^%.]+$") -- candran.can:102
env["write"]("-- MODULE " .. modpath .. " --\ env["write"]("-- MODULE " .. modpath .. " --\
" .. "local function _()\ " .. "local function _()\
" .. modcontent .. "\ " .. modcontent .. "\
" .. "end\ " .. "end\
" .. (margs["loadLocal"] and ("local %s = _() or %s\ " .. (margs["loadLocal"] and ("local %s = _() or %s\
"):format(modname, modname) or "") .. (margs["loadPackage"] and ("package.loaded[%q] = %s or true\ "):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 .. " --") -- candran.can:109 "):format(modpath, margs["loadLocal"] and modname or "_()") or "") .. "-- END OF MODULE " .. modpath .. " --") -- candran.can:111
end -- candran.can:109 end -- candran.can:111
env["include"] = function(file) -- candran.can:114 env["include"] = function(file) -- candran.can:116
local f = io["open"](file) -- candran.can:115 local f = io["open"](file) -- candran.can:117
if not f then -- candran.can:116 if not f then -- candran.can:118
error("Can't open the file " .. file .. " to include") -- candran.can:116 error("can't open the file " .. file .. " to include") -- candran.can:118
end -- candran.can:116
env["write"](f:read("*a")) -- candran.can:117
f:close() -- candran.can:118
end -- candran.can:118 end -- candran.can:118
env["write"] = function(...) -- candran.can:122 env["write"](f:read("*a")) -- candran.can:119
f:close() -- candran.can:120
end -- candran.can:120
env["write"] = function(...) -- candran.can:124
env["output"] = env["output"] .. (table["concat"]({ ... }, "\9") .. "\ env["output"] = env["output"] .. (table["concat"]({ ... }, "\9") .. "\
") -- candran.can:123 ") -- candran.can:125
end -- candran.can:123 end -- candran.can:125
env["placeholder"] = function(name) -- candran.can:127 env["placeholder"] = function(name) -- candran.can:129
if env[name] then -- candran.can:128 if env[name] then -- candran.can:130
env["write"](env[name]) -- candran.can:129 env["write"](env[name]) -- candran.can:131
end -- candran.can:129 end -- candran.can:131
end -- candran.can:129 end -- candran.can:131
local preprocess, err = util["load"](candran["compile"](preprocessor, args), "candran preprocessor", env) -- candran.can:134 local preprocess, err = candran["compile"](preprocessor, options) -- candran.can:136
if not preprocess then -- candran.can:135 if not preprocess then -- candran.can:137
error("Error while creating Candran preprocessor: " .. err) -- candran.can:135 return nil, "in preprocessor: " .. err -- candran.can:138
end -- candran.can:135 end -- candran.can:138
local success, output = pcall(preprocess) -- candran.can:138 preprocess, err = util["load"](preprocessor, "candran preprocessor", env) -- candran.can:141
if not success then -- candran.can:139 if not preprocess then -- candran.can:142
error("Error while preprocessing file: " .. output) -- candran.can:139 return nil, "in preprocessor: " .. err -- candran.can:143
end -- candran.can:139 end -- candran.can:143
return output -- candran.can:141 local success, output = pcall(preprocess) -- candran.can:147
end -- candran.can:141 if not success then -- candran.can:148
candran["compile"] = function(input, options) -- candran.can:148 return nil, "in preprocessor: " .. output -- candran.can:149
if options == nil then options = {} end -- candran.can:148 end -- candran.can:149
options = util["merge"](candran["default"], options) -- candran.can:149 return output -- candran.can:152
local ast, errmsg = parser["parse"](input, options["chunkname"]) -- candran.can:151 end -- candran.can:152
if not ast then -- candran.can:153 candran["compile"] = function(input, options) -- candran.can:161
error("Compiler: error while parsing file: " .. errmsg) -- candran.can:154 if options == nil then options = {} end -- candran.can:161
end -- candran.can:154 options = util["merge"](candran["default"], options) -- candran.can:162
return require("compiler." .. options["target"])(input, ast, options) -- candran.can:157 local ast, errmsg = parser["parse"](input, options["chunkname"]) -- candran.can:164
end -- candran.can:157 if not ast then -- candran.can:166
candran["make"] = function(code, options) -- candran.can:164 return nil, errmsg -- candran.can:167
return candran["compile"](candran["preprocess"](code, options), options) -- candran.can:165 end -- candran.can:167
end -- candran.can:165 return require("compiler." .. options["target"])(input, ast, options) -- candran.can:170
local errorRewritingActive = false -- candran.can:168 end -- candran.can:170
local codeCache = {} -- candran.can:169 candran["make"] = function(code, options) -- candran.can:179
candran["loadfile"] = function(filepath, env, options) -- candran.can:172 local r, err = candran["preprocess"](code, options) -- candran.can:180
local f, err = io["open"](filepath) -- candran.can:173 if r then -- candran.can:181
if not f then -- candran.can:174 r, err = candran["compile"](r, options) -- candran.can:182
error("can't open the file: " .. err) -- candran.can:174 if r then -- candran.can:183
end -- candran.can:174 return r -- candran.can:184
local content = f:read("*a") -- candran.can:175 end -- candran.can:184
f:close() -- candran.can:176 end -- candran.can:184
return candran["load"](content, filepath, env, options) -- candran.can:178 return r, err -- candran.can:187
end -- candran.can:178 end -- candran.can:187
candran["load"] = function(chunk, chunkname, env, options) -- candran.can:183 local errorRewritingActive = false -- candran.can:190
if options == nil then options = {} end -- candran.can:183 local codeCache = {} -- candran.can:191
options = util["merge"]({ ["chunkname"] = tostring(chunkname or chunk) }, options) -- candran.can:184 candran["loadfile"] = function(filepath, env, options) -- candran.can:194
codeCache[options["chunkname"]] = candran["make"](chunk, options) -- candran.can:186 local f, err = io["open"](filepath) -- candran.can:195
local f, err = util["load"](codeCache[options["chunkname"]], options["chunkname"], env) -- candran.can:187 if not f then -- candran.can:196
if f == nil then -- candran.can:192 return nil, ("cannot open %s"):format(err) -- candran.can:197
return f, "Candran unexpectedly generated invalid code: " .. err -- candran.can:193 end -- candran.can:197
end -- candran.can:193 local content = f:read("*a") -- candran.can:199
if options["rewriteErrors"] == false then -- candran.can:196 f:close() -- candran.can:200
return f -- candran.can:197 return candran["load"](content, filepath, env, options) -- candran.can:202
else -- candran.can:197 end -- candran.can:202
return function(...) -- candran.can:199 candran["load"] = function(chunk, chunkname, env, options) -- candran.can:207
local params = { ... } -- candran.can:200 if options == nil then options = {} end -- candran.can:207
if not errorRewritingActive then -- candran.can:201 options = util["merge"]({ ["chunkname"] = tostring(chunkname or chunk) }, options) -- candran.can:208
errorRewritingActive = true -- candran.can:202 local code, err = candran["make"](chunk, options) -- candran.can:210
local t = { xpcall(function() -- candran.can:203 if not code then -- candran.can:211
return f(unpack(params)) -- candran.can:203 return code, err -- candran.can:212
end, candran["messageHandler"]) } -- candran.can:203 end -- candran.can:212
errorRewritingActive = false -- candran.can:204 codeCache[options["chunkname"]] = code -- candran.can:215
if t[1] == false then -- candran.can:205 local f, err = util["load"](code, options["chunkname"], env) -- candran.can:216
error(t[2], 0) -- candran.can:206
end -- candran.can:206
return unpack(t, 2) -- candran.can:208
else -- candran.can:208
return f(...) -- candran.can:210
end -- candran.can:210
end -- candran.can:210
end -- candran.can:210
end -- candran.can:210
candran["dofile"] = function(filename, options) -- candran.can:218
local f, err = candran["loadfile"](filename, nil, options) -- candran.can:219
if f == nil then -- candran.can:221 if f == nil then -- candran.can:221
error(err) -- candran.can:222 return f, "candran unexpectedly generated invalid code: " .. err -- candran.can:222
else -- candran.can:222 end -- candran.can:222
return f() -- candran.can:224 if options["rewriteErrors"] == false then -- candran.can:225
end -- candran.can:224 return f -- candran.can:226
end -- candran.can:224 else -- candran.can:226
candran["messageHandler"] = function(message) -- candran.can:230 return function(...) -- candran.can:228
local params = { ... } -- candran.can:229
if not errorRewritingActive then -- candran.can:230
errorRewritingActive = true -- candran.can:231
local t = { xpcall(function() -- candran.can:232
return f(unpack(params)) -- candran.can:232
end, candran["messageHandler"]) } -- candran.can:232
errorRewritingActive = false -- candran.can:233
if t[1] == false then -- candran.can:234
error(t[2], 0) -- candran.can:235
end -- candran.can:235
return unpack(t, 2) -- candran.can:237
else -- candran.can:237
return f(...) -- candran.can:239
end -- candran.can:239
end -- candran.can:239
end -- candran.can:239
end -- candran.can:239
candran["dofile"] = function(filename, options) -- candran.can:247
local f, err = candran["loadfile"](filename, nil, options) -- candran.can:248
if f == nil then -- candran.can:250
error(err) -- candran.can:251
else -- candran.can:251
return f() -- candran.can:253
end -- candran.can:253
end -- candran.can:253
candran["messageHandler"] = function(message) -- candran.can:259
return debug["traceback"](message, 2):gsub("(\ return debug["traceback"](message, 2):gsub("(\
?%s*)([^\ ?%s*)([^\
]-)%:(%d+)%:", function(indentation, source, line) -- candran.can:231 ]-)%:(%d+)%:", function(indentation, source, line) -- candran.can:260
line = tonumber(line) -- candran.can:232 line = tonumber(line) -- candran.can:261
local originalFile -- candran.can:234 local originalFile -- candran.can:263
local strName = source:match("%[string \"(.-)\"%]") -- candran.can:235 local strName = source:match("%[string \"(.-)\"%]") -- candran.can:264
if strName then -- candran.can:236 if strName then -- candran.can:265
if codeCache[strName] then -- candran.can:237 if codeCache[strName] then -- candran.can:266
originalFile = codeCache[strName] -- candran.can:238 originalFile = codeCache[strName] -- candran.can:267
source = strName -- candran.can:239 source = strName -- candran.can:268
end -- candran.can:239 end -- candran.can:268
else -- candran.can:239 else -- candran.can:268
local fi = io["open"](source, "r") -- candran.can:242 local fi = io["open"](source, "r") -- candran.can:271
if fi then -- candran.can:243 if fi then -- candran.can:272
originalFile = fi:read("*a") -- candran.can:244 originalFile = fi:read("*a") -- candran.can:273
fi:close() -- candran.can:245 fi:close() -- candran.can:274
end -- candran.can:245 end -- candran.can:274
end -- candran.can:245 end -- candran.can:274
if originalFile then -- candran.can:249 if originalFile then -- candran.can:278
local i = 0 -- candran.can:250 local i = 0 -- candran.can:279
for l in originalFile:gmatch("([^\ for l in (originalFile .. "\
"):gmatch("([^\
]*)\ ]*)\
") do -- candran.can:251 ") do -- candran.can:280
i = i + 1 -- candran.can:252 i = i + 1 -- candran.can:281
if i == line then -- candran.can:253 if i == line then -- candran.can:282
local extSource, lineMap = l:match(".*%-%- (.-)%:(%d+)$") -- candran.can:254 local extSource, lineMap = l:match(".*%-%- (.-)%:(%d+)$") -- candran.can:283
if lineMap then -- candran.can:255 if lineMap then -- candran.can:284
if extSource ~= source then -- candran.can:256 if extSource ~= source then -- candran.can:285
return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):" -- candran.can:257 return indentation .. extSource .. ":" .. lineMap .. "(" .. extSource .. ":" .. line .. "):" -- candran.can:286
else -- candran.can:257 else -- candran.can:286
return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):" -- candran.can:259 return indentation .. extSource .. ":" .. lineMap .. "(" .. line .. "):" -- candran.can:288
end -- candran.can:259 end -- candran.can:288
end -- candran.can:259 end -- candran.can:288
break -- candran.can:262 break -- candran.can:291
end -- candran.can:262 end -- candran.can:291
end -- candran.can:262 end -- candran.can:291
end -- candran.can:262 end -- candran.can:291
end) -- candran.can:262 end) -- candran.can:291
end -- candran.can:262 end -- candran.can:291
candran["searcher"] = function(modpath) -- candran.can:270 candran["searcher"] = function(modpath) -- candran.can:299
local filepath = util["search"](modpath, { "can" }) -- candran.can:271 local filepath = util["search"](modpath, { "can" }) -- candran.can:300
if not filepath then -- candran.can:272 if not filepath then -- candran.can:301
return "\ return "\
\9no candran file in package.path" -- candran.can:273 \9no candran file in package.path" -- candran.can:302
end -- candran.can:273 end -- candran.can:302
return candran["loadfile"](filepath) -- candran.can:275 return candran["loadfile"](filepath) -- candran.can:304
end -- candran.can:275 end -- candran.can:304
candran["setup"] = function() -- candran.can:279 candran["setup"] = function() -- candran.can:308
if _VERSION == "Lua 5.1" then -- candran.can:280 if _VERSION == "Lua 5.1" then -- candran.can:309
table["insert"](package["loaders"], 2, candran["searcher"]) -- candran.can:281 table["insert"](package["loaders"], 2, candran["searcher"]) -- candran.can:310
else -- candran.can:281 else -- candran.can:310
table["insert"](package["searchers"], 2, candran["searcher"]) -- candran.can:283 table["insert"](package["searchers"], 2, candran["searcher"]) -- candran.can:312
end -- candran.can:283 end -- candran.can:312
return candran -- candran.can:285 return candran -- candran.can:314
end -- candran.can:285 end -- candran.can:314
return candran -- candran.can:288 return candran -- candran.can:317

View file

@ -183,7 +183,7 @@ return function(code, ast, options)
return "table.unpack("..list..(i and (", "..i..(j and (", "..j) or "")) or "")..")" return "table.unpack("..list..(i and (", "..i..(j and (", "..j) or "")) or "")..")"
end end
local APPEND = (t, toAppend) -- append values "toAppend" (multiple values possible) to t 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" return "do"..indent().."local "..var("a").." = table.pack("..toAppend..")"..newline().."table.move("..var("a")..", 1, "..var("a")..".n, #"..t.."+1, "..t..")"..unindent().."end"
end end
local CONTINUE_START = () -- at the start of loops using continue local CONTINUE_START = () -- at the start of loops using continue
return "do"..indent() return "do"..indent()

View file

@ -4,7 +4,7 @@ UNPACK = (list, i, j)
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
end end
APPEND = (t, toAppend) 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" return "do" .. indent() .. "local "..var("a")..", "..var("p").." = { " .. toAppend .. " }, #" .. t .. "+1" .. newline() .. "for i=1, #"..var("a").." do" .. indent() .. t .. "["..var("p").."] = "..var("a").."[i]" .. newline() .. ""..var("p").." = "..var("p").." + 1" .. unindent() .. "end" .. unindent() .. "end"
end end
tags._opid.idiv = (left, right) tags._opid.idiv = (left, right)

View file

@ -23,7 +23,9 @@ source = {
dependencies = { dependencies = {
"lua >= 5.1", "lua >= 5.1",
"lpeglabel >= 1.5.0" "lpeglabel >= 1.5.0",
"linenoise >= 0.9",
"luacheck >= 0.23.0"
} }
build = { build = {
@ -32,6 +34,6 @@ build = {
candran = "candran.lua" candran = "candran.lua"
}, },
install = { install = {
bin = { "bin/can", "bin/canc" } bin = { "bin/can", "bin/canc", "bin/cancheck" }
} }
} }

View file

@ -15,7 +15,7 @@ local function test(name, candranCode, expectedResult, options)
options.chunkname = name options.chunkname = name
-- make code -- make code
local success, code = pcall(candran.make, candranCode, options) local success, code = pcall(function() return assert(candran.make(candranCode, options)) end)
if not success then if not success then
self.result = "error" self.result = "error"
self.message = "/!\\ error while making code:\n"..code self.message = "/!\\ error while making code:\n"..code