2011-08-13 04:07:44 +00:00
|
|
|
#!/usr/bin/env lua
|
2011-05-29 16:33:25 +00:00
|
|
|
|
2013-02-28 03:17:16 +00:00
|
|
|
local alt_getopt = require "alt_getopt"
|
|
|
|
local lfs = require "lfs"
|
2011-05-29 16:33:25 +00:00
|
|
|
|
2014-05-28 05:33:24 +00:00
|
|
|
local opts, ind = alt_getopt.get_opts(arg, "lvhwt:o:pTXb", {
|
2013-12-26 09:29:00 +00:00
|
|
|
print = "p", tree = "T", version = "v", help = "h", lint = "l"
|
2011-08-13 04:07:44 +00:00
|
|
|
})
|
2011-05-29 16:33:25 +00:00
|
|
|
|
2012-01-22 19:53:13 +00:00
|
|
|
local read_stdin = arg[1] == "--"
|
|
|
|
|
2011-10-09 08:59:50 +00:00
|
|
|
local polling_rate = 1.0
|
|
|
|
|
2011-08-13 04:07:44 +00:00
|
|
|
local help = [[Usage: %s [options] files...
|
2011-05-29 16:33:25 +00:00
|
|
|
|
|
|
|
-h Print this message
|
|
|
|
-w Watch file/directory
|
|
|
|
-t path Specify where to place compiled files
|
2014-05-28 05:33:24 +00:00
|
|
|
-o file Write output to file
|
2011-08-13 04:07:44 +00:00
|
|
|
-p Write output to standard out
|
|
|
|
-T Write parse tree instead of code (to stdout)
|
2012-10-30 07:31:56 +00:00
|
|
|
-X Write line rewrite map instead of code (to stdout)
|
2013-12-26 09:29:00 +00:00
|
|
|
-l Perform lint on the file instead of compiling
|
2011-08-13 04:07:44 +00:00
|
|
|
-b Dump parse and compile time (doesn't write output)
|
2011-07-19 06:53:43 +00:00
|
|
|
-v Print version
|
2012-01-22 19:53:13 +00:00
|
|
|
|
|
|
|
-- Read from standard in, print to standard out
|
|
|
|
(Must be first and only argument)
|
2011-05-29 16:33:25 +00:00
|
|
|
]]
|
|
|
|
|
2011-07-19 06:53:43 +00:00
|
|
|
if opts.v then
|
|
|
|
local v = require "moonscript.version"
|
|
|
|
v.print_version()
|
|
|
|
os.exit()
|
|
|
|
end
|
|
|
|
|
2011-05-29 16:33:25 +00:00
|
|
|
function print_help(err)
|
2013-12-27 06:47:11 +00:00
|
|
|
local help_msg = help:format(arg[0])
|
|
|
|
|
2013-12-27 06:44:54 +00:00
|
|
|
if err then
|
|
|
|
io.stderr:write("Error: ".. err .. "\n")
|
2013-12-27 06:47:11 +00:00
|
|
|
io.stderr:write(help_msg .. "\n")
|
|
|
|
os.exit(1)
|
|
|
|
else
|
|
|
|
print(help_msg)
|
|
|
|
os.exit(0)
|
2013-12-27 06:44:54 +00:00
|
|
|
end
|
2011-05-29 16:33:25 +00:00
|
|
|
end
|
|
|
|
|
2013-12-27 06:44:54 +00:00
|
|
|
function log_msg(...)
|
2011-08-13 04:07:44 +00:00
|
|
|
if not opts.p then
|
2013-12-27 06:44:54 +00:00
|
|
|
io.stderr:write(table.concat({...}, " ") .. "\n")
|
2011-05-29 16:33:25 +00:00
|
|
|
end
|
2011-08-13 04:07:44 +00:00
|
|
|
end
|
|
|
|
|
2014-06-18 16:23:41 +00:00
|
|
|
local moonc = require("moonscript.cmd.moonc")
|
2015-03-12 08:09:17 +00:00
|
|
|
local util = require "moonscript.util"
|
2014-06-18 16:23:41 +00:00
|
|
|
local mkdir = moonc.mkdir
|
|
|
|
local normalize_dir = moonc.normalize_dir
|
|
|
|
local parse_dir = moonc.parse_dir
|
|
|
|
local parse_file = moonc.parse_file
|
2014-06-18 16:48:25 +00:00
|
|
|
local compile_and_write = moonc.compile_and_write
|
2014-06-18 17:14:26 +00:00
|
|
|
local path_to_target = moonc.path_to_target
|
2011-08-13 04:07:44 +00:00
|
|
|
|
2014-06-18 16:48:25 +00:00
|
|
|
local function scan_directory(root, collected)
|
2014-06-18 16:23:41 +00:00
|
|
|
root = normalize_dir(root)
|
2011-05-29 16:33:25 +00:00
|
|
|
collected = collected or {}
|
|
|
|
|
|
|
|
for fname in lfs.dir(root) do
|
2014-06-17 07:03:13 +00:00
|
|
|
if not fname:match("^%.") then
|
2011-05-29 16:33:25 +00:00
|
|
|
local full_path = root..fname
|
|
|
|
|
|
|
|
if lfs.attributes(full_path, "mode") == "directory" then
|
|
|
|
scan_directory(full_path, collected)
|
|
|
|
end
|
|
|
|
|
2013-01-14 07:38:38 +00:00
|
|
|
if fname:match("%.moon$") then
|
2011-05-29 16:33:25 +00:00
|
|
|
table.insert(collected, full_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return collected
|
|
|
|
end
|
|
|
|
|
2014-06-18 16:48:25 +00:00
|
|
|
local function remove_dups(tbl, key_fn)
|
2011-05-29 16:33:25 +00:00
|
|
|
local hash = {}
|
|
|
|
local final = {}
|
|
|
|
|
|
|
|
for _, v in ipairs(tbl) do
|
2014-06-17 17:45:42 +00:00
|
|
|
local dup_key = key_fn and key_fn(v) or v
|
|
|
|
if not hash[dup_key] then
|
2011-05-29 16:33:25 +00:00
|
|
|
table.insert(final, v)
|
2014-06-17 17:45:42 +00:00
|
|
|
hash[dup_key] = true
|
2011-05-29 16:33:25 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return final
|
|
|
|
end
|
|
|
|
|
2014-06-17 17:45:42 +00:00
|
|
|
-- creates tuples of input and target
|
2014-06-18 16:48:25 +00:00
|
|
|
local function get_files(fname, files)
|
2011-05-29 16:33:25 +00:00
|
|
|
files = files or {}
|
|
|
|
|
|
|
|
if lfs.attributes(fname, "mode") == "directory" then
|
2014-06-17 17:45:42 +00:00
|
|
|
for _, sub_fname in ipairs(scan_directory(fname)) do
|
2014-06-18 17:14:26 +00:00
|
|
|
table.insert(files, {
|
2014-06-18 17:26:20 +00:00
|
|
|
sub_fname,
|
|
|
|
path_to_target(sub_fname, opts.t, fname)
|
|
|
|
})
|
2014-06-17 17:45:42 +00:00
|
|
|
end
|
2011-05-29 16:33:25 +00:00
|
|
|
else
|
2014-06-18 17:14:26 +00:00
|
|
|
table.insert(files, {
|
2014-06-18 17:26:20 +00:00
|
|
|
fname,
|
|
|
|
path_to_target(fname, opts.t)
|
|
|
|
})
|
2011-05-29 16:33:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return files
|
|
|
|
end
|
|
|
|
|
2014-06-19 07:04:10 +00:00
|
|
|
if opts.h then
|
|
|
|
print_help()
|
|
|
|
end
|
2011-05-29 16:33:25 +00:00
|
|
|
|
2012-01-22 19:53:13 +00:00
|
|
|
if read_stdin then
|
2014-06-18 17:26:20 +00:00
|
|
|
local parse = require "moonscript.parse"
|
|
|
|
local compile = require "moonscript.compile"
|
|
|
|
|
2012-01-22 19:53:13 +00:00
|
|
|
local text = io.stdin:read("*a")
|
|
|
|
local tree, err = parse.string(text)
|
2014-06-18 17:26:20 +00:00
|
|
|
|
2012-01-22 19:53:13 +00:00
|
|
|
if not tree then error(err) end
|
|
|
|
local code, err, pos = compile.tree(tree)
|
|
|
|
|
|
|
|
if not code then
|
|
|
|
error(compile.format_error(err, pos, text))
|
|
|
|
end
|
|
|
|
|
|
|
|
print(code)
|
|
|
|
os.exit()
|
|
|
|
end
|
|
|
|
|
2011-05-29 16:33:25 +00:00
|
|
|
local inputs = {}
|
|
|
|
for i = ind, #arg do
|
|
|
|
table.insert(inputs, arg[i])
|
|
|
|
end
|
|
|
|
|
|
|
|
if #inputs == 0 then
|
|
|
|
print_help("No files specified")
|
|
|
|
end
|
|
|
|
|
|
|
|
local files = {}
|
|
|
|
for _, input in ipairs(inputs) do
|
|
|
|
get_files(input, files)
|
|
|
|
end
|
|
|
|
|
2014-06-18 03:27:10 +00:00
|
|
|
files = remove_dups(files, function(f)
|
2014-06-18 17:26:20 +00:00
|
|
|
return f[2]
|
2014-06-18 03:27:10 +00:00
|
|
|
end)
|
2014-06-17 17:45:42 +00:00
|
|
|
|
2014-06-19 07:04:10 +00:00
|
|
|
if opts.o and #files > 1 then
|
|
|
|
print_help("-o can not be used with multiple input files")
|
|
|
|
end
|
|
|
|
|
2014-06-18 16:48:25 +00:00
|
|
|
local function get_sleep_func()
|
2011-10-09 08:59:50 +00:00
|
|
|
local sleep
|
|
|
|
if not pcall(function()
|
2014-08-18 02:29:30 +00:00
|
|
|
local socket = require "socket"
|
2011-10-09 08:59:50 +00:00
|
|
|
sleep = socket.sleep
|
|
|
|
end) then
|
2013-01-03 01:32:00 +00:00
|
|
|
-- This is set by moonc.c in windows binaries
|
|
|
|
sleep = require("moonscript")._sleep
|
2011-10-09 08:59:50 +00:00
|
|
|
end
|
2011-12-01 03:25:41 +00:00
|
|
|
if not sleep then
|
|
|
|
error("Missing sleep function; install LuaSocket")
|
|
|
|
end
|
2011-10-09 08:59:50 +00:00
|
|
|
return sleep
|
|
|
|
end
|
|
|
|
|
2014-06-18 16:48:25 +00:00
|
|
|
local function plural(count, word)
|
2011-10-09 21:52:50 +00:00
|
|
|
if count ~= 1 then
|
|
|
|
word = word .. "s"
|
|
|
|
end
|
|
|
|
return table.concat({count, word}, " ")
|
|
|
|
end
|
|
|
|
|
2011-10-09 08:59:50 +00:00
|
|
|
-- returns an iterator that returns files that have been updated
|
2014-06-18 16:48:25 +00:00
|
|
|
local function create_watcher(files)
|
2011-12-01 03:25:41 +00:00
|
|
|
local msg = "Starting watch loop (Ctrl-C to exit)"
|
2011-10-09 08:59:50 +00:00
|
|
|
|
2011-05-29 16:33:25 +00:00
|
|
|
local inotify
|
2011-10-09 09:03:09 +00:00
|
|
|
pcall(function()
|
2011-05-29 16:33:25 +00:00
|
|
|
inotify = require "inotify"
|
2011-10-09 09:03:09 +00:00
|
|
|
end)
|
2011-05-29 16:33:25 +00:00
|
|
|
|
2016-04-14 02:47:27 +00:00
|
|
|
local watchers = require("moonscript.cmd.watchers")
|
2014-06-18 03:27:10 +00:00
|
|
|
|
2016-04-14 02:47:27 +00:00
|
|
|
if watchers.InotifyWacher:available() then
|
|
|
|
return watchers.InotifyWacher(files):each_update()
|
|
|
|
end
|
2014-06-18 03:27:10 +00:00
|
|
|
|
2016-04-14 02:47:27 +00:00
|
|
|
-- poll the filesystem instead
|
|
|
|
local sleep = get_sleep_func()
|
|
|
|
return coroutine.wrap(function()
|
|
|
|
io.stderr:write(("%s with polling [%s]"):format(msg, plural(#files, "file")) .. "\n")
|
|
|
|
|
|
|
|
local mod_time = {}
|
|
|
|
while true do
|
|
|
|
for _, tuple in ipairs(files) do
|
|
|
|
local file = tuple[1]
|
|
|
|
local time = lfs.attributes(file, "modification")
|
|
|
|
if not time then
|
|
|
|
mod_time[file] = nil -- file doesn't exist
|
|
|
|
elseif not mod_time[file] then
|
|
|
|
mod_time[file] = time -- new file created
|
|
|
|
else
|
|
|
|
if time ~= mod_time[file] then
|
|
|
|
if time > mod_time[file] then
|
|
|
|
coroutine.yield(file)
|
|
|
|
mod_time[file] = time
|
2011-10-09 08:59:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-04-14 02:47:27 +00:00
|
|
|
sleep(polling_rate)
|
|
|
|
end
|
|
|
|
end)
|
2011-10-09 08:59:50 +00:00
|
|
|
end
|
|
|
|
|
2015-12-28 04:36:54 +00:00
|
|
|
|
2011-10-09 08:59:50 +00:00
|
|
|
if opts.w then
|
2015-12-28 06:14:55 +00:00
|
|
|
-- build function to check for lint or compile in watch
|
|
|
|
local handle_file
|
|
|
|
if opts.l then
|
|
|
|
local lint = require "moonscript.cmd.lint"
|
|
|
|
handle_file = lint.lint_file
|
|
|
|
else
|
|
|
|
handle_file = compile_and_write
|
|
|
|
end
|
|
|
|
|
2011-10-09 08:59:50 +00:00
|
|
|
local watcher = create_watcher(files)
|
|
|
|
-- catches interrupt error for ctl-c
|
|
|
|
local protected = function()
|
2014-06-18 03:27:10 +00:00
|
|
|
local status, file = true, watcher()
|
2011-12-01 03:25:41 +00:00
|
|
|
if status then
|
|
|
|
return file
|
|
|
|
elseif file ~= "interrupted!" then
|
|
|
|
error(file)
|
|
|
|
end
|
2011-10-09 08:59:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
for fname in protected do
|
2014-06-19 07:00:10 +00:00
|
|
|
local target = path_to_target(fname, opts.t)
|
2014-06-19 07:04:10 +00:00
|
|
|
|
|
|
|
if opts.o then
|
|
|
|
target = opts.o
|
|
|
|
end
|
|
|
|
|
2015-12-28 06:14:55 +00:00
|
|
|
local success, err = handle_file(fname, target)
|
2015-12-28 04:36:54 +00:00
|
|
|
if opts.l then
|
|
|
|
if success then
|
|
|
|
io.stderr:write(success .. "\n\n")
|
|
|
|
elseif err then
|
|
|
|
io.stderr:write(fname .. "\n" .. err .. "\n\n")
|
|
|
|
end
|
|
|
|
elseif not success then
|
2013-12-27 06:44:54 +00:00
|
|
|
io.stderr:write(table.concat({
|
|
|
|
"",
|
|
|
|
"Error: " .. fname,
|
|
|
|
err,
|
|
|
|
"\n",
|
|
|
|
}, "\n"))
|
2014-06-18 16:23:41 +00:00
|
|
|
elseif success == "build" then
|
|
|
|
log_msg("Built", fname, "->", target)
|
2011-10-09 08:59:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-12-27 06:44:54 +00:00
|
|
|
io.stderr:write("\nQuitting...\n")
|
2013-12-26 09:29:00 +00:00
|
|
|
elseif opts.l then
|
2016-01-07 23:37:47 +00:00
|
|
|
local has_linted_with_error;
|
2015-12-28 06:14:55 +00:00
|
|
|
local lint = require "moonscript.cmd.lint"
|
2014-06-18 03:27:10 +00:00
|
|
|
for _, tuple in pairs(files) do
|
|
|
|
local fname = tuple[1]
|
2015-12-28 06:14:55 +00:00
|
|
|
local res, err = lint.lint_file(fname)
|
2013-12-26 09:29:00 +00:00
|
|
|
if res then
|
2016-01-07 23:37:47 +00:00
|
|
|
has_linted_with_error = true
|
2013-12-27 06:44:54 +00:00
|
|
|
io.stderr:write(res .. "\n\n")
|
2013-12-27 23:32:06 +00:00
|
|
|
elseif err then
|
2016-01-07 23:37:47 +00:00
|
|
|
has_linted_with_error = true
|
2013-12-27 23:32:06 +00:00
|
|
|
io.stderr:write(fname .. "\n" .. err.. "\n\n")
|
2013-12-26 09:29:00 +00:00
|
|
|
end
|
|
|
|
end
|
2016-01-07 23:37:47 +00:00
|
|
|
if has_linted_with_error then
|
|
|
|
os.exit(1)
|
|
|
|
end
|
2011-05-29 16:33:25 +00:00
|
|
|
else
|
2014-06-18 03:27:10 +00:00
|
|
|
for _, tuple in ipairs(files) do
|
2015-03-12 08:09:17 +00:00
|
|
|
local fname, target = util.unpack(tuple)
|
2014-06-19 07:04:10 +00:00
|
|
|
if opts.o then
|
|
|
|
target = opts.o
|
|
|
|
end
|
|
|
|
|
2014-07-09 06:28:45 +00:00
|
|
|
local success, err = compile_and_write(fname, target, {
|
2015-02-28 19:47:58 +00:00
|
|
|
print = opts.p,
|
|
|
|
fname = fname,
|
|
|
|
benchmark = opts.b,
|
|
|
|
show_posmap = opts.X,
|
|
|
|
show_parse_tree = opts.T,
|
2014-07-09 06:28:45 +00:00
|
|
|
})
|
2015-02-28 19:47:58 +00:00
|
|
|
|
2011-05-29 16:33:25 +00:00
|
|
|
if not success then
|
2013-12-27 06:44:54 +00:00
|
|
|
io.stderr:write(fname .. "\t" .. err .. "\n")
|
2011-09-19 06:23:23 +00:00
|
|
|
os.exit(1)
|
2014-06-18 16:23:41 +00:00
|
|
|
elseif success == "build" then
|
|
|
|
log_msg("Built", fname)
|
2011-05-29 16:33:25 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|