diff --git a/README.md b/README.md index fe679b0..57c26bd 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Nomsu's only dependencies are [Lua 5.2 or later](https://www.lua.org/) (tested w ## Usage * To get a nomsu [REPL](https://en.wikipedia.org/wiki/Read-eval-print_loop), simply run `lua nomsu.lua`. -* To run a .nom file with nomsu code, run `lua nomsu.lua your_file.nom`. Or `lua nomsu.lua -` if reading from stdin. -* (Advanced/optional) To precompile a .nom file into lua, run `lua nomsu.lua -o ` to precompile your file into lua (by default, saved at `.lua`). If you run the compiler with the "-O" (for Optimized) flag, the compiler will run the precompiled lua files directly, instead of parsing and compiling the .nom files before running them. This is not necessary, but it can speed things up if you precompile a bunch of code that involves macros or compile-time activity. +* To run a .nom file with nomsu code, run `lua nomsu.lua your_file.nom`. +* (Advanced/optional) To precompile a .nom file into lua, run `lua nomsu.lua -c ` to precompile your file into lua (by default, saved in the same location, but with a `.lua` extension instead of `.nom`). If you run the compiler with the "-O" (for Optimized) flag, the compiler will run the precompiled lua files directly, instead of parsing and compiling the .nom files before running them. This is not necessary, but it can speed things up if you precompile a bunch of code that involves macros or compile-time activity. * More usage options are avilable via `lua nomsu.lua --help`. ## Layout diff --git a/compile_lib.sh b/compile_lib.sh index d7df5f8..463bb20 100755 --- a/compile_lib.sh +++ b/compile_lib.sh @@ -4,13 +4,5 @@ set -e moonc *.moon rm -f core/*.lua lib/*.lua -for file in core/*.nom; do - printf "Compiling $file ..." - luajit ./nomsu.lua -O -o "core/$(basename $file .nom).lua" $file - echo "done." -done -for file in lib/*.nom; do - printf "Compiling $file ..." - luajit ./nomsu.lua -O -o "lib/$(basename $file .nom).lua" $file - echo "done." -done +luajit ./nomsu.lua -c core lib +echo "done." diff --git a/nomsu.lua b/nomsu.lua index ea1ba1b..a162b65 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -316,10 +316,7 @@ do end return tree end, - run = function(self, nomsu_code, compile_fn) - if compile_fn == nil then - compile_fn = nil - end + run = function(self, nomsu_code) local tree = assert(self:parse(nomsu_code)) if type(tree) == 'number' then return nil @@ -327,15 +324,12 @@ do local lua = self:tree_to_lua(tree):as_statements() lua:declare_locals() lua:prepend("-- File: " .. tostring(nomsu_code.source or "") .. "\n") - if compile_fn then - compile_fn(lua) + if self.compile_fn then + self.compile_fn(lua, nomsu_code.source.filename) end return self:run_lua(lua) end, - run_file = function(self, filename, compile_fn) - if compile_fn == nil then - compile_fn = nil - end + run_file = function(self, filename) local loaded = self.environment.LOADED if loaded[filename] then return loaded[filename] @@ -384,7 +378,7 @@ do if not file then error("File does not exist: " .. tostring(filename), 0) end - ret = self:run(Nomsu(Source(filename, 1, #file), file), compile_fn) + ret = self:run(Nomsu(Source(filename, 1, #file), file)) else error("Invalid filetype for " .. tostring(filename), 0) end @@ -1355,7 +1349,8 @@ if arg and debug_getinfo(2).func ~= require then / {:format: ("-f" -> true) :} / {:syntax: ("-s" -> true) :} / {:print_file: "-p" ";" {file} :} - / {:output_file: "-o" ";" {file} :} + / {:compile: ("-c" -> true) :} + / {:verbose: ("-v" -> true) :} / {:help: (("-h" / "--help") -> true) :} file <- "-" / [^;]+ ]], { @@ -1368,15 +1363,16 @@ if arg and debug_getinfo(2).func ~= require then if not args or args.help then print([=[Nomsu Compiler -Usage: (lua nomsu.lua | moon nomsu.moon) [-i] [-O] [-f] [-s] [--help] [-o output] [-p print_file] file1 file2... [-- nomsu args...] +Usage: (lua nomsu.lua | moon nomsu.moon) [-i] [-O] [-v] [-c] [-f] [-s] [--help] [-p print_file] file1 file2... [-- nomsu args...] OPTIONS -i Run the compiler in interactive mode (REPL) -O Run the compiler in optimized mode (use precompiled .lua versions of Nomsu files, when available) + -v Verbose: print compiled lua code + -c Compile .nom files into .lua files -f Auto-format the given Nomsu file and print the result. -s Check the program for syntax errors. -h/--help Print this message. - -o Output the compiled Lua file to the given file (use "-" to output to stdout; if outputting to stdout and -p is not specified, -p will default to /dev/null) -p Print to the specified file instead of stdout. Input file can be "-" to use stdin. ]=]) @@ -1613,8 +1609,6 @@ OPTIONS print_file = io.stdout elseif args.print_file then print_file = io.open(args.print_file, 'w') - elseif args.output_file == '-' then - print_file = nil else print_file = io.stdout end @@ -1634,55 +1628,64 @@ OPTIONS return print_file:flush() end end - local compile_fn - if args.output_file then - compile_fn = function(code) - local output_file - if args.output_file == "-" then - output_file = io.stdout - elseif args.output_file then - output_file = io.open(args.output_file, 'w') - end - output_file:write("local IMMEDIATE = true;\n" .. tostring(code)) - return output_file:flush() - end - else - compile_fn = nil - end - local parse_errs = { } + local input_files = { } + local to_run = { } local _list_0 = args.inputs for _index_0 = 1, #_list_0 do local input = _list_0[_index_0] - if args.syntax then - for input_file in all_files(input) do - local err - ok, err = pcall(nomsu.parse, nomsu, Nomsu(input_file, io.open(input_file):read("*a"))) - if not ok then - insert(parse_errs, err) - elseif print_file then - print_file:write("Parse succeeded: " .. tostring(input_file) .. "\n") - print_file:flush() + for f in all_files(input) do + input_files[#input_files + 1] = f + to_run[f] = true + end + end + if args.compile or args.verbose then + nomsu.compile_fn = function(code, from_file) + if to_run[from_file] then + if args.verbose then + io.write(tostring(code), "\n") end + if args.compile and from_file:match("%.nom$") then + local output_filename = from_file:gsub("%.nom$", ".lua") + local output_file = io.open(output_filename, 'w') + output_file:write("local IMMEDIATE = true;\n", tostring(code)) + output_file:flush() + print(("Compiled %-25s -> %s"):format(from_file, output_filename)) + return output_file:close() + end + end + end + else + nomsu.compile_fn = nil + end + local parse_errs = { } + for _index_0 = 1, #input_files do + local filename = input_files[_index_0] + if args.syntax then + local err + ok, err = pcall(nomsu.parse, nomsu, Nomsu(filename, io.open(filename):read("*a"))) + if not ok then + insert(parse_errs, err) + elseif print_file then + print_file:write("Parse succeeded: " .. tostring(filename) .. "\n") + print_file:flush() end elseif args.format then - for input_file in all_files(input) do - local tree = nomsu:parse(io.open(input_file):read("*a")) - local formatted = tostring(self:tree_to_nomsu(tree)) - 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 + local file = FILE_CACHE[filename] + if not file then + error("File does not exist: " .. tostring(filename), 0) end - elseif input == STDIN then + local tree = nomsu:parse(Nomsu(Source(filename, 1, #file), file)) + local formatted = tostring(nomsu:tree_to_nomsu(tree)) + if print_file then + print_file:write(formatted, "\n") + print_file:flush() + end + elseif filename == STDIN then local file = io.input():read("*a") FILE_CACHE.stdin = file - nomsu:run(Nomsu(Source('stdin', 1, #file), file), compile_fn) + nomsu:run(Nomsu(Source('stdin', 1, #file), file)) else - nomsu:run_file(input, compile_fn) + nomsu:run_file(filename) end end if #parse_errs > 0 then @@ -1693,9 +1696,9 @@ OPTIONS os.exit(true, true) end if args.interactive then - while true do + for repl_line = 1, math.huge do io.write(colored.bright(colored.yellow(">> "))) - local buff = "" + local buff = { } while true do local line = io.read("*L") if line == "\n" or not line then @@ -1705,14 +1708,21 @@ OPTIONS break end line = line:gsub("\t", " ") - buff = buff .. line + insert(buff, line) io.write(colored.dim(colored.yellow(".. "))) end if #buff == 0 then break end + buff = concat(buff) + FILE_CACHE["REPL#" .. repl_line] = buff + local code = Nomsu(Source("REPL#" .. repl_line, 1, #buff), buff) + local err_hand + err_hand = function(error_message) + return print_err_msg(error_message) + end local ret - ok, ret = pcall(nomsu.run, nomsu, buff) + ok, ret = xpcall(nomsu.run, err_hand, nomsu, code) if ok and ret ~= nil then print("= " .. repr(ret)) elseif not ok then diff --git a/nomsu.moon b/nomsu.moon index 3c38b1f..94e6aa2 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -9,7 +9,7 @@ -- nomsu = Nomsu() -- nomsu:run(your_nomsu_code) -- Or from the command line: --- lua nomsu.lua [input_file [output_file or -]] +-- lua nomsu.lua your_file.nom export lpeg, re _pairs, _ipairs = pairs, ipairs if jit @@ -324,19 +324,19 @@ class NomsuCompiler return tree - run: (nomsu_code, compile_fn=nil)=> + run: (nomsu_code)=> tree = assert(@parse(nomsu_code)) if type(tree) == 'number' -- Happens if pattern matches, but there are no captures, e.g. an empty string return nil lua = @tree_to_lua(tree)\as_statements! lua\declare_locals! lua\prepend "-- File: #{nomsu_code.source or ""}\n" - if compile_fn - compile_fn(lua) + if @compile_fn + self.compile_fn(lua, nomsu_code.source.filename) return @run_lua(lua) _running_files = {} -- For detecting circular imports - run_file: (filename, compile_fn=nil)=> + run_file: (filename)=> loaded = @environment.LOADED if loaded[filename] return loaded[filename] @@ -367,7 +367,7 @@ class NomsuCompiler file = file or FILE_CACHE[filename] if not file error("File does not exist: #{filename}", 0) - ret = @run(Nomsu(Source(filename,1,#file), file), compile_fn) + ret = @run(Nomsu(Source(filename,1,#file), file)) else error("Invalid filetype for #{filename}", 0) loaded[filename] = ret or true @@ -966,7 +966,8 @@ if arg and debug_getinfo(2).func != require / {:format: ("-f" -> true) :} / {:syntax: ("-s" -> true) :} / {:print_file: "-p" ";" {file} :} - / {:output_file: "-o" ";" {file} :} + / {:compile: ("-c" -> true) :} + / {:verbose: ("-v" -> true) :} / {:help: (("-h" / "--help") -> true) :} file <- "-" / [^;]+ ]], {true: -> true}) @@ -976,15 +977,16 @@ if arg and debug_getinfo(2).func != require print [=[ Nomsu Compiler -Usage: (lua nomsu.lua | moon nomsu.moon) [-i] [-O] [-f] [-s] [--help] [-o output] [-p print_file] file1 file2... [-- nomsu args...] +Usage: (lua nomsu.lua | moon nomsu.moon) [-i] [-O] [-v] [-c] [-f] [-s] [--help] [-p print_file] file1 file2... [-- nomsu args...] OPTIONS -i Run the compiler in interactive mode (REPL) -O Run the compiler in optimized mode (use precompiled .lua versions of Nomsu files, when available) + -v Verbose: print compiled lua code + -c Compile .nom files into .lua files -f Auto-format the given Nomsu file and print the result. -s Check the program for syntax errors. -h/--help Print this message. - -o Output the compiled Lua file to the given file (use "-" to output to stdout; if outputting to stdout and -p is not specified, -p will default to /dev/null) -p Print to the specified file instead of stdout. Input file can be "-" to use stdin. ]=] @@ -1131,7 +1133,6 @@ OPTIONS print_file = if args.print_file == "-" then io.stdout elseif args.print_file then io.open(args.print_file, 'w') - elseif args.output_file == '-' then nil else io.stdout nomsu.skip_precompiled = not args.optimized @@ -1147,42 +1148,53 @@ OPTIONS print_file\write('\n') print_file\flush! - compile_fn = if args.output_file - (code)-> - output_file = if args.output_file == "-" then io.stdout - elseif args.output_file then io.open(args.output_file, 'w') - output_file\write("local IMMEDIATE = true;\n"..tostring(code)) - output_file\flush! + input_files = {} + to_run = {} + for input in *args.inputs + for f in all_files(input) + input_files[#input_files+1] = f + to_run[f] = true + + nomsu.compile_fn = if args.compile or args.verbose + (code, from_file)-> + if to_run[from_file] + if args.verbose + io.write(tostring(code), "\n") + if args.compile and from_file\match("%.nom$") + output_filename = from_file\gsub("%.nom$", ".lua") + output_file = io.open(output_filename, 'w') + output_file\write("local IMMEDIATE = true;\n", tostring(code)) + output_file\flush! + print ("Compiled %-25s -> %s")\format(from_file, output_filename) + output_file\close! else nil parse_errs = {} - for input in *args.inputs + for filename in *input_files if args.syntax -- Check syntax: - for input_file in all_files(input) - ok,err = pcall nomsu.parse, nomsu, Nomsu(input_file, io.open(input_file)\read("*a")) - if not ok - insert parse_errs, err - elseif print_file - print_file\write("Parse succeeded: #{input_file}\n") - print_file\flush! + ok,err = pcall nomsu.parse, nomsu, Nomsu(filename, io.open(filename)\read("*a")) + if not ok + insert parse_errs, err + elseif print_file + print_file\write("Parse succeeded: #{filename}\n") + print_file\flush! elseif args.format -- Auto-format - for input_file in all_files(input) - tree = nomsu\parse(io.open(input_file)\read("*a")) - formatted = tostring(@tree_to_nomsu(tree)) - if output_file - output_file\write(formatted, "\n") - output_file\flush! - if print_file - print_file\write(formatted, "\n") - print_file\flush! - elseif input == STDIN + file = FILE_CACHE[filename] + if not file + error("File does not exist: #{filename}", 0) + tree = nomsu\parse(Nomsu(Source(filename,1,#file), file)) + formatted = tostring(nomsu\tree_to_nomsu(tree)) + if print_file + print_file\write(formatted, "\n") + print_file\flush! + elseif filename == STDIN file = io.input!\read("*a") FILE_CACHE.stdin = file - nomsu\run(Nomsu(Source('stdin',1,#file), file), compile_fn) + nomsu\run(Nomsu(Source('stdin',1,#file), file)) else - nomsu\run_file(input, compile_fn) + nomsu\run_file(filename) if #parse_errs > 0 io.stderr\write concat(parse_errs, "\n\n") @@ -1193,9 +1205,9 @@ OPTIONS if args.interactive -- REPL - while true + for repl_line=1,math.huge io.write(colored.bright colored.yellow ">> ") - buff = "" + buff = {} while true line = io.read("*L") if line == "\n" or not line @@ -1203,11 +1215,17 @@ OPTIONS io.write("\027[1A\027[2K") break -- Run buffer line = line\gsub("\t", " ") - buff ..= line + insert buff, line io.write(colored.dim colored.yellow ".. ") if #buff == 0 break -- Exit - ok, ret = pcall(nomsu.run, nomsu, buff) + + buff = concat(buff) + FILE_CACHE["REPL#"..repl_line] = buff + code = Nomsu(Source("REPL#"..repl_line, 1, #buff), buff) + err_hand = (error_message)-> + print_err_msg error_message + ok, ret = xpcall(nomsu.run, err_hand, nomsu, code) if ok and ret != nil print "= "..repr(ret) elseif not ok