mirror of
https://github.com/Reuh/candran.git
synced 2025-10-27 09:59:29 +00:00
192 lines
4.1 KiB
Lua
192 lines
4.1 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()
|
|
|
|
-- check errors in static import
|
|
-- note: static imports will be run every line, as preprocessors macros and constants aren't kept between compilations...
|
|
do
|
|
local r, e = candran.load("local _", "stdin")
|
|
if not r then
|
|
print("In static import: "..e)
|
|
candran.default.import = {}
|
|
else
|
|
r, e = pcall(r)
|
|
if not r then
|
|
print("In static import: "..e)
|
|
candran.default.import = {}
|
|
end
|
|
end
|
|
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
|