#!/usr/bin/env lua local argparse = require "argparse" local lfs = require "lfs" local parser = argparse() parser:flag("-l --lint", "Perform a lint on the file instead of compiling") parser:flag("-v --version", "Print version") parser:flag("-w --watch", "Watch file/directory for updates") parser:mutex( parser:flag("-t --output-to", "Specify where to place compiled files"), parser:flag("-o", "Write output to file"):args(1), parser:flag("-p", "Write output to standard output"), parser:flag("-T", "Write parse tree instead of code (to stdout)"), parser:flag("-b", "Write parse and compile time instead of code(to stdout)"), parser:flag("-X", "Write line rewrite map instead of code (to stdout)") ) parser:flag("-", "Read from standard in, print to standard out (Must be only argument)") local read_stdin = arg[1] == "--" -- luacheck: ignore 113 if not read_stdin then parser:argument("file/directory"):args("+") end local opts = parser:parse() if opts.version then local v = require "moonscript.version" v.print_version() os.exit() end function log_msg(...) if not opts.p then io.stderr:write(table.concat({...}, " ") .. "\n") end end local moonc = require("moonscript.cmd.moonc") local util = require "moonscript.util" local normalize_dir = moonc.normalize_dir local compile_and_write = moonc.compile_and_write local path_to_target = moonc.path_to_target local function scan_directory(root, collected) root = normalize_dir(root) collected = collected or {} for fname in lfs.dir(root) do if not fname:match("^%.") then local full_path = root..fname if lfs.attributes(full_path, "mode") == "directory" then scan_directory(full_path, collected) elseif fname:match("%.moon$") then table.insert(collected, full_path) end end end return collected end local function remove_dups(tbl, key_fn) local hash = {} local final = {} for _, v in ipairs(tbl) do local dup_key = key_fn and key_fn(v) or v if not hash[dup_key] then table.insert(final, v) hash[dup_key] = true end end return final end -- creates tuples of input and target local function get_files(fname, files) files = files or {} if lfs.attributes(fname, "mode") == "directory" then for _, sub_fname in ipairs(scan_directory(fname)) do table.insert(files, { sub_fname, path_to_target(sub_fname, opts.output_to, fname) }) end else table.insert(files, { fname, path_to_target(fname, opts.output_to) }) end return files end if read_stdin then local parse = require "moonscript.parse" local compile = require "moonscript.compile" local text = io.stdin:read("*a") local tree, err = parse.string(text) 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 local inputs = opts["file/directory"] local files = {} for _, input in ipairs(inputs) do get_files(input, files) end files = remove_dups(files, function(f) return f[2] end) -- returns an iterator that returns files that have been updated local function create_watcher(files) local watchers = require("moonscript.cmd.watchers") if watchers.InotifyWacher:available() then return watchers.InotifyWacher(files):each_update() end return watchers.SleepWatcher(files):each_update() end if opts.watch then -- build function to check for lint or compile in watch local handle_file if opts.lint then local lint = require "moonscript.cmd.lint" handle_file = lint.lint_file else handle_file = compile_and_write end local watcher = create_watcher(files) -- catches interrupt error for ctl-c local protected = function() local status, file = true, watcher() if status then return file elseif file ~= "interrupted!" then error(file) end end for fname in protected do local target = path_to_target(fname, opts.t) if opts.o then target = opts.o end local success, err = handle_file(fname, target) if opts.lint 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 io.stderr:write(table.concat({ "", "Error: " .. fname, err, "\n", }, "\n")) elseif success == "build" then log_msg("Built", fname, "->", target) end end io.stderr:write("\nQuitting...\n") elseif opts.lint then local has_linted_with_error; local lint = require "moonscript.cmd.lint" for _, tuple in pairs(files) do local fname = tuple[1] local res, err = lint.lint_file(fname) if res then has_linted_with_error = true io.stderr:write(res .. "\n\n") elseif err then has_linted_with_error = true io.stderr:write(fname .. "\n" .. err.. "\n\n") end end if has_linted_with_error then os.exit(1) end else for _, tuple in ipairs(files) do local fname, target = util.unpack(tuple) if opts.o then target = opts.o end local success, err = compile_and_write(fname, target, { print = opts.p, fname = fname, benchmark = opts.b, show_posmap = opts.X, show_parse_tree = opts.T, }) if not success then io.stderr:write(fname .. "\t" .. err .. "\n") os.exit(1) elseif success == "build" then log_msg("Built", fname) end end end