#!/usr/bin/env lua
--Set up paths so we can access Brat runtime files
program_path = debug.getinfo(1, "S").source
program_path = program_path:match("([^@].+/)bin/brat$")
if program_path == nil then
program_path = "../"
end
package.cpath = package.cpath .. ";" .. program_path .. "lib/?.so"
package.path = package.path ..
";" .. program_path .. "core/?.lua;" .. program_path .. "stdlib/?.lua;" .. program_path .. "lib/?.lua"
require "lfs"
io.output(io.stdout)
local abort = function(message)
print(message)
os.exit(-1)
end
require 'core'
require "parser/brat2lua"
local brat2lua = _exports['brat2lua']
local compile_file = function(file_name, force)
local brat_file_name = file_name .. ".brat"
local lua_file_name = file_name .. ".lua"
local brat_modified = lfs.attributes(brat_file_name, "modification")
--.brat file does not exist
if brat_modified == nil then
abort("Could not open " .. brat_file_name)
end
local lua_modified = lfs.attributes(lua_file_name, "modification")
if force or lua_modified == nil or brat_modified > lua_modified then
brat2lua:compile_underfile(base_string:new(file_name))
end
end
local interactive = function()
local ffi = require "ffi"
ffi.cdef [[
int linenoiseHistoryAdd(const char *line);
char *linenoise(const char *prompt);
]]
local ln = ffi.load("linenoise")
local get_line = ln.linenoise;
local add_line = ln.linenoiseHistoryAdd;
local input
local line = 1
local parsed_function
local lua_code
_exports = {}
local interactive_env = {
require = require,
package = package,
print = print,
object = object,
array = array,
number = number,
string = base_string,
exception = exception,
hash = hash,
regex = regex,
symbol = symbol,
_self = object,
_type = type,
_error = error,
_tostring = tostring,
_exports = _exports,
_rawget = rawget,
_table = table,
}
local rescue = function(obj, err)
return err
end
local protect_options = hash:new({ [base_string:new("rescue")] = rescue })
local compiler = brat2lua:start_underinteractive()
print("# Interactive Brat")
while true do
input = get_line("brat:" .. line .. "> ")
if input ~= nil then
input = ffi.string(input)
end
if input == nil or input == "quit" then
return
elseif input ~= "" and not input:match("^%s+$") then
line = line + 1
add_line(input)
lua_code = compiler:run(base_string:new(input))._lua_string
parsed_function = assert(loadstring(lua_code, "interactive"))
setfenv(parsed_function, interactive_env)
print("#=> " .. tostring(object:protect(parsed_function, protect_options)))
for k, v in pairs(_exports) do
interactive_env[k] = v
end
_exports = {}
end
end
end
local run_string = function(string)
local parsed = brat2lua:compile_understring(base_string:new(string))._lua_string
parsed_function = assert(load(parsed, "string"))
local rescue = function(obj, err)
return err
end
local protect_options = hash:new({ [base_string:new("rescue")] = rescue })
local result = tostring(object:protect(parsed_function, protect_options))
return result
end
local get_filename = function(arg)
local file_name = arg:match("(.+).brat")
if file_name == nil then
abort("File to execute should end in .brat: " .. tostring(arg))
end
return file_name
end
if #arg == 0 then
interactive()
abort("Exiting")
end
local first_arg = arg[1]
if first_arg == "-!" then
load(io.read("*a"))()
elseif first_arg == "-" then
run_string(io.read("*a"))
else
local file_name
if first_arg == "-x" or first_arg == "-f" then
file_name = get_filename(arg[2])
else
file_name = get_filename(first_arg)
end
if first_arg == '-f' then
compile_file(file_name, true)
else
compile_file(file_name)
end
if first_arg == '-x' then
dofile(file_name .. ".lua")
os.remove(file_name .. ".lua")
end
end