diff --git a/doc/nomsu.1 b/doc/nomsu.1 index 23a3549..ddeedfa 100644 --- a/doc/nomsu.1 +++ b/doc/nomsu.1 @@ -10,30 +10,44 @@ nomsu \- run a Nomsu program ] [ .I nomsu_file +| +.I -t tool +| +.I -e nomsu_code +| +.I -m nomsu_files... +[--] +] [ .I args ] -] .SH DESCRIPTION \fBnomsu\fR is the compiler/interpreter for the Nomsu programming language. .SH INPUT .TP If an input file is provided, \fBnomsu\fR will run that file. .TP -If an input directory is provided, \fBnomsu\fR will run every .nom file in the directory and its subdirectories. -.TP If "-" is used, \fBnomsu\fR will read from standard input .TP If no input files are provided, \fBnomsu\fR will run in interactive mode. .SH OPTIONS .TP +.BI \-t " tool" +Run the specified Nomsu tool (one of the tools/*.nom files provided with the compiler). +.TP +.BI \-m " file1 file2... " [--] +Run multiple files. If a "--" is supplied, any additional arguments will be treated as arguments for the files, rather than additional files. +.TP +.BI \-e " code" +Execute the given nomsu code string. +.TP .BI \-V " version" Specify the desired Nomsu version (defaults to the latest installed version). E.g. \fBnomsu -V 1.2\fR or \fBnomsu -V 1.2.5.9\fR .TP .BI \-L List the installed versions of Nomsu (if \fB-V\fR is supplied, only print versions that match the requested pattern). .TP -.B \-O " level" +.BI \-O " optimization" Run the compiler with the given optimization level (default: 1). If \fBlevel\fR is >0, use precompiled .lua versions of .nom files, when available. .TP .B \-v @@ -42,23 +56,17 @@ Verbose: print compiled lua code as it's generated for the input files. .B \-c Compile the input files into .lua files. .TP -.B \-s " file" +.B \-s Check the input files for syntax errors without running them. .TP -.B \-I " file" -Include the specified file (or all .nom files in a directory and its subdirectories) in the input files. -.TP -.B \-e " code" -Execute the given nomsu code string. -.TP -.B \-d " debugger" +.BI \-d " debugger" If provided, \fBnomsu\fR will attempt to use the specified debugger to wrap the main body of execution. .TP .B \--version Print the version number and exit. .TP .B \--no-core -Disable the default behavior of running the files in 'core' before running anything else. +Run \fBnomsu\fR without running the files in the 'core' directory (this will disable most functionality). .TP .B \--help Print the command line usage. @@ -73,6 +81,11 @@ Runs nomsu in interactive mode (a read-evaluate-print loop) nomsu my_file.nom Runs the Nomsu file 'my_file.nom' +.TP +.B +nomsu -t autoformat -i my_file.nom +Runs the tools/autoformat.nom script with arguments "-i my_file.nom", which will automatically format my_file.nom in-place. + .TP .B nomsu -c my_file.nom @@ -80,7 +93,7 @@ Compiles the Nomsu file 'my_file.nom' into a Lua file called 'my_file.lua' .TP .B -nomsu -I one.nom -I two.nom three.nom +nomsu -m one.nom two.nom three.nom Runs 'one.nom', then 'two.nom', then 'three.nom'. .TP diff --git a/nomsu.lua b/nomsu.lua index 08ab170..1b5a73e 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -59,15 +59,16 @@ else end local usage = [=[Nomsu Compiler -Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [-O optimization level] [-v] [-c] [-s] [-I file] [--help | -h] [--version] [--no-core] [file [nomsu args...]] +Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [--help | -h] [--version] [-O optimization level] [-v] [-c] [-s] [-d debugger] [--no-core] [(file | -t tool | -e "nomsu code..." | -m files... [--]) [nomsu args...]] OPTIONS + -t Run a tool. + -e Execute the specified string. + -m Run multiple files (all extra arguments). -O Run the compiler with the given optimization level (>0: use precompiled .lua versions of Nomsu files, when available). -v Verbose: print compiled lua code. -c Compile the input files into a .lua files. - -e Execute the specified string. -s Check the input files for syntax errors. - -I Add an additional input file or directory. -d Attempt to use the specified debugger to wrap the main body of execution. -h/--help Print this message. --version Print the version number and exit. @@ -99,36 +100,31 @@ local nomsu_environment = require('nomsu_environment') if not arg or debug.getinfo(2).func == require then return nomsu_environment end -local file_queue = List({ }) local sep = "\3" -local parser = re.compile([[ args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)? - {:nomsu_args: {| (nomsu_flag %sep)* {:extra_args: {| ({[^%sep]+} %sep)* |} :} |} :} |} !. +local parser = re.compile([[ args <- {| (flag %sep)* + (("-e" %sep {:execute: {[^%sep]+} :} %sep) + / {:files: {| + ("-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~} %sep + / "-m" %sep (!("--" %sep) {[^%sep]+} %sep)* ("--" %sep)? + / {[^%sep]+} %sep + / {~ '' %sep? -> 'nomsu://tools/repl.nom' ~}) |} :}) + {:nomsu_args: {| (nomsu_flag %sep)* {:extra_args: {| ({[^%sep]+} %sep)* |} :} |} :} + |} !. flag <- {:optimization: "-O" (%sep? %number)? :} - / ("-I" %sep? ({~ file ~} -> add_file)) - / ("-e" %sep? (({} {~ file ~}) -> add_exec_string) {:exec_strings: %true :}) / ({:check_syntax: "-s" %true:}) / ({:compile: "-c" %true:}) / {:verbose: "-v" %true :} / {:help: ("-h" / "--help") %true :} / {:version: "--version" %true :} / {:no_core: "--no-core" %true :} - / {:debugger: ("-d" %sep? {(!%sep .)*}) :} + / {:debugger: ("-d" %sep? {[^%sep]*}) :} / {:requested_version: "-V" (%sep? {([0-9.])+})? :} nomsu_flag <- {| ({:key: ('-' [a-z]) :} {:value: %true :}) / ({:key: ('--' [^%sep=]+) :} {:value: ('=' {[^%sep]+}) / %true :}) |} - file <- ("-" -> "stdin") / {(!%sep .)+} ]], { ["true"] = lpeg.Cc(true), number = lpeg.R("09") ^ 1 / tonumber, - sep = lpeg.P(sep), - add_file = function(f) - return file_queue:add(f) - end, - add_exec_string = function(pos, s) - local name = "command line arg @" .. tostring(pos) .. ".nom" - Files.spoof(name, s) - return file_queue:add(name) - end + sep = lpeg.P(sep) }) local arg_string = table.concat(arg, sep) .. sep local args = parser:match(arg_string) @@ -145,97 +141,137 @@ end nomsu_args.extra_args = List(args.nomsu_args.extra_args or { }) nomsu_environment.command_line_args = nomsu_args nomsu_environment.OPTIMIZATION = tonumber(args.optimization or 1) -if args.version then - nomsu_environment.run_file_1_in('core', nomsu_environment, nomsu_environment.OPTIMIZATION) - nomsu_environment.run_1_in([[say (Nomsu version)]], nomsu_environment) - os.exit(EXIT_SUCCESS) -end local run run = function() - local input_files = { } - for _index_0 = 1, #file_queue do - local f = file_queue[_index_0] - if not (Files.exists(f)) then - error("Could not find: '" .. tostring(f) .. "'") - end - local _list_1 = Files.list(f) - for _index_1 = 1, #_list_1 do - local filename = _list_1[_index_1] - input_files[filename] = true - end - end if not (args.no_core) then - nomsu_environment.run_file_1_in('core', nomsu_environment, nomsu_environment.OPTIMIZATION) - end - for _index_0 = 1, #file_queue do - local _continue_0 = false - repeat - local filename = file_queue[_index_0] - if not (filename == "stdin" or filename:match("%.nom$")) then + for nomsupath in package.nomsupath:gmatch("[^;]+") do + local _continue_0 = false + repeat + local files = Files.list(nomsupath .. "/core") + if not (files) then + _continue_0 = true + break + end + for _index_0 = 1, #files do + local _continue_1 = false + repeat + local f = files[_index_0] + if not (f:match("%.nom$")) then + _continue_1 = true + break + end + nomsu_environment.run_file_1_in(f, nomsu_environment, nomsu_environment.OPTIMIZATION) + _continue_1 = true + until true + if not _continue_1 then + break + end + end _continue_0 = true + until true + if not _continue_0 then break end - if args.check_syntax then - local code = Files.read(filename) - local source = Source(filename, 1, #code) - nomsu_environment._1_parsed(NomsuCode:from(source, code)) - print("Parse succeeded: " .. tostring(filename)) - elseif args.compile then - local output - if filename == 'stdin' then - output = io.output() - else - output = io.open(filename:gsub("%.nom$", ".lua"), "w") - end - local code = Files.read(filename) - local source = Source(filename, 1, #code) - code = NomsuCode:from(source, code) - local tree = nomsu_environment._1_parsed(code) - if not (tree.type == 'FileChunks') then - tree = { - tree - } - end - for chunk_no, chunk in ipairs(tree) do - local lua = nomsu_environment.compile(chunk) - lua:declare_locals() - lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n") - if args.verbose then - print(lua:text()) - end - nomsu_environment.run_1_in(chunk, nomsu_environment) - output:write(lua:text(), "\n") - end - print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua"))) - output:close() - elseif args.verbose then - local code = Files.read(filename) - local source = Source(filename, 1, #code) - code = NomsuCode:from(source, code) - local tree = nomsu_environment._1_parsed(code) - if not (tree.type == 'FileChunks') then - tree = { - tree - } - end - for chunk_no, chunk in ipairs(tree) do - local lua = nomsu_environment.compile(chunk) - lua:declare_locals() - lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n") - print(lua:text()) - nomsu_environment.run_1_in(lua, nomsu_environment) - end - else - nomsu_environment.run_file_1_in(filename, nomsu_environment, 0) - end - _continue_0 = true - until true - if not _continue_0 then - break end end - if not (args.primary_file or args.exec_strings) then - return nomsu_environment.run_file_1_in("tools/repl.nom", nomsu_environment, nomsu_environment.OPTIMIZATION) + if args.version then + nomsu_environment.run_1_in("say (Nomsu version)", nomsu_environment) + os.exit(EXIT_SUCCESS) + end + local input_files = { } + if args.execute then + table.insert(input_files, Files.spoof("", args.execute)) + end + if args.files then + local _list_1 = args.files + for _index_0 = 1, #_list_1 do + local f = _list_1[_index_0] + local files + do + local nomsu_name = f:match("^nomsu://(.*)") + if nomsu_name then + for nomsupath in package.nomsupath:gmatch("[^;]+") do + local _continue_0 = false + repeat + files = Files.list(nomsupath .. "/" .. nomsu_name) + if not (files) then + _continue_0 = true + break + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + else + files = Files.list(f) + end + end + if not (files and #files > 0) then + error("Could not find: '" .. tostring(f) .. "'") + end + for _index_1 = 1, #files do + local filename = files[_index_1] + table.insert(input_files, filename) + end + end + end + for _index_0 = 1, #input_files do + local filename = input_files[_index_0] + if args.check_syntax then + local code = Files.read(filename) + local source = Source(filename, 1, #code) + nomsu_environment._1_parsed(NomsuCode:from(source, code)) + print("Parse succeeded: " .. tostring(filename)) + elseif args.compile then + local output + if filename == 'stdin' then + output = io.output() + else + output = io.open(filename:gsub("%.nom$", ".lua"), "w") + end + local code = Files.read(filename) + local source = Source(filename, 1, #code) + code = NomsuCode:from(source, code) + local tree = nomsu_environment._1_parsed(code) + if not (tree.type == 'FileChunks') then + tree = { + tree + } + end + for chunk_no, chunk in ipairs(tree) do + local lua = nomsu_environment.compile(chunk) + lua:declare_locals() + lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n") + if args.verbose then + print(lua:text()) + end + nomsu_environment.run_1_in(chunk, nomsu_environment) + output:write(lua:text(), "\n") + end + print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua"))) + output:close() + elseif args.verbose then + local code = Files.read(filename) + local source = Source(filename, 1, #code) + code = NomsuCode:from(source, code) + local tree = nomsu_environment._1_parsed(code) + if not (tree.type == 'FileChunks') then + tree = { + tree + } + end + for chunk_no, chunk in ipairs(tree) do + local lua = nomsu_environment.compile(chunk) + lua:declare_locals() + lua:prepend((chunk_no > 1) and '\n' or '', "-- File " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n") + print(lua:text()) + nomsu_environment.run_1_in(lua, nomsu_environment) + end + else + nomsu_environment.run_file_1_in(filename, nomsu_environment, 0) + end end end local debugger diff --git a/nomsu.moon b/nomsu.moon index fd4e727..d9a7d18 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -20,15 +20,16 @@ else usage = [=[ Nomsu Compiler -Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [-O optimization level] [-v] [-c] [-s] [-I file] [--help | -h] [--version] [--no-core] [file [nomsu args...]] +Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [--help | -h] [--version] [-O optimization level] [-v] [-c] [-s] [-d debugger] [--no-core] [(file | -t tool | -e "nomsu code..." | -m files... [--]) [nomsu args...]] OPTIONS + -t Run a tool. + -e Execute the specified string. + -m Run multiple files (all extra arguments). -O Run the compiler with the given optimization level (>0: use precompiled .lua versions of Nomsu files, when available). -v Verbose: print compiled lua code. -c Compile the input files into a .lua files. - -e Execute the specified string. -s Check the input files for syntax errors. - -I Add an additional input file or directory. -d Attempt to use the specified debugger to wrap the main body of execution. -h/--help Print this message. --version Print the version number and exit. @@ -57,32 +58,30 @@ nomsu_environment = require('nomsu_environment') if not arg or debug.getinfo(2).func == require return nomsu_environment -file_queue = List{} sep = "\3" parser = re.compile([[ - args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)? - {:nomsu_args: {| (nomsu_flag %sep)* {:extra_args: {| ({[^%sep]+} %sep)* |} :} |} :} |} !. + args <- {| (flag %sep)* + (("-e" %sep {:execute: {[^%sep]+} :} %sep) + / {:files: {| + ("-t" %sep {~ {[^%sep]+} -> "nomsu://tools/%1.nom" ~} %sep + / "-m" %sep (!("--" %sep) {[^%sep]+} %sep)* ("--" %sep)? + / {[^%sep]+} %sep + / {~ '' %sep? -> 'nomsu://tools/repl.nom' ~}) |} :}) + {:nomsu_args: {| (nomsu_flag %sep)* {:extra_args: {| ({[^%sep]+} %sep)* |} :} |} :} + |} !. flag <- {:optimization: "-O" (%sep? %number)? :} - / ("-I" %sep? ({~ file ~} -> add_file)) - / ("-e" %sep? (({} {~ file ~}) -> add_exec_string) {:exec_strings: %true :}) / ({:check_syntax: "-s" %true:}) / ({:compile: "-c" %true:}) / {:verbose: "-v" %true :} / {:help: ("-h" / "--help") %true :} / {:version: "--version" %true :} / {:no_core: "--no-core" %true :} - / {:debugger: ("-d" %sep? {(!%sep .)*}) :} + / {:debugger: ("-d" %sep? {[^%sep]*}) :} / {:requested_version: "-V" (%sep? {([0-9.])+})? :} nomsu_flag <- {| ({:key: ('-' [a-z]) :} {:value: %true :}) / ({:key: ('--' [^%sep=]+) :} {:value: ('=' {[^%sep]+}) / %true :}) |} - file <- ("-" -> "stdin") / {(!%sep .)+} ]], { true:lpeg.Cc(true), number:lpeg.R("09")^1/tonumber, sep:lpeg.P(sep) - add_file: (f)-> file_queue\add(f) - add_exec_string: (pos, s)-> - name = "command line arg @#{pos}.nom" - Files.spoof(name, s) - file_queue\add name }) arg_string = table.concat(arg, sep)..sep args = parser\match(arg_string) @@ -96,24 +95,37 @@ nomsu_args.extra_args = List(args.nomsu_args.extra_args or {}) nomsu_environment.command_line_args = nomsu_args nomsu_environment.OPTIMIZATION = tonumber(args.optimization or 1) -if args.version - nomsu_environment.run_file_1_in 'core', nomsu_environment, nomsu_environment.OPTIMIZATION - nomsu_environment.run_1_in([[say (Nomsu version)]], nomsu_environment) - os.exit(EXIT_SUCCESS) - run = -> - input_files = {} - for f in *file_queue - unless Files.exists(f) - error("Could not find: '#{f}'") - for filename in *Files.list(f) - input_files[filename] = true - unless args.no_core - nomsu_environment.run_file_1_in 'core', nomsu_environment, nomsu_environment.OPTIMIZATION + for nomsupath in package.nomsupath\gmatch("[^;]+") + files = Files.list(nomsupath.."/core") + continue unless files + for f in *files + continue unless f\match("%.nom$") + nomsu_environment.run_file_1_in f, nomsu_environment, nomsu_environment.OPTIMIZATION + + if args.version + nomsu_environment.run_1_in("say (Nomsu version)", nomsu_environment) + os.exit(EXIT_SUCCESS) + + input_files = {} + if args.execute + table.insert input_files, Files.spoof("", args.execute) + if args.files + for f in *args.files + local files + if nomsu_name = f\match("^nomsu://(.*)") + for nomsupath in package.nomsupath\gmatch("[^;]+") + files = Files.list(nomsupath.."/"..nomsu_name) + continue unless files + else + files = Files.list(f) + unless files and #files > 0 + error("Could not find: '#{f}'") + for filename in *files + table.insert input_files, filename - for filename in *file_queue - continue unless filename == "stdin" or filename\match("%.nom$") + for filename in *input_files if args.check_syntax -- Check syntax code = Files.read(filename) @@ -154,9 +166,6 @@ run = -> -- Just run the file nomsu_environment.run_file_1_in(filename, nomsu_environment, 0) - unless args.primary_file or args.exec_strings - nomsu_environment.run_file_1_in("tools/repl.nom", nomsu_environment, nomsu_environment.OPTIMIZATION) - debugger = if args.debugger == "nil" then {} else require(args.debugger or 'error_handling') guard = if type(debugger) == 'function' then debugger diff --git a/nomsu_environment.lua b/nomsu_environment.lua index 5838a1d..fc5ab31 100644 --- a/nomsu_environment.lua +++ b/nomsu_environment.lua @@ -312,35 +312,25 @@ local nomsu_environment = Importer({ local _continue_0 = false repeat do - local files = Files.list(nomsupath .. "/" .. path) + local full_path = nomsupath == "." and path or nomsupath .. "/" .. path + local files = Files.list(full_path) if not (files) then _continue_0 = true break end for _index_0 = 1, #files do - local _continue_1 = false - repeat - local filename = files[_index_0] - if not (filename == "stdin" or filename:match("%.nom$")) then - _continue_1 = true - break - end - local lua_filename = filename:gsub("%.nom$", ".lua") - local code - if optimization ~= 0 and Files.read(lua_filename) then - local file = Files.read(lua_filename) - code = LuaCode:from(Source(filename, 1, #file), file) - else - local file = Files.read(filename) - code = NomsuCode:from(Source(filename, 1, #file), file) - end - environment.run_1_in(code, mod) - did_anything = true - _continue_1 = true - until true - if not _continue_1 then - break + local filename = files[_index_0] + local lua_filename = filename:gsub("%.nom$", ".lua") + local code + if optimization ~= 0 and Files.read(lua_filename) then + local file = Files.read(lua_filename) + code = LuaCode:from(Source(filename, 1, #file), file) + else + local file = Files.read(filename) + code = NomsuCode:from(Source(filename, 1, #file), file) end + environment.run_1_in(code, mod) + did_anything = true end break end diff --git a/nomsu_environment.moon b/nomsu_environment.moon index add852a..5d26701 100644 --- a/nomsu_environment.moon +++ b/nomsu_environment.moon @@ -172,13 +172,14 @@ nomsu_environment = Importer{ did_anything = false for nomsupath in package.nomsupath\gmatch("[^;]+") - files = Files.list(nomsupath.."/"..path) + full_path = nomsupath == "." and path or nomsupath.."/"..path + files = Files.list(full_path) continue unless files for filename in *files - continue unless filename == "stdin" or filename\match("%.nom$") lua_filename = filename\gsub("%.nom$", ".lua") -- TODO: don't automatically use precompiled version? code = if optimization != 0 and Files.read(lua_filename) + -- TODO: use a checksum? file = Files.read(lua_filename) LuaCode\from(Source(filename, 1, #file), file) else