#!/usr/bin/env lua local candran = require("candran").setup() local util = require("candran.util") local argparse = require("argparse") local unpack = unpack or table.unpack -- Parse args -- local parser = argparse() :name "can" :description("Candran "..candran.VERSION.." interpreter by Reuh.") :epilog "For more info, see https://github.com/Reuh/candran" parser:argument("filename", "Candran file to run. Use - to read from standard input. Start the REPL if no filename given.") :args "?" util.cli.addCandranOptions(parser) local args = parser:parse() local options = util.cli.makeCandranOptions(args) -- Run -- -- stdin if args.filename == "-" then local f, err = candran.load(io.read("*a"), "stdin", nil, options) if not f then io.stderr:write("can: "..err.."\n") os.exit(1) end local r, e = xpcall(f, candran.messageHandler) if not r then io.stderr:write(e.."\n") os.exit(1) end -- file elseif args.filename then local f, err = candran.loadfile(args.filename, nil, options) if not f then io.stderr:write("can: "..err.."\n") os.exit(1) else local r, e = xpcall(f, candran.messageHandler) if not r then io.stderr:write(e.."\n") os.exit(1) end end -- REPL else candran.default = util.merge(candran.default, options) -- Setup linenoise local s, l = pcall(require, "linenoise") if not s then -- pure Lua compatibility thingy l = { linenoise = function(prompt) io.write(prompt) local s, line = pcall(io.read) if not s then if line == "interrupted!" then return nil else return nil, err end end return line end, historyadd = function() end, setcompletion = function() end, sethints = function() end, enableutf8 = function() end } end local keywords = { -- Lua "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", -- Candran "continue", "let", "push" } l.enableutf8() l.setcompletion(function(comp, line) local var = line:match("[a-zA-Z_][a-zA-Z_0-9]*$") if var then for _, k in ipairs(keywords) do if k:match("^"..var) then comp:add(line .. k:sub(#var+1)) end end for k in pairs(_ENV) do if k:match("^"..var) then comp:add(line .. k:sub(#var+1)) end end end end) l.sethints(function(line) local var = line:match("[a-zA-Z_][a-zA-Z_0-9]*$") if var then for _, k in ipairs(keywords) do if k:match("^"..var) then return k:sub(#var+1), { color = 2, bold = true } end end for k in pairs(_ENV) do if k:match("^"..var) then return k:sub(#var+1), { color = 2 } end end end end) -- Introduction print("Candran " .. candran.VERSION .. ", targeting " .. candran.default.target) candran.setup() -- only perform static imports once, on startup do local r, e = candran.load("local _", "stdin") if not r then print("In static import: "..e) else r, e = pcall(r) if not r then print("In static import: "..e) end end candran.default.import = {} end -- REPL loop local multiline = false -- true if wait for another line local buffer while true do local line, err = l.linenoise(multiline and ">> " or "> ") -- exit if not line then if not err then if multiline then multiline = false line = "" else return end else error(err) end end -- history if line:match("[^%s]") then l.historyadd(line) end -- multiline if multiline then buffer = buffer .. "\n" .. line multiline = false else buffer = line end -- print shortcut if buffer:match("^=") then buffer = buffer:gsub("^=", "return tostring(") .. ")" end -- exec local r, e = candran.load(buffer, "stdin") if not r then if e:match("expected '[end})]+' to close") then multiline = true else print(e) end else local t = { pcall(r) } if t[1] == false then print(t[2]) elseif #t > 1 then print(unpack(t, 2)) end end end end