Simplified the command line interface.

This commit is contained in:
Bruce Hill 2018-07-09 16:58:46 -07:00
parent 0923b0192c
commit 11e9e36636
7 changed files with 300 additions and 350 deletions

View File

@ -1,6 +1,6 @@
.\" Manpage for nomsu. .\" Manpage for nomsu.
.\" Contact bruce@bruce-hill.com to correct errors or typos. .\" Contact bruce@bruce-hill.com to correct errors or typos.
.TH man 8 "24 June 2018" "1.0" "nomsu man page" .TH man 8 "9 July 2018" "1.1" "nomsu man page"
.SH NAME .SH NAME
nomsu \- run a Nomsu program nomsu \- run a Nomsu program
.SH SYNOPSIS .SH SYNOPSIS
@ -9,27 +9,26 @@ nomsu \- run a Nomsu program
.I options .I options
] ]
[ [
.I scripts .I nomsu_file
[ -- [
.I args .I args
] ]
] ]
.SH DESCRIPTION .SH DESCRIPTION
.B \fBnomsu\fR is the compiler/interpreter for the Nomsu programming language.
nomsu .SH INPUT
is the compiler/interpreter for the Nomsu programming language. .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 .SH OPTIONS
.TP .TP
.BI \-V " version" .BI \-V " version"
Specify the desired Nomsu version (defaults to the latest installed version). E.g. 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
.B
nomsu -V 1.2
or
.B
nomsu -V 1.2.5.9
.TP
.B \-i
Enter interactive mode after the input files are executed.
.TP .TP
.B \-O .B \-O
Run the compiler in optimized mode (use precompiled .lua versions of .nom files, when available) Run the compiler in optimized mode (use precompiled .lua versions of .nom files, when available)
@ -38,13 +37,13 @@ Run the compiler in optimized mode (use precompiled .lua versions of .nom files,
Verbose: print compiled lua code as it's generated for the input files. Verbose: print compiled lua code as it's generated for the input files.
.TP .TP
.B \-c .B \-c
Compile input files into .lua files Compile the input files into .lua files.
.TP .TP
.B \-f .B \-s " file"
Auto-format the given Nomsu file and print the result. Check the input files for syntax errors without running them.
.TP .TP
.B \-s .B \-I " file"
Check the program for syntax errors. Include the specified file (or all .nom files in a directory and its subdirectories) in the input files.
.TP .TP
.B \--version .B \--version
Print the version number and exit. Print the version number and exit.
@ -69,8 +68,8 @@ Compiles the Nomsu file 'my_file.nom' into a Lua file called 'my_file.lua'
.TP .TP
.B .B
nomsu my_directory nomsu -I one.nom -I two.nom three.nom
Runs every '.nom' file in 'my_directory' or its subdirectories. Runs 'one.nom', then 'two.nom', then 'three.nom'.
.TP .TP
.B .B

View File

@ -56,30 +56,52 @@ iterate_single = function(item, prev)
end end
local ok, lfs = pcall(require, "lfs") local ok, lfs = pcall(require, "lfs")
if ok then if ok then
files.walk = function(path) local raw_file_exists
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin' then raw_file_exists = function(filename)
return iterate_single, path local mode = lfs.attributes(filename, 'mode')
if mode == 'file' or mode == 'directory' then
return true
else
return false
end end
local browse end
browse = function(filename) files.exists = function(path)
local file_type = lfs.attributes(filename, 'mode') if path == 'stdin' or raw_file_exists(path) then
if file_type == 'file' then return true
if match(filename, "%.nom$") or match(filename, "%.lua$") then end
coroutine.yield(filename) if package.nomsupath then
for nomsupath in package.nomsupath:gmatch("[^;]+") do
if raw_file_exists(nomsupath .. "/" .. path) then
return true return true
end end
elseif file_type == 'directory' then end
for subfile in lfs.dir(filename) do end
if not (subfile == "." or subfile == ".." or not subfile:match("%.nom$")) then return false
browse(filename .. "/" .. subfile) end
end local browse
end browse = function(filename)
return true local file_type = lfs.attributes(filename, 'mode')
elseif file_type == 'char device' then if file_type == 'file' then
if match(filename, "%.nom$") or match(filename, "%.lua$") then
coroutine.yield(filename) coroutine.yield(filename)
return true return true
end end
return false elseif file_type == 'directory' then
for subfile in lfs.dir(filename) do
if not (subfile == "." or subfile == ".." or not subfile:match("%.nom$")) then
browse(filename .. "/" .. subfile)
end
end
return true
elseif file_type == 'char device' then
coroutine.yield(filename)
return true
end
return false
end
files.walk = function(path)
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin' then
return iterate_single, path
end end
return coroutine.wrap(function() return coroutine.wrap(function()
if not browse(path) and package.nomsupath then if not browse(path) and package.nomsupath then
@ -96,14 +118,32 @@ else
if io.popen('find . -maxdepth 0'):close() then if io.popen('find . -maxdepth 0'):close() then
error("Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: http://keplerproject.github.io/luafilesystem/ or `luarocks install luafilesystem`)", 0) error("Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: http://keplerproject.github.io/luafilesystem/ or `luarocks install luafilesystem`)", 0)
end end
files.walk = function(path) local sanitize
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin' then sanitize = function(path)
return iterate_single, path
end
path = gsub(path, "\\", "\\\\") path = gsub(path, "\\", "\\\\")
path = gsub(path, "`", "") path = gsub(path, "`", "")
path = gsub(path, '"', '\\"') path = gsub(path, '"', '\\"')
path = gsub(path, "$", "") path = gsub(path, "$", "")
return path
end
files.exists = function(path)
if not (io.popen("ls " .. tostring(sanitize(path))):close()) then
return true
end
if package.nomsupath then
for nomsupath in package.nomsupath:gmatch("[^;]+") do
if not (io.popen("ls " .. tostring(nomsupath) .. "/" .. tostring(sanitize(path))):close()) then
return true
end
end
end
return false
end
files.walk = function(path)
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin' then
return iterate_single, path
end
path = sanitize(path)
return coroutine.wrap(function() return coroutine.wrap(function()
local f = io.popen('find -L "' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"') local f = io.popen('find -L "' .. path .. '" -not -path "*/\\.*" -type f -name "*.nom"')
local found = false local found = false

View File

@ -35,26 +35,35 @@ iterate_single = (item, prev) -> if item == prev then nil else item
iterate_single = (item, prev) -> if item == prev then nil else item iterate_single = (item, prev) -> if item == prev then nil else item
ok, lfs = pcall(require, "lfs") ok, lfs = pcall(require, "lfs")
if ok if ok
raw_file_exists = (filename)->
mode = lfs.attributes(filename, 'mode')
return if mode == 'file' or mode == 'directory' then true else false
files.exists = (path)->
return true if path == 'stdin' or raw_file_exists(path)
if package.nomsupath
for nomsupath in package.nomsupath\gmatch("[^;]+")
return true if raw_file_exists(nomsupath.."/"..path)
return false
-- Return 'true' if any files or directories are found, otherwise 'false', and yield every filename
browse = (filename)->
file_type = lfs.attributes(filename, 'mode')
if file_type == 'file'
if match(filename, "%.nom$") or match(filename, "%.lua$")
coroutine.yield filename
return true
elseif file_type == 'directory'
for subfile in lfs.dir(filename)
-- Only include .nom files unless directly specified
unless subfile == "." or subfile == ".." or not subfile\match("%.nom$")
browse(filename.."/"..subfile)
return true
elseif file_type == 'char device'
coroutine.yield(filename)
return true
return false
files.walk = (path)-> files.walk = (path)->
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin' if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin'
return iterate_single, path return iterate_single, path
-- Return 'true' if any files or directories are found, otherwise 'false'
browse = (filename)->
file_type = lfs.attributes(filename, 'mode')
if file_type == 'file'
if match(filename, "%.nom$") or match(filename, "%.lua$")
coroutine.yield filename
return true
elseif file_type == 'directory'
for subfile in lfs.dir(filename)
-- Only include .nom files unless directly specified
unless subfile == "." or subfile == ".." or not subfile\match("%.nom$")
browse(filename.."/"..subfile)
return true
elseif file_type == 'char device'
coroutine.yield(filename)
return true
return false
return coroutine.wrap -> return coroutine.wrap ->
if not browse(path) and package.nomsupath if not browse(path) and package.nomsupath
for nomsupath in package.nomsupath\gmatch("[^;]+") for nomsupath in package.nomsupath\gmatch("[^;]+")
@ -63,16 +72,26 @@ if ok
else else
if io.popen('find . -maxdepth 0')\close! if io.popen('find . -maxdepth 0')\close!
error "Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: http://keplerproject.github.io/luafilesystem/ or `luarocks install luafilesystem`)", 0 error "Could not find 'luafilesystem' module and couldn't run system command `find` (this might happen on Windows). Please install `luafilesystem` (which can be found at: http://keplerproject.github.io/luafilesystem/ or `luarocks install luafilesystem`)", 0
files.walk = (path)-> -- TODO: improve sanitization
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin' sanitize = (path)->
return iterate_single, path
-- Sanitize path
-- TODO: improve sanitization
path = gsub(path,"\\","\\\\") path = gsub(path,"\\","\\\\")
path = gsub(path,"`","") path = gsub(path,"`","")
path = gsub(path,'"','\\"') path = gsub(path,'"','\\"')
path = gsub(path,"$","") path = gsub(path,"$","")
return path
files.exists = (path)->
return true unless io.popen("ls #{sanitize(path)}")\close!
if package.nomsupath
for nomsupath in package.nomsupath\gmatch("[^;]+")
return true unless io.popen("ls #{nomsupath}/#{sanitize(path)}")\close!
return false
files.walk = (path)->
if match(path, "%.nom$") or match(path, "%.lua$") or path == 'stdin'
return iterate_single, path
path = sanitize(path)
return coroutine.wrap -> return coroutine.wrap ->
f = io.popen('find -L "'..path..'" -not -path "*/\\.*" -type f -name "*.nom"') f = io.popen('find -L "'..path..'" -not -path "*/\\.*" -type f -name "*.nom"')
found = false found = false

255
nomsu.lua
View File

@ -53,20 +53,18 @@ end
local EXIT_SUCCESS, EXIT_FAILURE = 0, 1 local EXIT_SUCCESS, EXIT_FAILURE = 0, 1
local usage = [=[Nomsu Compiler local usage = [=[Nomsu Compiler
Usage: (lua nomsu.lua | moon nomsu.moon) [-V version] [-i] [-O] [-v] [-c] [-f] [-s] [--help] [--version] [-p print_file] file1 file2... [-- nomsu args...] Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [-O] [-v] [-c] [-s] [-I file] [--help | -h] [--version] [file [nomsu args...]]
OPTIONS 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).
-O Run the compiler in optimized mode (use precompiled .lua versions of Nomsu files, when available) -v Verbose: print compiled lua code.
-v Verbose: print compiled lua code -c Compile the input files into a .lua files.
-c Compile .nom files into .lua files -s Check the input files for syntax errors.
-f Auto-format the given Nomsu file and print the result. -I <file> Add an additional input file or directory.
-s Check the program for syntax errors.
-h/--help Print this message. -h/--help Print this message.
--version Print the version number and exit. --version Print the version number and exit.
-V specify which Nomsu version is desired -V specify which Nomsu version is desired.
-p <file> Print to the specified file instead of stdout. <file> The Nomsu file to run (can be "-" to use stdin).
<input> Input file can be "-" to use stdin.
]=] ]=]
local ok, _ = pcall(function() local ok, _ = pcall(function()
lpeg = require('lpeg') lpeg = require('lpeg')
@ -88,22 +86,24 @@ repr = require("utils").repr
if not arg or debug.getinfo(2).func == require then if not arg or debug.getinfo(2).func == require then
return NomsuCompiler return NomsuCompiler
end end
local parser = re.compile([[ args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !. local file_queue = { }
local parser = re.compile([[ args <- {| (flag ";")* (({~ file ~} -> add_file) ";")? {:nomsu_args: {| ({[^;]*} ";")* |} :} ";"? |} !.
flag <- flag <-
{:interactive: ("-i" -> true) :} {:optimized: ("-O" -> true) :}
/ {:optimized: ("-O" -> true) :} / ("-I" (";")? ({~ file ~} -> add_file))
/ {:format: ("-f" -> true) :} / ({:check_syntax: ("-s" -> true):})
/ {:syntax: ("-s" -> true) :} / ({:compile: ("-c" -> true):})
/ {:print_file: "-p" ";" {file} :}
/ {:compile: ("-c" -> true) :}
/ {:verbose: ("-v" -> true) :} / {:verbose: ("-v" -> true) :}
/ {:help: (("-h" / "--help") -> true) :} / {:help: (("-h" / "--help") -> true) :}
/ {:version: ("--version" -> true) :} / {:version: ("--version" -> true) :}
/ {:requested_version: "-V" ((";")? {([0-9.])+})? :} / {:requested_version: "-V" ((";")? {([0-9.])+})? :}
file <- "-" / [^;]+ file <- ("-" -> "stdin") / {[^;]+}
]], { ]], {
["true"] = function() ["true"] = function()
return true return true
end,
add_file = function(f)
return table.insert(file_queue, f)
end end
}) })
local arg_string = table.concat(arg, ";") .. ";" local arg_string = table.concat(arg, ";") .. ";"
@ -134,160 +134,106 @@ FILE_CACHE = setmetatable({ }, {
}) })
local run local run
run = function() run = function()
for i, input in ipairs(args.inputs) do
if input == "-" then
args.inputs[i] = 'stdin'
end
end
if #args.inputs == 0 and not args.interactive then
args.inputs = {
"core"
}
args.interactive = true
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')
else
print_file = io.stdout
end
if print_file == nil then
nomsu.print = function() end
elseif print_file ~= io.stdout then
nomsu.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
local input_files = { } local input_files = { }
local to_run = { } for _index_0 = 1, #file_queue do
local _list_0 = args.inputs local f = file_queue[_index_0]
for _index_0 = 1, #_list_0 do if not (files.exists(f)) then
local _continue_0 = false error("Could not find: " .. tostring(f))
repeat end
local input = _list_0[_index_0] for filename in files.walk(f) do
if input == 'stdin' then input_files[filename] = true
input_files[#input_files + 1] = 'stdin'
to_run['stdin'] = true
_continue_0 = true
break
end
local found = false
for f in files.walk(input) do
input_files[#input_files + 1] = f
to_run[f] = true
found = true
end
if not found then
error("Could not find: " .. tostring(input))
end
_continue_0 = true
until true
if not _continue_0 then
break
end end
end end
nomsu.can_optimize = function(f) nomsu.can_optimize = function(f)
if not (args.optimized) then if not (args.optimized) then
return false return false
end end
if to_run[f] then if args.compile and input_files[f] then
return false return false
end end
return true return true
end end
local get_file_and_source
get_file_and_source = function(filename)
local file, source
if filename == 'stdin' then
file = io.read("*a")
files.spoof('stdin', file)
source = Source('stdin', 1, #file)
elseif filename:match("%.nom$") then
file = files.read(filename)
if not file then
error("File does not exist: " .. tostring(filename), 0)
end
source = Source(filename, 1, #file)
else
return nil
end
source = Source(filename, 1, #file)
return file, source
end
local run_file
run_file = function(filename, lua_handler)
if lua_handler == nil then
lua_handler = nil
end
local file, source = get_file_and_source(filename)
if not (file) then
return nil
end
local tree = nomsu:parse(file, source)
if tree then
if tree.type ~= "FileChunks" then
tree = {
tree
}
end
for _index_0 = 1, #tree do
local chunk = tree[_index_0]
local lua = nomsu:compile(chunk):as_statements("return ")
lua:declare_locals()
lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
if lua_handler then
lua_handler(tostring(lua))
end
nomsu:run_lua(lua)
end
end
end
local parse_errs = { } local parse_errs = { }
local _list_1 = args.inputs for _index_0 = 1, #file_queue do
for _index_0 = 1, #_list_1 do local f = file_queue[_index_0]
local arg = _list_1[_index_0] for filename in files.walk(f) do
for filename in files.walk(arg) do
local _continue_0 = false local _continue_0 = false
repeat repeat
local file, source if args.check_syntax then
if filename == 'stdin' then local file, source = get_file_and_source(filename)
file = io.read("*a") if not (file) then
files.spoof('stdin', file) _continue_0 = true
source = Source('stdin', 1, #file) break
elseif filename:match("%.nom$") then
file = files.read(filename)
if not file then
error("File does not exist: " .. tostring(filename), 0)
end end
source = Source(filename, 1, #file) nomsu:parse(file, source)
else print("Parse succeeded: " .. tostring(filename))
_continue_0 = true
break
end end
source = Source(filename, 1, #file)
local output
if args.compile then if args.compile then
output = io.open(filename:gsub("%.nom$", ".lua"), "w") local output
else if filename == 'stdin' then
output = nil output = io.output()
end
if args.syntax then
local err
ok, err = pcall(nomsu.parse, nomsu, file, source)
if not ok then
table.insert(parse_errs, err)
elseif print_file then
print_file:write("Parse succeeded: " .. tostring(filename) .. "\n")
print_file:flush()
end
_continue_0 = true
break
end
local tree = nomsu:parse(file, source)
if args.format then
local formatted = tree and tostring(nomsu:tree_to_nomsu(tree)) or ""
if print_file then
print_file:write(formatted, "\n")
print_file:flush()
end
_continue_0 = true
break
end
if tree then
if tree.type == "FileChunks" then
for _index_1 = 1, #tree do
local chunk = tree[_index_1]
local lua = nomsu:compile(chunk):as_statements("return ")
lua:declare_locals()
lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
if args.compile then
output:write(tostring(lua), "\n")
end
if args.verbose then
print(tostring(lua))
end
nomsu:run_lua(lua)
end
else else
local lua = nomsu:compile(tree):as_statements("return ") output = io.open(filename:gsub("%.nom$", ".lua"), "w")
lua:declare_locals()
lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
if args.compile then
output:write(tostring(lua), "\n")
end
if args.verbose then
print(tostring(lua))
end
nomsu:run_lua(lua)
end end
end run_file(filename, function(lua)
if args.compile then output:write(tostring(lua), "\n")
if args.verbose then
return print(tostring(lua))
end
end)
print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua"))) print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua")))
output:close() output:close()
end end
if not args.check_syntax and not args.compile then
run_file(filename, (args.verbose and print or nil))
end
_continue_0 = true _continue_0 = true
until true until true
if not _continue_0 then if not _continue_0 then
@ -295,14 +241,7 @@ run = function()
end end
end end
end end
if #parse_errs > 0 then if #file_queue == 0 then
io.stderr:write(table.concat(parse_errs, "\n\n"))
io.stderr:flush()
os.exit(EXIT_FAILURE)
elseif args.syntax then
os.exit(EXIT_SUCCESS)
end
if args.interactive then
nomsu:run([[use "core" nomsu:run([[use "core"
use "lib/consolecolor.nom" use "lib/consolecolor.nom"
action [quit, exit]: lua> "os.exit(0)" action [quit, exit]: lua> "os.exit(0)"

View File

@ -11,20 +11,18 @@ EXIT_SUCCESS, EXIT_FAILURE = 0, 1
usage = [=[ usage = [=[
Nomsu Compiler Nomsu Compiler
Usage: (lua nomsu.lua | moon nomsu.moon) [-V version] [-i] [-O] [-v] [-c] [-f] [-s] [--help] [--version] [-p print_file] file1 file2... [-- nomsu args...] Usage: (nomsu | lua nomsu.lua | moon nomsu.moon) [-V version] [-O] [-v] [-c] [-s] [-I file] [--help | -h] [--version] [file [nomsu args...]]
OPTIONS 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).
-O Run the compiler in optimized mode (use precompiled .lua versions of Nomsu files, when available) -v Verbose: print compiled lua code.
-v Verbose: print compiled lua code -c Compile the input files into a .lua files.
-c Compile .nom files into .lua files -s Check the input files for syntax errors.
-f Auto-format the given Nomsu file and print the result. -I <file> Add an additional input file or directory.
-s Check the program for syntax errors.
-h/--help Print this message. -h/--help Print this message.
--version Print the version number and exit. --version Print the version number and exit.
-V specify which Nomsu version is desired -V specify which Nomsu version is desired.
-p <file> Print to the specified file instead of stdout. <file> The Nomsu file to run (can be "-" to use stdin).
<input> Input file can be "-" to use stdin.
]=] ]=]
ok, _ = pcall -> ok, _ = pcall ->
@ -43,21 +41,23 @@ NomsuCompiler = require "nomsu_compiler"
if not arg or debug.getinfo(2).func == require if not arg or debug.getinfo(2).func == require
return NomsuCompiler return NomsuCompiler
file_queue = {}
parser = re.compile([[ parser = re.compile([[
args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !. args <- {| (flag ";")* (({~ file ~} -> add_file) ";")? {:nomsu_args: {| ({[^;]*} ";")* |} :} ";"? |} !.
flag <- flag <-
{:interactive: ("-i" -> true) :} {:optimized: ("-O" -> true) :}
/ {:optimized: ("-O" -> true) :} / ("-I" (";")? ({~ file ~} -> add_file))
/ {:format: ("-f" -> true) :} / ({:check_syntax: ("-s" -> true):})
/ {:syntax: ("-s" -> true) :} / ({:compile: ("-c" -> true):})
/ {:print_file: "-p" ";" {file} :}
/ {:compile: ("-c" -> true) :}
/ {:verbose: ("-v" -> true) :} / {:verbose: ("-v" -> true) :}
/ {:help: (("-h" / "--help") -> true) :} / {:help: (("-h" / "--help") -> true) :}
/ {:version: ("--version" -> true) :} / {:version: ("--version" -> true) :}
/ {:requested_version: "-V" ((";")? {([0-9.])+})? :} / {:requested_version: "-V" ((";")? {([0-9.])+})? :}
file <- "-" / [^;]+ file <- ("-" -> "stdin") / {[^;]+}
]], {true: -> true}) ]], {
true: -> true
add_file: (f)-> table.insert(file_queue, f)
})
arg_string = table.concat(arg, ";")..";" arg_string = table.concat(arg, ";")..";"
args = parser\match(arg_string) args = parser\match(arg_string)
if not args or args.help if not args or args.help
@ -87,123 +87,76 @@ FILE_CACHE = setmetatable {}, {
} }
run = -> run = ->
for i,input in ipairs args.inputs
if input == "-" then args.inputs[i] = 'stdin'
if #args.inputs == 0 and not args.interactive
args.inputs = {"core"}
args.interactive = true
print_file = if args.print_file == "-" then io.stdout
elseif args.print_file then io.open(args.print_file, 'w')
else io.stdout
if print_file == nil
nomsu.print = ->
elseif print_file != io.stdout
nomsu.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!
input_files = {} input_files = {}
to_run = {} for f in *file_queue
for input in *args.inputs unless files.exists(f)
if input == 'stdin' error("Could not find: #{f}")
input_files[#input_files+1] = 'stdin' for filename in files.walk(f)
to_run['stdin'] = true input_files[filename] = true
continue
found = false
for f in files.walk(input)
input_files[#input_files+1] = f
to_run[f] = true
found = true
if not found
error("Could not find: #{input}")
nomsu.can_optimize = (f)-> nomsu.can_optimize = (f)->
return false unless args.optimized return false unless args.optimized
return false if to_run[f] return false if args.compile and input_files[f]
return true return true
get_file_and_source = (filename)->
local file, source
if filename == 'stdin'
file = io.read("*a")
files.spoof('stdin', file)
source = Source('stdin', 1, #file)
elseif filename\match("%.nom$")
file = files.read(filename)
if not file
error("File does not exist: #{filename}", 0)
source = Source(filename, 1, #file)
else return nil
source = Source(filename,1,#file)
return file, source
run_file = (filename, lua_handler=nil)->
file, source = get_file_and_source(filename)
return nil unless file
tree = nomsu\parse(file, source)
if tree
if tree.type != "FileChunks"
tree = {tree}
-- Each chunk's compilation is affected by the code in the previous chunks
-- (typically), so each chunk needs to compile and run before the next one
-- compiles.
for chunk in *tree
lua = nomsu\compile(chunk)\as_statements("return ")
lua\declare_locals!
lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
if lua_handler then lua_handler(tostring(lua))
nomsu\run_lua(lua)
parse_errs = {} parse_errs = {}
for arg in *args.inputs for f in *file_queue
for filename in files.walk(arg) for filename in files.walk(f)
local file, source if args.check_syntax
if filename == 'stdin' -- Check syntax
file = io.read("*a") file, source = get_file_and_source(filename)
files.spoof('stdin', file) continue unless file
source = Source('stdin', 1, #file) nomsu\parse(file, source)
elseif filename\match("%.nom$") print("Parse succeeded: #{filename}")
file = files.read(filename)
if not file
error("File does not exist: #{filename}", 0)
source = Source(filename, 1, #file)
else continue
source = Source(filename,1,#file)
output = if args.compile then io.open(filename\gsub("%.nom$", ".lua"), "w") else nil
if args.syntax
-- Check syntax:
ok,err = pcall nomsu.parse, nomsu, file, source
if not ok
table.insert parse_errs, err
elseif print_file
print_file\write("Parse succeeded: #{filename}\n")
print_file\flush!
continue
tree = nomsu\parse(file, source)
if args.format
-- Auto-format
formatted = tree and tostring(nomsu\tree_to_nomsu(tree)) or ""
if print_file
print_file\write(formatted, "\n")
print_file\flush!
continue
if tree
if tree.type == "FileChunks"
-- Each chunk's compilation is affected by the code in the previous chunks
-- (typically), so each chunk needs to compile and run before the next one
-- compiles.
for chunk in *tree
lua = nomsu\compile(chunk)\as_statements("return ")
lua\declare_locals!
lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
if args.compile
output\write(tostring(lua), "\n")
if args.verbose
print(tostring(lua))
nomsu\run_lua(lua)
else
lua = nomsu\compile(tree)\as_statements("return ")
lua\declare_locals!
lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
if args.compile
output\write(tostring(lua), "\n")
if args.verbose
print(tostring(lua))
nomsu\run_lua(lua)
if args.compile if args.compile
-- Compile .nom files into .lua
output = if filename == 'stdin' then io.output()
else io.open(filename\gsub("%.nom$", ".lua"), "w")
run_file filename, (lua)->
output\write(tostring(lua), "\n")
if args.verbose then print(tostring(lua))
print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua")) print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua"))
output\close! output\close!
if #parse_errs > 0 if not args.check_syntax and not args.compile
io.stderr\write table.concat(parse_errs, "\n\n") -- Just run the file
io.stderr\flush! run_file filename, (args.verbose and print or nil)
os.exit(EXIT_FAILURE)
elseif args.syntax
os.exit(EXIT_SUCCESS)
if args.interactive if #file_queue == 0
-- REPL -- Run in interactive mode (REPL)
nomsu\run [[ nomsu\run [[
use "core" use "core"
use "lib/consolecolor.nom" use "lib/consolecolor.nom"

View File

@ -150,7 +150,7 @@ local NomsuCompiler = setmetatable({ }, {
end end
}) })
do do
NomsuCompiler.NOMSU_COMPILER_VERSION = 2 NomsuCompiler.NOMSU_COMPILER_VERSION = 3
NomsuCompiler.NOMSU_SYNTAX_VERSION = Parser.version NomsuCompiler.NOMSU_SYNTAX_VERSION = Parser.version
NomsuCompiler._ENV = NomsuCompiler NomsuCompiler._ENV = NomsuCompiler
NomsuCompiler.nomsu = NomsuCompiler NomsuCompiler.nomsu = NomsuCompiler

View File

@ -92,7 +92,7 @@ dict = (t)-> setmetatable(t, _dict_mt)
MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
NomsuCompiler = setmetatable({}, {__index: (k)=> if _self = rawget(@, "self") then _self[k] else nil}) NomsuCompiler = setmetatable({}, {__index: (k)=> if _self = rawget(@, "self") then _self[k] else nil})
with NomsuCompiler with NomsuCompiler
.NOMSU_COMPILER_VERSION = 2 .NOMSU_COMPILER_VERSION = 3
.NOMSU_SYNTAX_VERSION = Parser.version .NOMSU_SYNTAX_VERSION = Parser.version
._ENV = NomsuCompiler ._ENV = NomsuCompiler
.nomsu = NomsuCompiler .nomsu = NomsuCompiler