all repos — min @ 9057f45c7c8ce39d8b2760c430a22c6cdbe6ac37

A small but practical concatenative programming language.

Updates for asset management.
h3rald h3rald@h3rald.com
Tue, 22 Dec 2020 08:46:46 +0100
commit

9057f45c7c8ce39d8b2760c430a22c6cdbe6ac37

parent

8ad3286035c2d9707f5a4260d25f879624946e1c

5 files changed, 223 insertions(+), 184 deletions(-)

jump to
M core/env.nimcore/env.nim

@@ -23,5 +23,3 @@ EDITOR = initEditor(historyFile = MINHISTORY)

var MINCOMPILED* {.threadvar.}: bool MINCOMPILED = false -var BUNDLEDASSETS* {.threadvar.}: bool -BUNDLEDASSETS = false
M core/interpreter.nimcore/interpreter.nim

@@ -10,6 +10,7 @@ minilogger

else: import os, + base64, logging import baseutils,

@@ -22,8 +23,10 @@ MinTrappedException* = ref object of CatchableError

MinRuntimeError* = ref object of CatchableError data*: MinValue +var ASSETPATH* {.threadvar.}: string +ASSETPATH = "" var COMPILEDMINFILES* {.threadvar.}: CritBitTree[MinOperatorProc] -var COMPILEDASSETS* {.threadvar.}: CritBitTree[MinOperatorProc] +var COMPILEDASSETS* {.threadvar.}: CritBitTree[string] const USER_SYMBOL_REGEX* = "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$"

@@ -356,11 +359,19 @@

proc initCompiledFile*(i: In, files: seq[string]): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = result = newSeq[string](0) result.add "import min" - if files.len > 0: + if files.len > 0 or ASSETPATH != "": result.add "import critbits" + if ASSETPATH != "": + result.add "import base64" result.add "MINCOMPILED = true" result.add "var i = newMinInterpreter(\"$#\")" % i.filename result.add "i.stdLib()" + if ASSETPATH != "": + for f in walkDirRec(ASSETPATH): + logging.notice("- Including: $#" % f) + let ef = f.readFile.encode + let asset = "COMPILEDASSETS[\"$#\"] = \"$#\".decode" % [f, ef] + result.add asset proc eval*(i: In, s: string, name="<eval>", parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}= var i2 = i.copy(name)
M lib/min_io.nimlib/min_io.nim

@@ -1,176 +1,184 @@

-import - strutils, - logging, - critbits, - terminal -import - ../packages/nimline/nimline, - ../packages/nim-sgregex/sgregex, - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils - -var ORIGKEYMAP {.threadvar.}: CritBitTree[KeyCallback] -for key, value in KEYMAP.pairs: - ORIGKEYMAP[key] = value - -proc io_module*(i: In) = - let def = i.define() - - def.symbol("clear") do (i: In): - stdout.eraseScreen - stdout.setCursorPos(0, 0) - - def.symbol("unmapkey") do (i: In): - let vals = i.expect("'sym") - let key = vals[0].getString.toLowerAscii - if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): - raiseInvalid("Unrecognized key: " & key) - if KEYMAP.hasKey(key): - if ORIGKEYMAP.hasKey(key): - KEYMAP[key] = ORIGKEYMAP[key] - else: - KEYMAP.excl(key) - - def.symbol("mapkey") do (i: In): - let vals = i.expect("'sym", "quot") - let key = vals[0].getString.toLowerAscii - var q = vals[1] - if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): - raiseInvalid("Unrecognized key: " & key) - var ic = i.copy(i.filename) - KEYMAP[key] = proc (ed: var LineEditor) {.gcsafe.} = - ic.apply(q) - - def.symbol("newline") do (i: In): - echo "" - - def.symbol("notice") do (i: In): - let a = i.peek - notice $$a - - def.symbol("info") do (i: In): - let a = i.peek - info $$a - - def.symbol("error") do (i: In): - let a = i.peek - error $$a - - def.symbol("warn") do (i: In): - let a = i.peek - warn $$a - - def.symbol("debug") do (i: In): - let a = i.peek - debug $$a - - def.symbol("fatal") do (i: In): - let a = i.peek - fatal $$a - quit(100) - - def.symbol("column-print") do (i: In): - let vals = i.expect("int", "quot") - let n = vals[0] - let q = vals[1] - var c = 0 - for s in q.qVal: - c.inc - stdout.write $$s & spaces(max(0, 15 - ($$s).len)) - if c mod n.intVal == 0: - echo "" - echo "" - - def.symbol("getchr") do (i: In): - i.push getchr().newVal - - def.symbol("putchr") do (i: In): - let ch = i.expect("string") - if ch[0].getString.len != 1: - raiseInvalid("Symbol putch requires a string containing a single character.") - putchr(ch[0].getString[0].cint) - - def.symbol("password") do (i: In) {.gcsafe.}: - var ed = initEditor() - i.push ed.password("Enter Password: ").newVal - - def.symbol("ask") do (i: In) {.gcsafe.}: - var ed = initEditor() - let vals = i.expect("string") - let s = vals[0] - i.push ed.readLine(s.getString & ": ").newVal - - def.symbol("confirm") do (i: In) {.gcsafe.}: - var ed = initEditor() - let vals = i.expect("string") - let s = vals[0] - proc confirm(): bool = - let answer = ed.readLine(s.getString & " [yes/no]: ") - if answer.match("^y(es)?$", "i"): - return true - elif answer.match("^no?$", "i"): - return false - else: - stdout.write "Invalid answer. Please enter 'yes' or 'no': " - return confirm() - i.push confirm().newVal - - def.symbol("choose") do (i: In) {.gcsafe.}: - var ed = initEditor() - let vals = i.expect("'sym", "quot") - let s = vals[0] - var q = vals[1] - if q.qVal.len <= 0: - raiseInvalid("No choices to display") - stdout.writeLine(s.getString) - proc choose(): int = - var c = 0 - for item in q.qVal: - if not item.isQuotation or not item.qVal.len == 2 or not item.qVal[0].isString or not item.qVal[1].isQuotation: - raiseInvalid("Each item of the quotation must be a quotation containing a string and a quotation") - c.inc - echo "$1 - $2" % [$c, item.qVal[0].getString] - let answer = ed.readLine("Enter your choice ($1 - $2): " % ["1", $c]) - var choice: int - try: - choice = answer.parseInt - except: - choice = 0 - if choice <= 0 or choice > c: - echo "Invalid choice." - return choose() - else: - return choice - let choice = choose() - i.dequote(q.qVal[choice-1].qVal[1]) - - def.symbol("print") do (i: In): - let a = i.peek - a.print - - def.symbol("print!") do (i: In): - i.pop.print - - def.symbol("fread") do (i: In): - let vals = i.expect("string") - let a = vals[0] - i.push newVal(a.strVal.readFile) - - def.symbol("fwrite") do (i: In): - let vals = i.expect("string", "string") - let a = vals[0] - let b = vals[1] - a.strVal.writeFile(b.strVal) - - def.symbol("fappend") do (i: In): - let vals = i.expect("string", "string") - let a = vals[0] - let b = vals[1] - var f:File - discard f.open(a.strVal, fmAppend) - f.write(b.strVal) - f.close() - - def.finalize("io") +import + strutils, + logging, + critbits, + terminal +import + ../packages/nimline/nimline, + ../packages/nim-sgregex/sgregex, + ../core/parser, + ../core/value, + ../core/env, + ../core/interpreter, + ../core/utils + +var ORIGKEYMAP {.threadvar.}: CritBitTree[KeyCallback] +for key, value in KEYMAP.pairs: + ORIGKEYMAP[key] = value + +proc io_module*(i: In) = + let def = i.define() + + def.symbol("clear") do (i: In): + stdout.eraseScreen + stdout.setCursorPos(0, 0) + + def.symbol("unmapkey") do (i: In): + let vals = i.expect("'sym") + let key = vals[0].getString.toLowerAscii + if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): + raiseInvalid("Unrecognized key: " & key) + if KEYMAP.hasKey(key): + if ORIGKEYMAP.hasKey(key): + KEYMAP[key] = ORIGKEYMAP[key] + else: + KEYMAP.excl(key) + + def.symbol("mapkey") do (i: In): + let vals = i.expect("'sym", "quot") + let key = vals[0].getString.toLowerAscii + var q = vals[1] + if not KEYNAMES.contains(key) and not KEYSEQS.contains(key): + raiseInvalid("Unrecognized key: " & key) + var ic = i.copy(i.filename) + KEYMAP[key] = proc (ed: var LineEditor) {.gcsafe.} = + ic.apply(q) + + def.symbol("newline") do (i: In): + echo "" + + def.symbol("notice") do (i: In): + let a = i.peek + notice $$a + + def.symbol("info") do (i: In): + let a = i.peek + info $$a + + def.symbol("error") do (i: In): + let a = i.peek + error $$a + + def.symbol("warn") do (i: In): + let a = i.peek + warn $$a + + def.symbol("debug") do (i: In): + let a = i.peek + debug $$a + + def.symbol("fatal") do (i: In): + let a = i.peek + fatal $$a + quit(100) + + def.symbol("column-print") do (i: In): + let vals = i.expect("int", "quot") + let n = vals[0] + let q = vals[1] + var c = 0 + for s in q.qVal: + c.inc + stdout.write $$s & spaces(max(0, 15 - ($$s).len)) + if c mod n.intVal == 0: + echo "" + echo "" + + def.symbol("getchr") do (i: In): + i.push getchr().newVal + + def.symbol("putchr") do (i: In): + let ch = i.expect("string") + if ch[0].getString.len != 1: + raiseInvalid("Symbol putch requires a string containing a single character.") + putchr(ch[0].getString[0].cint) + + def.symbol("password") do (i: In) {.gcsafe.}: + var ed = initEditor() + i.push ed.password("Enter Password: ").newVal + + def.symbol("ask") do (i: In) {.gcsafe.}: + var ed = initEditor() + let vals = i.expect("string") + let s = vals[0] + i.push ed.readLine(s.getString & ": ").newVal + + def.symbol("confirm") do (i: In) {.gcsafe.}: + var ed = initEditor() + let vals = i.expect("string") + let s = vals[0] + proc confirm(): bool = + let answer = ed.readLine(s.getString & " [yes/no]: ") + if answer.match("^y(es)?$", "i"): + return true + elif answer.match("^no?$", "i"): + return false + else: + stdout.write "Invalid answer. Please enter 'yes' or 'no': " + return confirm() + i.push confirm().newVal + + def.symbol("choose") do (i: In) {.gcsafe.}: + var ed = initEditor() + let vals = i.expect("'sym", "quot") + let s = vals[0] + var q = vals[1] + if q.qVal.len <= 0: + raiseInvalid("No choices to display") + stdout.writeLine(s.getString) + proc choose(): int = + var c = 0 + for item in q.qVal: + if not item.isQuotation or not item.qVal.len == 2 or not item.qVal[0].isString or not item.qVal[1].isQuotation: + raiseInvalid("Each item of the quotation must be a quotation containing a string and a quotation") + c.inc + echo "$1 - $2" % [$c, item.qVal[0].getString] + let answer = ed.readLine("Enter your choice ($1 - $2): " % ["1", $c]) + var choice: int + try: + choice = answer.parseInt + except: + choice = 0 + if choice <= 0 or choice > c: + echo "Invalid choice." + return choose() + else: + return choice + let choice = choose() + i.dequote(q.qVal[choice-1].qVal[1]) + + def.symbol("print") do (i: In): + let a = i.peek + a.print + + def.symbol("print!") do (i: In): + i.pop.print + + def.symbol("fread") do (i: In): + let vals = i.expect("string") + let file = vals[0].strVal + var contents = "" + if MINCOMPILED: + var compiledFile = strutils.replace(strutils.replace(file, "\\", "/"), "./", "") + if COMPILEDASSETS.hasKey(compiledFile): + contents = COMPILEDASSETS[compiledFile] + if contents == "": + contents = file.readFile + i.push newVal(contents) + + def.symbol("fwrite") do (i: In): + let vals = i.expect("string", "string") + let a = vals[0] + let b = vals[1] + a.strVal.writeFile(b.strVal) + + def.symbol("fappend") do (i: In): + let vals = i.expect("string", "string") + let a = vals[0] + let b = vals[1] + var f:File + discard f.open(a.strVal, fmAppend) + f.write(b.strVal) + f.close() + + def.finalize("io")
M lib/min_sys.nimlib/min_sys.nim

@@ -2,11 +2,13 @@ import

os, osproc, strutils, + critbits, logging when not defined(lite): import sequtils import ../core/parser, + ../core/env, ../core/value, ../core/interpreter, ../core/utils,

@@ -98,13 +100,29 @@ i.push hostCPU.newVal

def.symbol("exists?") do (i: In): let vals = i.expect("'sym") - let f = vals[0] - i.push newVal(f.getString.fileExists or f.getString.dirExists) + let f = vals[0].getString + var found = false + if MINCOMPILED: + let cf = strutils.replace(strutils.replace(f, "\\", "/"), "./", "") + + found = COMPILEDASSETS.hasKey(cf) + if found: + i.push true.newVal + else: + i.push newVal(f.fileExists or f.dirExists) def.symbol("file?") do (i: In): let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.fileExists.newVal + let f = vals[0].getString + var found = false + if MINCOMPILED: + let cf = strutils.replace(strutils.replace(f, "\\", "/"), "./", "") + + found = COMPILEDASSETS.hasKey(cf) + if found: + i.push true.newVal + else: + i.push f.fileExists.newVal def.symbol("dir?") do (i: In): let vals = i.expect("'sym")
M min.nimmin.nim

@@ -413,6 +413,8 @@

Arguments: filename A $exe file to interpret or compile (default: STDIN). Options:$installOpt$uninstallOpt + -a, --asset-path Specify a directory containing the asset files to include in the + compiled executable (if -c is set) -c, --compile Compile the specified file -e, --evaluate Evaluate a $exe program inline -h, --help Print this help$iOpt

@@ -451,6 +453,8 @@ of "compile", "c":

COMPILE = true of "module-path", "m": MODULEPATH = val + of "asset-path", "a": + ASSETPATH = val of "prelude", "p": customPrelude = val of "log", "l":