-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathmin.nim
More file actions
executable file
·329 lines (311 loc) · 9.98 KB
/
min.nim
File metadata and controls
executable file
·329 lines (311 loc) · 9.98 KB
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
import
std/[streams,
strutils,
sequtils,
times,
os,
logging]
import
minpkg/core/[niftylogger,
baseutils,
env,
parser,
value,
interpreter,
stdlib,
shell,
utils,
mmm]
import
minpkg/lib/[
min_global
]
export
env,
parser,
interpreter,
utils,
value,
shell,
stdlib,
min_global,
niftylogger
var NIMOPTIONS* = ""
var MINMODULES* = newSeq[string](0)
var MMM*: MinModuleManager
if logging.getHandlers().len == 0:
newNiftyLogger().addHandler()
proc interpret*(i: In, s: Stream) =
i.stdLib()
i.open(s, i.filename)
discard i.parser.getToken()
try:
i.interpret()
except CatchableError:
discard
i.close()
proc minFile*(fn: string, op = "interpret", main = true): seq[
string] {.discardable.}
proc compile*(i: In, s: Stream, main = true): seq[string] =
if "nim".findExe == "":
logging.error "Nim compiler not found, unable to compile."
quit(7)
result = newSeq[string](0)
i.open(s, i.filename)
discard i.parser.getToken()
try:
MINCOMPILED = true
let dotindex = i.filename.rfind(".")
let nimFile = i.filename[0..dotindex-1] & ".nim"
if main:
logging.notice("Generating $#..." % nimFile)
result = i.initCompiledFile(MINMODULES)
for m in MINMODULES:
let f = m.replace("\\", "/")
result.add "### $#" % f
logging.notice("- Including: $#" % f)
result = result.concat(minFile(f, "compile", main = false))
result.add "### $# (main)" % i.filename
result = result.concat(i.compileFile(main))
writeFile(nimFile, result.join("\n"))
let cmd = "nim c --threadAnalysis:off --mm:refc $#$#" % [NIMOPTIONS&" ", nimFile]
logging.notice("Calling Nim compiler:")
logging.notice(cmd)
discard execShellCmd(cmd)
else:
result = result.concat(i.compileFile(main))
except CatchableError:
discard
i.close()
proc minStream(s: Stream, filename: string, op = "interpret", main = true): seq[
string] {.discardable.} =
var i = newMinInterpreter(filename = filename)
i.pwd = filename.parentDirEx
if op == "interpret":
i.interpret(s)
newSeq[string](0)
else:
i.compile(s, main)
proc minStr*(buffer: string) =
minStream(newStringStream(buffer), "input")
proc minFile*(fn: string, op = "interpret", main = true): seq[
string] {.discardable.} =
var fileLines = newSeq[string](0)
var contents = ""
try:
fileLines = fn.readFile().splitLines()
except CatchableError:
logging.fatal("Cannot read from file: " & fn)
quit(3)
if fileLines[0].len >= 2 and fileLines[0][0..1] == "#!":
contents = ";;\n" & fileLines[1..fileLines.len-1].join("\n")
else:
contents = fileLines.join("\n")
minStream(newStringStream(contents), fn, op, main)
when isMainModule:
import
terminal,
parseopt,
minpkg/core/meta
var REPL = false
var MODULEPATH = ""
var GLOBAL = false
proc resolveFile(file: string): string =
if (file.endsWith(".min") or file.endsWith(".mn")) and fileExists(file):
return file
elif fileExists(file&".min"):
return file&".min"
elif fileExists(file&".mn"):
return file&".mn"
return ""
proc executeMmmCmd(cmd: proc (): void) =
try:
MMM.setup()
cmd()
quit(0)
except CatchableError:
error getCurrentExceptionMsg()
debug getCurrentException().getStackTrace()
quit(10)
let usage* = """ $exe v$version - a small but practical concatenative programming language
(c) 2014-$year Fabio Cevasco
Usage:
$exe [options] [filename | command] [...comamand-arguments]
Arguments:
filename A $exe file to interpret or compile
command A command to execute
Commands:
compile <file>.min Compile <file>.min.
eval <string> Evaluate <string> as a min program.
help <symbol|sigil> Print the help contents related to <symbol|sigil>.
init Sets up the current directory as a managed min module.
install [<module>@<version>] Install the specified managed min module or all dependent modules.
uninstall [<module>@<version>] Uninstall the specified managed min module or all dependent modules.
update [<module>@<version>] Update the specified managed min module or all dependent modules.
run <mmm> Executes the main symbol exposed by the specified min managed module,
downloading it and installing it globally if necessary.
search [...terms...] Search for a managed min module matching the specified terms.
list List all managed min modules installed in the local directory or globally.
Options:
-a, --asset-path Specify a directory containing the asset files to include in the
compiled executable (if -c is set)
-d, --dev Enable "development mode" (runtime checks)
-g, --global Execute the specified command (install or uninstall) globally.
-h, --help Print this help
-i, --interactive Start $exe shell (with advanced prompt, default if no file specidied)"
-j, --interactive-simple Start $exe shell (without advanced prompt)
-l, --log Set log level (debug|info|notice|warn|error|fatal)
Default: notice
-m, --module-path Specify a directory containing the .min files to include in the
compiled executable (if -c is set)
-n, --passN Pass options to the nim compiler (if -c is set)
-p, --prelude:<file.min> If specified, it loads <file.min> instead of the default prelude code
-r, --registry:<url> If specified, uses the specified url as MMM registry
-v, —-version Print the program version""" % [
"exe", pkgName,
"version", pkgVersion,
"year", $(now().year)
]
var file = ""
var args = newSeq[string](0)
logging.setLogFilter(logging.lvlNotice)
var p = initOptParser()
for kind, key, val in getopt(p):
case kind:
of cmdArgument:
args.add key
if file == "":
file = key
of cmdLongOption, cmdShortOption:
case key:
of "module-path", "m":
MODULEPATH = val
of "asset-path", "a":
ASSETPATH = val
of "global", "g":
GLOBAL = true
of "prelude", "p":
customPrelude = val
of "dev", "d":
DEV = true
of "log", "l":
var v = val
niftylogger.setLogLevel(v)
of "passN", "n":
NIMOPTIONS = val
of "help", "h":
if file == "":
echo usage
quit(0)
of "version", "v":
if file == "":
echo pkgVersion
quit(0)
of "interactive", "i":
if file == "":
REPL = true
of "interactive-simple", "j":
if file == "":
SIMPLEREPL = true
of "registry", "r":
MMMREGISTRY = val
else:
discard
else:
discard
if MODULEPATH.len > 0:
for f in walkDirRec(MODULEPATH):
if f.endsWith(".min"):
MINMODULES.add f
elif REPL:
minRepl()
quit(0)
if file != "":
var fn = resolveFile(file)
if fn == "":
if file == "compile":
if args.len < 2:
logging.error "No file was specified."
quit(8)
fn = resolveFile(args[1])
if fn == "":
logging.error "File '$#' does not exist." % [args[1]]
quit(9)
minFile fn, "compile"
quit(0)
elif file == "eval":
if args.len < 2:
logging.error "No string to evaluate was specified."
quit(9)
minStr args[1]
quit(0)
elif file == "help":
if args.len < 2:
logging.error "No symbol to lookup was specified."
quit(9)
minStr("\"$#\" help" % [args[1]])
quit(0)
elif file == "init":
executeMmmCmd(proc () = MMM.init())
elif file == "run":
if args.len < 1:
logging.error "No script was specified."
quit(8)
MMM.setup()
var script: string
try:
script = MMM.generateRunScript(args[1])
except CatchableError:
error getCurrentExceptionMsg()
debug getCurrentException().getStackTrace()
quit(10)
minStr(script)
quit(0)
elif file == "install":
if args.len < 2:
executeMmmCmd(proc () = MMM.install())
if args.len == 2:
executeMmmCmd(proc () = MMM.install(args[1], GLOBAL))
let name = args[1]
let version = args[2]
executeMmmCmd(proc () = MMM.install(name, version, GLOBAL))
elif file == "uninstall":
if args.len < 2:
executeMmmCmd(proc () = MMM.uninstall())
if args.len == 2:
executeMmmCmd(proc () = MMM.uninstall(args[1], GLOBAL))
let name = args[1]
let version = args[2]
executeMmmCmd(proc () = MMM.uninstall(name, version, GLOBAL))
elif file == "update":
if args.len < 2:
executeMmmCmd(proc () = MMM.update())
if args.len == 2:
executeMmmCmd(proc () = MMM.update(args[1], GLOBAL))
let name = args[1]
let version = args[2]
executeMmmCmd(proc () = MMM.update(name, version, GLOBAL))
elif file == "search":
var str = ""
if args.len > 1:
str = args[1 .. ^1].join(" ")
executeMmmCmd(proc () = MMM.search(str))
elif file == "list":
if GLOBAL:
executeMmmCmd(proc () = MMM.list(MMM.globalDir))
else:
executeMmmCmd(proc () = MMM.list(MMM.localDir))
else:
logging.error "File not found: $#" % [file]
quit(1)
else:
minFile fn, "interpret"
elif SIMPLEREPL:
minSimpleRepl()
quit(0)
else:
if isatty(stdin):
minRepl()
quit(0)
else:
minStream newFileStream(stdin), "stdin", "interpret"