1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2025-10-27 17:59:30 +00:00
candran/bin/can

190 lines
4 KiB
Lua

#!/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