From 3a049c15dfd012c8645ba41753d7d6ac72f7d03d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 28 Apr 2018 18:07:14 -0700 Subject: [PATCH] Improvements to command line file handling. --- nomsu.lua | 216 ++++++++++++++++++++++++++++++++--------------------- nomsu.moon | 170 ++++++++++++++++++++++------------------- 2 files changed, 223 insertions(+), 163 deletions(-) diff --git a/nomsu.lua b/nomsu.lua index 2c91540..d8efeeb 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -34,6 +34,7 @@ do local _obj_0 = require("code_obj") Nomsu, Lua, Source = _obj_0.Nomsu, _obj_0.Lua, _obj_0.Source end +local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" FILE_CACHE = setmetatable({ }, { __index = function(self, filename) local file = io.open(filename) @@ -56,7 +57,7 @@ iterate_single = function(item, prev) end local all_files all_files = function(path) - if path:match("%.nom$") or path:match("%.lua$") then + if path:match("%.nom$") or path:match("%.lua$") or path:match("^/dev/fd/[012]$") then return iterate_single, path end path = path:gsub("\\", "\\\\"):gsub("`", ""):gsub('"', '\\"'):gsub("$", "") @@ -388,7 +389,7 @@ do if filename:match("%.lua$") then local file = assert(FILE_CACHE[filename], "Could not find file: " .. tostring(filename)) ret = self:run_lua(Lua(Source(filename), file)) - elseif filename:match("%.nom$") then + elseif filename:match("%.nom$") or filename:match("^/dev/fd/[012]$") then if not self.skip_precompiled then local lua_filename = filename:gsub("%.nom$", ".lua") local file = FILE_CACHE[lua_filename] @@ -1032,89 +1033,13 @@ OPTIONS end return info end - local run - run = function() - if args.flags["-v"] then - nomsu.debug = true + local print_err_msg + print_err_msg = function(error_message, stack_offset) + if stack_offset == nil then + stack_offset = 2 end - if args.input == "-" then - args.input = "/dev/fd/0" - end - if args.output == nil then - args.output = "/dev/null" - elseif args.output == "-" then - args.output = "/dev/fd/1" - end - nomsu.skip_precompiled = not args.flags["-O"] - if args.input then - local compile_fn = nil - if args.output == "/dev/fd/1" and not args.print_file then - args.print_file = "/dev/null" - elseif not args.print_file or args.print_file == "-" then - args.print_file = "/dev/fd/1" - end - local print_file = io.open(args.print_file, "w") - nomsu.environment.print = function(...) - local N = select("#", ...) - if N > 0 then - print_file:write(tostring(select(1, ...))) - for i = 2, N do - print_file:write('\t', tostring(select(1, ...))) - end - end - print_file:write('\n') - return print_file:flush() - end - local output_file = io.open(args.output, 'w') - compile_fn = function(code) - return output_file:write("local IMMEDIATE = true;\n" .. tostring(code)) - end - if args.input:match("%.lua$") then - dofile(args.input)(nomsu, { }) - else - for input_file in all_files(args.input) do - if args.flags["-s"] then - nomsu:parse(io.open(input_file):read("*a")) - elseif args.flags["-f"] then - local tree = nomsu:parse(io.open(input_file):read("*a")) - output_file:write(tostring(tree:as_nomsu())) - else - nomsu:run_file(input_file, compile_fn) - end - end - end - end - if not args.input or args.flags["-i"] then - nomsu:run('use "core"') - while true do - io.write(colored.bright(colored.yellow(">> "))) - local buff = "" - while true do - local line = io.read("*L") - if line == "\n" or not line then - break - end - line = line:gsub("\t", " ") - buff = buff .. line - io.write(colored.dim(colored.yellow(".. "))) - end - if #buff == 0 then - break - end - local ret - ok, ret = pcall(nomsu.run, nomsu, buff) - if ok and ret ~= nil then - print("= " .. repr(ret)) - elseif not ok then - print(colored.bright(colored.red(ret))) - end - end - end - end - local err_hand - err_hand = function(error_message) - print(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.yellow(colored.onred((error_message or "")))))) - print("stack traceback:") + io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n") + io.stderr:write("stack traceback:\n") ok, to_lua = pcall(function() return require('moonscript.base').to_lua end) @@ -1125,7 +1050,7 @@ OPTIONS end local nomsu_source = FILE_CACHE["nomsu.moon"] local _, line_table = to_lua(nomsu_source) - local level = 2 + local level = stack_offset while true do local _continue_0 = false repeat @@ -1182,15 +1107,132 @@ OPTIONS end end local _from = colored.dim(colored.white("|")) - print(("%32s %s %s"):format(name, _from, line)) + io.stderr:write(("%32s %s %s\n"):format(name, _from, line)) _continue_0 = true until true if not _continue_0 then break end end + return io.stderr:flush() + end + local run + run = function() + if args.flags["-v"] then + nomsu.debug = true + end + if args.input == "-" then + args.input = STDIN + end + local output_file + if args.output == "-" then + output_file = io.stdout + elseif args.output then + output_file = io.open(args.output, 'w') + end + local print_file + if args.print_file == "-" then + print_file = io.stdout + elseif args.print_file then + print_file = io.open(args.print_file, 'w') + elseif output_file == io.stdout then + print_file = nil + else + print_file = io.stdout + end + nomsu.skip_precompiled = not args.flags["-O"] + if args.input then + local compile_fn = nil + if print_file == nil then + nomsu.environment.print = function() end + elseif print_file ~= io.stdout then + nomsu.environment.print = function(...) + local N = select("#", ...) + if N > 0 then + print_file:write(tostring(select(1, ...))) + for i = 2, N do + print_file:write('\t', tostring(select(1, ...))) + end + end + print_file:write('\n') + return print_file:flush() + end + end + if output_file then + compile_fn = function(code) + output_file:write("local IMMEDIATE = true;\n" .. tostring(code)) + return output_file:flush() + end + end + if args.flags["-s"] then + for input_file in all_files(args.input) do + nomsu:parse(io.open(input_file):read("*a")) + end + if print_file then + print_file:write("Success!\n") + print_file:flush() + end + elseif args.flags["-f"] then + for input_file in all_files(args.input) do + local tree = nomsu:parse(io.open(input_file):read("*a")) + local formatted = tostring(tree:as_nomsu()) + if output_file then + output_file:write(formatted, "\n") + output_file:flush() + end + if print_file then + print_file:write(formatted, "\n") + print_file:flush() + end + end + elseif args.input == STDIN then + nomsu:run(io.input():read("*a"), compile_fn) + else + nomsu:run_file(args.input, compile_fn) + end + end + if not args.input or args.flags["-i"] then + nomsu:run('use "core"') + while true do + io.write(colored.bright(colored.yellow(">> "))) + local buff = "" + while true do + local line = io.read("*L") + if line == "\n" or not line then + if #buff > 0 then + io.write("\027[1A\027[2K") + end + break + end + line = line:gsub("\t", " ") + buff = buff .. line + io.write(colored.dim(colored.yellow(".. "))) + end + if #buff == 0 then + break + end + local ret + ok, ret = pcall(nomsu.run, nomsu, buff) + if ok and ret ~= nil then + print("= " .. repr(ret)) + elseif not ok then + print_err_msg(ret) + end + end + end + end + local err_hand + err_hand = function(error_message) + print_err_msg(error_message) return os.exit(false, true) end - require('ldt').guard(run) + do + local ldt = require('ldt') + if ldt then + ldt.guard(run) + else + xpcall(run, err_hand) + end + end end return NomsuCompiler diff --git a/nomsu.moon b/nomsu.moon index c38417a..ed056dd 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -26,6 +26,7 @@ colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring {:insert, :remove, :concat} = table debug_getinfo = debug.getinfo {:Nomsu, :Lua, :Source} = require "code_obj" +STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions @@ -52,7 +53,7 @@ FILE_CACHE = setmetatable {}, { iterate_single = (item, prev) -> if item == prev then nil else item all_files = (path)-> -- Sanitize path - if path\match("%.nom$") or path\match("%.lua$") + if path\match("%.nom$") or path\match("%.lua$") or path\match("^/dev/fd/[012]$") return iterate_single, path -- TODO: improve sanitization path = path\gsub("\\","\\\\")\gsub("`","")\gsub('"','\\"')\gsub("$","") @@ -336,7 +337,7 @@ class NomsuCompiler if filename\match("%.lua$") file = assert(FILE_CACHE[filename], "Could not find file: #{filename}") ret = @run_lua(Lua(Source(filename), file)) - elseif filename\match("%.nom$") + elseif filename\match("%.nom$") or filename\match("^/dev/fd/[012]$") if not @skip_precompiled -- Look for precompiled version lua_filename = filename\gsub("%.nom$", ".lua") file = FILE_CACHE[lua_filename] @@ -727,84 +728,17 @@ OPTIONS ]=] return info - - run = -> - if args.flags["-v"] - nomsu.debug = true - - if args.input == "-" then args.input = "/dev/fd/0" -- stdin - if args.output == nil then args.output = "/dev/null" - elseif args.output == "-" then args.output = "/dev/fd/1" -- stdout - - nomsu.skip_precompiled = not args.flags["-O"] - if args.input - compile_fn = nil - if args.output == "/dev/fd/1" and not args.print_file - args.print_file = "/dev/null" - elseif not args.print_file or args.print_file == "-" - args.print_file = "/dev/fd/1" -- stdout - - print_file = io.open(args.print_file, "w") - nomsu.environment.print = (...)-> - N = select("#",...) - if N > 0 - print_file\write(tostring(select(1,...))) - for i=2,N - print_file\write('\t',tostring(select(1,...))) - print_file\write('\n') - print_file\flush! - - output_file = io.open(args.output, 'w') - compile_fn = (code)-> - output_file\write("local IMMEDIATE = true;\n"..tostring(code)) - - if args.input\match("%.lua$") - dofile(args.input)(nomsu, {}) - else - for input_file in all_files(args.input) - -- Check syntax: - if args.flags["-s"] - nomsu\parse(io.open(input_file)\read("*a")) - elseif args.flags["-f"] - tree = nomsu\parse(io.open(input_file)\read("*a")) - output_file\write(tostring(tree\as_nomsu!)) - else - nomsu\run_file(input_file, compile_fn) - - if not args.input or args.flags["-i"] - -- REPL - nomsu\run('use "core"') - while true - io.write(colored.bright colored.yellow ">> ") - buff = "" - while true - line = io.read("*L") - if line == "\n" or not line - break - line = line\gsub("\t", " ") - buff ..= line - io.write(colored.dim colored.yellow ".. ") - if #buff == 0 - break - ok, ret = pcall(nomsu.run, nomsu, buff) - if ok and ret != nil - print "= "..repr(ret) - elseif not ok - print colored.bright colored.red ret - - err_hand = (error_message)-> - -- TODO: write properly to stderr - print("#{colored.red "ERROR:"} #{colored.bright colored.yellow colored.onred (error_message or "")}") - print("stack traceback:") + print_err_msg = (error_message, stack_offset=2)-> + io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n") + io.stderr\write("stack traceback:\n") -- TODO: properly print out the calling site of nomsu code, not just the *called* code - ok, to_lua = pcall -> require('moonscript.base').to_lua if not ok then to_lua = -> nil nomsu_source = FILE_CACHE["nomsu.moon"] _, line_table = to_lua(nomsu_source) - level = 2 + level = stack_offset while true -- TODO: reduce duplicate code calling_fn = debug_getinfo(level) @@ -841,8 +775,91 @@ OPTIONS line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline}") name = colored.bright(colored.blue(name or "???")) _from = colored.dim colored.white "|" - print(("%32s %s %s")\format(name, _from, line)) + io.stderr\write(("%32s %s %s\n")\format(name, _from, line)) + io.stderr\flush! + + run = -> + if args.flags["-v"] + nomsu.debug = true + + if args.input == "-" then args.input = STDIN + output_file = if args.output == "-" then io.stdout + elseif args.output then io.open(args.output, 'w') + + print_file = if args.print_file == "-" then io.stdout + elseif args.print_file then io.open(args.print_file, 'w') + elseif output_file == io.stdout then nil + else io.stdout + + nomsu.skip_precompiled = not args.flags["-O"] + if args.input + compile_fn = nil + if print_file == nil + nomsu.environment.print = -> + elseif print_file != io.stdout + nomsu.environment.print = (...)-> + N = select("#",...) + if N > 0 + print_file\write(tostring(select(1,...))) + for i=2,N + print_file\write('\t',tostring(select(1,...))) + print_file\write('\n') + print_file\flush! + + if output_file + compile_fn = (code)-> + output_file\write("local IMMEDIATE = true;\n"..tostring(code)) + output_file\flush! + + if args.flags["-s"] + -- Check syntax: + for input_file in all_files(args.input) + nomsu\parse(io.open(input_file)\read("*a")) + if print_file + print_file\write("Success!\n") + print_file\flush! + elseif args.flags["-f"] + -- Auto-format + for input_file in all_files(args.input) + tree = nomsu\parse(io.open(input_file)\read("*a")) + formatted = tostring(tree\as_nomsu!) + if output_file + output_file\write(formatted, "\n") + output_file\flush! + if print_file + print_file\write(formatted, "\n") + print_file\flush! + elseif args.input == STDIN + nomsu\run(io.input!\read("*a"), compile_fn) + else + nomsu\run_file(args.input, compile_fn) + + if not args.input or args.flags["-i"] + -- REPL + nomsu\run('use "core"') + while true + io.write(colored.bright colored.yellow ">> ") + buff = "" + while true + line = io.read("*L") + if line == "\n" or not line + if #buff > 0 + io.write("\027[1A\027[2K") + break -- Run buffer + line = line\gsub("\t", " ") + buff ..= line + io.write(colored.dim colored.yellow ".. ") + if #buff == 0 + break -- Exit + ok, ret = pcall(nomsu.run, nomsu, buff) + if ok and ret != nil + print "= "..repr(ret) + elseif not ok + print_err_msg ret + + err_hand = (error_message)-> + print_err_msg error_message os.exit(false, true) -- Note: xpcall has a slightly different API in Lua <=5.1 vs. >=5.2, but this works @@ -851,8 +868,9 @@ OPTIONS --ProFi = require 'ProFi' --ProFi\start() - require('ldt').guard run - --xpcall(run, err_hand) + if ldt = require('ldt') + ldt.guard run + else xpcall(run, err_hand) --ProFi\stop() --ProFi\writeReport( 'MyProfilingReport.txt' )