1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
#!/usr/bin/env moon
-- This file contains the command-line Nomsu runner.
usage = [=[
Nomsu Compiler
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.
-p <file> Print to the specified file instead of stdout.
<input> Input file can be "-" to use stdin.
]=]
lpeg = require 'lpeg'
re = require 're'
run_safely = require "error_handling"
NomsuCompiler = require "nomsu_compiler"
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
-- If this file was reached via require(), then just return the Nomsu compiler
if not arg or debug.getinfo(2).func == require
return NomsuCompiler
parser = re.compile([[
args <- {| (flag ";")* {:inputs: {| ({file} ";")* |} :} {:nomsu_args: {| ("--;" ({[^;]*} ";")*)? |} :} ";"? |} !.
flag <-
{:interactive: ("-i" -> true) :}
/ {:optimized: ("-O" -> true) :}
/ {:format: ("-f" -> true) :}
/ {:syntax: ("-s" -> true) :}
/ {:print_file: "-p" ";" {file} :}
/ {:compile: ("-c" -> true) :}
/ {:verbose: ("-v" -> true) :}
/ {:help: (("-h" / "--help") -> true) :}
file <- "-" / [^;]+
]], {true: -> true})
args = table.concat(arg, ";")..";"
args = parser\match(args)
if not args or args.help
print usage
os.exit!
nomsu = NomsuCompiler
nomsu.arg = args.nomsu_args
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
nomsu.skip_precompiled = not args.optimized
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 = {}
to_run = {}
for input in *args.inputs
for f in all_files(input)
input_files[#input_files+1] = f
to_run[f] = true
if args.compile or args.verbose
nomsu.on_compile = (code, from_file)->
return unless 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(tostring(code))
output_file\flush!
print ("Compiled %-25s -> %s")\format(from_file, output_filename)
output_file\close!
parse_errs = {}
for filename in *input_files
if args.syntax
-- Check syntax:
file_contents = io.open(filename)\read('*a')
ok,err = pcall nomsu.parse, nomsu, file_contents, Source(filename, 1, #file_contents)
if not ok
table.insert parse_errs, err
elseif print_file
print_file\write("Parse succeeded: #{filename}\n")
print_file\flush!
elseif args.format
-- Auto-format
file = FILE_CACHE[filename]
if not file
error("File does not exist: #{filename}", 0)
tree = nomsu\parse(file, Source(filename,1,#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(file, Source('stdin',1,#file))
else
nomsu\run_file(filename)
if #parse_errs > 0
io.stderr\write table.concat(parse_errs, "\n\n")
io.stderr\flush!
os.exit(false, true)
elseif args.syntax
os.exit(true, true)
if args.interactive
-- REPL
for repl_line=1,math.huge
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", " ")
table.insert buff, line
io.write(colored.dim colored.yellow ".. ")
if #buff == 0
break -- Exit
buff = table.concat(buff)
FILE_CACHE["REPL#"..repl_line] = buff
err_hand = (error_message)->
print_err_msg error_message
ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source("REPL#"..repl_line, 1, #buff))
if ok and ret != nil
print "= "..repr(ret)
elseif not ok
print_err_msg ret
has_ldt, ldt = pcall(require,'ldt')
if has_ldt
ldt.guard(run)
else
run_safely(run)
|