all repos — min @ 2aaa2ea2b80435986e04fd9240c163798a8605bb

A small but practical concatenative programming language.

Reduced the size of minimin; improved tasks.
h3rald h3rald@h3rald.com
Fri, 11 Dec 2020 19:41:25 +0100
commit

2aaa2ea2b80435986e04fd9240c163798a8605bb

parent

fb6442d8a1555b77aca622c0b3bb467c0349ba3d

A core/baseutils.nim

@@ -0,0 +1,26 @@

+proc reverse*[T](xs: openarray[T]): seq[T] = + result = newSeq[T](xs.len) + for i, x in xs: + result[result.len-i-1] = x + + +when defined(mini): + import + strutils + + proc parentDirEx*(s: string): string = + let fslash = s.rfind("/") + let bslash = s.rfind("\\") + var dirEnd = fslash-1 + if dirEnd < 0: + dirEnd = bslash-1 + if dirEnd < 0: + dirEnd = s.len-1 + if dirEnd < 0: + return s + return s[0..dirEnd] + +else: + import os + proc parentDirEx*(s: string): string = + return s.parentDir
M core/env.nimcore/env.nim

@@ -1,18 +1,19 @@

-import os +when not defined(mini): + import os + var HOME*: string + if defined(windows): + HOME = getenv("USERPROFILE") + if not defined(windows): + HOME = getenv("HOME") -var HOME*: string -if defined(windows): - HOME = getenv("USERPROFILE") -if not defined(windows): - HOME = getenv("HOME") + var MINRC* {.threadvar.}: string + MINRC = HOME / ".minrc" + var MINSYMBOLS* {.threadvar.}: string + MINSYMBOLS = HOME / ".min_symbols" + var MINHISTORY* {.threadvar.}: string + MINHISTORY = HOME / ".min_history" + var MINLIBS* {.threadvar.} : string + MINLIBS = HOME / ".minlibs" -var MINRC* {.threadvar.}: string -MINRC = HOME / ".minrc" -var MINSYMBOLS* {.threadvar.}: string -MINSYMBOLS = HOME / ".min_symbols" -var MINHISTORY* {.threadvar.}: string -MINHISTORY = HOME / ".min_history" -var MINLIBS* {.threadvar.} : string -MINLIBS = HOME / ".minlibs" var MINCOMPILED* {.threadvar.}: bool MINCOMPILED = false
M core/interpreter.nimcore/interpreter.nim

@@ -3,10 +3,16 @@ streams,

strutils, sequtils, critbits, - os, - algorithm, - logging + algorithm +when defined(mini): + import + minilogger +else: + import + os, + logging import + baseutils, value, scope, parser

@@ -61,8 +67,9 @@ i.scope = origScope

proc newMinInterpreter*(filename = "input", pwd = ""): MinInterpreter {.extern:"min_exported_symbol_$1".}= var path = pwd - if not pwd.isAbsolute: - path = joinPath(getCurrentDir(), pwd) + when not defined(mini): #TODO investigate impact + if not pwd.isAbsolute: + path = joinPath(getCurrentDir(), pwd) var stack:MinStack = newSeq[MinValue](0) var trace:MinStack = newSeq[MinValue](0) var stackcopy:MinStack = newSeq[MinValue](0)

@@ -82,11 +89,12 @@ return i

proc copy*(i: MinInterpreter, filename: string): MinInterpreter {.extern:"min_exported_symbol_$1_2".}= var path = filename - if not filename.isAbsolute: - path = joinPath(getCurrentDir(), filename) + when not defined(mini): + if not filename.isAbsolute: + path = joinPath(getCurrentDir(), filename) result = newMinInterpreter() result.filename = filename - result.pwd = path.parentDir + result.pwd = path.parentDirEx result.stack = i.stack result.trace = i.trace result.stackcopy = i.stackcopy

@@ -315,10 +323,11 @@ result = i.rawCompile("")

proc initCompiledFile*(i: In, files: seq[string]): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = result = newSeq[string](0) - result.add "import min, critbits" + result.add "import min" + if files.len > 0: + result.add "import critbits" result.add "MINCOMPILED = true" - if files.len > 0: - result.add "var i = newMinInterpreter(\"$#\")" % i.filename + result.add "var i = newMinInterpreter(\"$#\")" % i.filename result.add "i.stdLib()" proc eval*(i: In, s: string, name="<eval>", parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".}=
A core/minilogger.nim

@@ -0,0 +1,86 @@

+import + strutils + +type + Level* = enum lvlAll, lvlDebug, lvlInfo, lvlNotice, lvlWarn, lvlError, lvlFatal, lvlNone + +const + LevelNames: array[Level, string] = [ "DEBUG", "DEBUG", "INFO", "NOTICE", "WARN", "ERROR", "FATAL", "NONE"] + +var LOGLEVEL* {.threadvar.}: Level +LOGLEVEL = lvlNotice + +proc logPrefix(level: Level): string = + case level: + of lvlDebug: + return ("---") + of lvlInfo: + return ("(i)") + of lvlNotice: + return (" ") + of lvlWarn: + return ("(!)") + of lvlError: + return ("(!)") + of lvlFatal: + return ("(x)") + else: + return (" ") + +proc log*(level: Level; args: varargs[string, `$`]) = + var f = stdout + if level >= LOGLEVEL: + if level >= lvlWarn: + f = stderr + let prefix = level.logPrefix() + f.write(prefix&" ") + f.write(args.join(" ")) + f.write("\n") + if level in {lvlError, lvlFatal}: flushFile(f) + +proc fatal*(args: varargs[string, `$`]) = + log(lvlFatal, args) + +proc error*(args: varargs[string, `$`]) = + log(lvlError, args) + +proc warn*(args: varargs[string, `$`]) = + log(lvlWarn, args) + +proc notice*(args: varargs[string, `$`]) = + log(lvlNotice, args) + +proc info*(args: varargs[string, `$`]) = + log(lvlInfo, args) + +proc debug*(args: varargs[string, `$`]) = + log(lvlDebug, args) + +proc getLogLevel*(): string = + return LevelNames[LOGLEVEL].toLowerAscii + +proc setLogFilter*(lvl: Level) = + LOGLEVEL = lvl + +proc setLogLevel*(val: var string): string {.discardable.} = + var lvl: Level + case val: + of "debug": + lvl = lvlDebug + of "info": + lvl = lvlInfo + of "notice": + lvl = lvlNotice + of "warn": + lvl = lvlWarn + of "error": + lvl = lvlError + of "fatal": + lvl = lvlFatal + of "none": + lvl = lvlNone + else: + val = "warn" + lvl = lvlWarn + LOGLEVEL = lvl + return val
M core/utils.nimcore/utils.nim

@@ -3,15 +3,11 @@ strutils,

critbits, json import + baseutils, parser, value, scope, interpreter - -proc reverse[T](xs: openarray[T]): seq[T] = - result = newSeq[T](xs.len) - for i, x in xs: - result[result.len-i-1] = x # Library methods
M lib/min_io.nimlib/min_io.nim

@@ -18,13 +18,6 @@

def.symbol("newline") do (i: In): echo "" - def.symbol("puts") do (i: In): - let a = i.peek - echo $$a - - def.symbol("puts!") do (i: In): - echo $$i.pop - def.symbol("notice") do (i: In): let a = i.peek notice $$a

@@ -61,10 +54,6 @@ stdout.write $$s & spaces(max(0, 15 - ($$s).len))

if c mod n.intVal == 0: echo "" echo "" - - def.symbol("gets") do (i: In) {.gcsafe.}: - var ed = initEditor() - i.push ed.readLine().newVal def.symbol("getchr") do (i: In): i.push getchr().newVal
M lib/min_lang.nimlib/min_lang.nim

@@ -4,11 +4,17 @@ strutils,

sequtils, parseopt, algorithm, - logging, json -when not defined(mini): +when defined(mini): + import + rdstdin, + ../core/minilogger +else: import os, + logging, + ../packages/niftylogger, + ../packages/nimline/nimline, ../packages/nim-sgregex/sgregex import ../core/env,

@@ -17,7 +23,6 @@ ../core/parser,

../core/value, ../core/interpreter, ../core/utils, - ../packages/niftylogger, ../core/scope proc lang_module*(i: In) =

@@ -25,6 +30,20 @@ let def = i.scope

def.symbol("exit") do (i: In): let vals = i.expect("int") quit(vals[0].intVal.int) + + def.symbol("puts") do (i: In): + let a = i.peek + echo $$a + + def.symbol("puts!") do (i: In): + echo $$i.pop + + def.symbol("gets") do (i: In) {.gcsafe.}: + when defined(mini): + i.push readLineFromStdin("").newVal + else: + var ed = initEditor() + i.push ed.readLine().newVal def.symbol("apply") do (i: In): let vals = i.expect("quot|dict")

@@ -124,10 +143,16 @@ def.symbol("loglevel") do (i: In):

let vals = i.expect("'sym") let s = vals[0] var str = s.getString - echo "Log level: ", setLogLevel(str) + when defined(mini): + echo "Log level: ", minilogger.setLogLevel(str) + else: + echo "Log level: ", niftylogger.setLogLevel(str) def.symbol("loglevel?") do (i: In): - i.push getLogLevel().newVal + when defined(mini): + i.push minilogger.getLogLevel().newVal + else: + i.push niftylogger.getLogLevel().newVal # Language constructs

@@ -227,6 +252,46 @@ let s = vals[0]

i.push i.parse s.strVal when not defined(mini): + # Save/load symbols + + def.symbol("save-symbol") do (i: In) {.gcsafe.}: + let vals = i.expect("'sym") + let s = vals[0] + let sym = s.getString + let op = i.scope.getSymbol(sym) + if op.kind == minProcOp: + raiseInvalid("Symbol '$1' cannot be serialized." % sym) + let json = MINSYMBOLS.readFile.parseJson + json[sym] = i%op.val + MINSYMBOLS.writeFile(json.pretty) + + def.symbol("load-symbol") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + let sym = s.getString + let json = MINSYMBOLS.readFile.parseJson + if not json.hasKey(sym): + raiseUndefined("Symbol '$1' not found." % sym) + let val = i.fromJson(json[sym]) + i.scope.symbols[sym] = MinOperator(kind: minValOp, val: val, quotation: true) + + def.symbol("saved-symbols") do (i: In): + var q = newSeq[MinValue](0) + let json = MINSYMBOLS.readFile.parseJson + for k,v in json.pairs: + q.add k.newVal + i.push q.newVal + + def.symbol("remove-symbol") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + let sym = s.getString + var json = MINSYMBOLS.readFile.parseJson + if not json.hasKey(sym): + raiseUndefined("Symbol '$1' not found." % sym) + json.delete(sym) + MINSYMBOLS.writeFile(json.pretty) + def.symbol("load") do (i: In): let vals = i.expect("'sym") let s = vals[0]

@@ -553,46 +618,6 @@ i.linrec(p, t, r1, r2)

def.symbol("version") do (i: In): i.push pkgVersion.newVal - - # Save/load symbols - - def.symbol("save-symbol") do (i: In) {.gcsafe.}: - let vals = i.expect("'sym") - let s = vals[0] - let sym = s.getString - let op = i.scope.getSymbol(sym) - if op.kind == minProcOp: - raiseInvalid("Symbol '$1' cannot be serialized." % sym) - let json = MINSYMBOLS.readFile.parseJson - json[sym] = i%op.val - MINSYMBOLS.writeFile(json.pretty) - - def.symbol("load-symbol") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - let sym = s.getString - let json = MINSYMBOLS.readFile.parseJson - if not json.hasKey(sym): - raiseUndefined("Symbol '$1' not found." % sym) - let val = i.fromJson(json[sym]) - i.scope.symbols[sym] = MinOperator(kind: minValOp, val: val, quotation: true) - - def.symbol("saved-symbols") do (i: In): - var q = newSeq[MinValue](0) - let json = MINSYMBOLS.readFile.parseJson - for k,v in json.pairs: - q.add k.newVal - i.push q.newVal - - def.symbol("remove-symbol") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - let sym = s.getString - var json = MINSYMBOLS.readFile.parseJson - if not json.hasKey(sym): - raiseUndefined("Symbol '$1' not found." % sym) - json.delete(sym) - MINSYMBOLS.writeFile(json.pretty) def.symbol("seal") do (i: In): let vals = i.expect("'sym")
M min.nimmin.nim

@@ -2,18 +2,21 @@ import

streams, critbits, strutils, - os, - sequtils, - logging + sequtils -when not defined(mini): +when defined(mini): + import + core/minilogger +else: import json, + os, algorithm, - dynlib - + dynlib, + logging, + packages/niftylogger import - packages/niftylogger, + core/baseutils, core/env, core/parser, core/value,

@@ -49,10 +52,13 @@ env,

parser, interpreter, utils, - niftylogger, value, - scope, - min_lang + scope#, + #min_lang +when defined(mini): + export minilogger +else: + export niftylogger const PRELUDE* = "prelude.min".slurp.strip

@@ -60,10 +66,10 @@ var NIMOPTIONS* = ""

var MINMODULES* = newSeq[string](0) var customPrelude = "" -if logging.getHandlers().len == 0: - newNiftyLogger().addHandler() +when not defined(mini): + if logging.getHandlers().len == 0: + newNiftyLogger().addHandler() -when not defined(mini): proc getExecs(): seq[string] = var res = newSeq[string](0) let getFiles = proc(dir: string) =

@@ -76,7 +82,6 @@ getFiles(dir)

res.sort(system.cmp) return res -when not defined(mini): proc getCompletions(ed: LineEditor, symbols: seq[string]): seq[string] = var words = ed.lineText.split(" ") var word: string

@@ -127,14 +132,38 @@ dir = getCurrentDir()

return toSeq(walkDir(dir, true)).filterIt(it.path.toLowerAscii.startsWith(f.toLowerAscii)).mapIt("\"$1" % [it.path.replace("\\", "/")]) return symbols + type + LibProc = proc(i: In) {.nimcall.} + + proc dynLib*(i: In) = + discard MINLIBS.existsOrCreateDir + for library in walkFiles(MINLIBS & "/*"): + var modname = library.splitFile.name + var libfile = library.splitFile.name & library.splitFile.ext + if modname.len > 3 and modname[0..2] == "lib": + modname = modname[3..modname.len-1] + let dll = library.loadLib() + if dll != nil: + let modsym = dll.symAddr(modname) + if modsym != nil: + let modproc = cast[LibProc](dll.symAddr(modname)) + i.modproc() + logging.info("[$1] Dynamic module loaded successfully: $2" % [libfile, modname]) + else: + logging.warn("[$1] Library does not contain symbol $2" % [libfile, modname]) + else: + logging.warn("Unable to load dynamic library: " & libfile) + + proc stdLib*(i: In) = - setLogFilter(lvlNotice) - if not MINSYMBOLS.fileExists: - MINSYMBOLS.writeFile("{}") - if not MINHISTORY.fileExists: - MINHISTORY.writeFile("") - if not MINRC.fileExists: - MINRC.writeFile("") + when not defined(mini): + setLogFilter(logging.lvlNotice) + if not MINSYMBOLS.fileExists: + MINSYMBOLS.writeFile("{}") + if not MINHISTORY.fileExists: + MINHISTORY.writeFile("") + if not MINRC.fileExists: + MINRC.writeFile("") i.lang_module i.stack_module i.seq_module

@@ -158,31 +187,12 @@ else:

try: i.eval customPrelude.readFile, customPrelude except: - warn("Unable to process custom prelude code in $1" % customPrelude) - i.eval MINRC.readFile() - -when not defined(mini): - type - LibProc = proc(i: In) {.nimcall.} - - proc dynLib*(i: In) = - discard MINLIBS.existsOrCreateDir - for library in walkFiles(MINLIBS & "/*"): - var modname = library.splitFile.name - var libfile = library.splitFile.name & library.splitFile.ext - if modname.len > 3 and modname[0..2] == "lib": - modname = modname[3..modname.len-1] - let dll = library.loadLib() - if dll != nil: - let modsym = dll.symAddr(modname) - if modsym != nil: - let modproc = cast[LibProc](dll.symAddr(modname)) - i.modproc() - info("[$1] Dynamic module loaded successfully: $2" % [libfile, modname]) - else: - warn("[$1] Library does not contain symbol $2" % [libfile, modname]) + when defined(mini): + minilogger.warn("Unable to process custom prelude code in $1" % customPrelude) else: - warn("Unable to load dynamic library: " & libfile) + logging.warn("Unable to process custom prelude code in $1" % customPrelude) + when not defined(mini): + i.eval MINRC.readFile() proc interpret*(i: In, s: Stream) = i.stdLib()

@@ -208,30 +218,41 @@

proc minFile*(filename: string, op = "interpret", main = true): seq[string] {.discardable.} proc compile*(i: In, s: Stream, main = true): seq[string] = - if "nim".findExe == "": - error "Nim compiler not found, unable to compile." - quit(7) + when not defined(mini): + 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 nimFile = i.filename.changeFileExt("nim") + let dotindex = i.filename.rfind(".") + let nimFile = i.filename[0..dotindex-1] & ".nim" if main: - notice("Generating $#..." % nimFile) + when defined(mini): + minilogger.notice("Generating $#..." % nimFile) + else: + logging.notice("Generating $#..." % nimFile) result = i.initCompiledFile(MINMODULES) for m in MINMODULES: let f = m.replace("\\", "/") result.add "### $#" % f - notice("- Including: $#" % f) + when defined(mini): + minilogger.notice("- Including: $#" % f) + else: + 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 $#$#" % [NIMOPTIONS&" ", nimFile] - notice("Calling Nim compiler:") - notice(cmd) - discard execShellCmd(cmd) + when not defined(mini): + let cmd = "nim c $#$#" % [NIMOPTIONS&" ", nimFile] + logging.notice("Calling Nim compiler:") + logging.notice(cmd) + discard execShellCmd(cmd) + else: + minilogger.notice("$# generated successfully, call the nim compiler to compile it.") else: result = result.concat(i.compileFile(main)) except:

@@ -240,7 +261,7 @@ i.close()

proc minStream(s: Stream, filename: string, op = "interpret", main = true): seq[string] {.discardable.}= var i = newMinInterpreter(filename = filename) - i.pwd = filename.parentDir + i.pwd = filename.parentDirEx if op == "interpret": i.interpret(s) newSeq[string](0)

@@ -259,7 +280,10 @@ var contents = ""

try: fileLines = fn.readFile().splitLines() except: - fatal("Cannot read from file: " & fn) + when defined(mini): + minilogger.fatal("Cannot read from file: " & fn) + else: + 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")

@@ -270,90 +294,17 @@

proc minFile*(file: File, filename="stdin", op = "interpret") = var stream = newFileStream(filename) if stream == nil: - fatal("Cannot read from file: " & filename) + when defined(mini): + minilogger.fatal("Cannot read from file: " & filename) + else: + logging.fatal("Cannot read from file: " & filename) quit(3) minStream(stream, filename, op) -proc printResult(i: In, res: MinValue) = - if res.isNil: - return - if i.stack.len > 0: - let n = $i.stack.len - if res.isQuotation and res.qVal.len > 1: - echo " (" - for item in res.qVal: - echo " " & $item - echo " ".repeat(n.len) & ")" - elif res.isDictionary and res.dVal.len > 1: - echo " {" - for item in res.dVal.pairs: - var v = "" - if item.val.kind == minProcOp: - v = "<native>" - else: - v = $item.val.val - echo " " & v & " :" & $item.key - if res.objType == "": - echo " ".repeat(n.len) & "}" - else: - echo " ".repeat(n.len) & " ;" & res.objType - echo " ".repeat(n.len) & "}" - else: - echo " $1" % [$i.stack[i.stack.len - 1]] - -proc minSimpleRepl*(i: var MinInterpreter) = - i.stdLib() - when not defined(mini): - i.dynLib() - var s = newStringStream("") - i.open(s, "<repl>") - var line: string - while true: - i.push("prompt".newSym) - let vals = i.expect("string") - let v = vals[0] - let prompt = v.getString() - stdout.write(prompt) - stdout.flushFile() - line = stdin.readLine() - let r = i.interpret($line) - if $line != "": - i.printResult(r) - -when not defined(mini): - proc minRepl*(i: var MinInterpreter) = - i.stdLib() - i.dynLib() - var s = newStringStream("") - i.open(s, "<repl>") - var line: string - var ed = initEditor(historyFile = MINHISTORY) - while true: - let symbols = toSeq(i.scope.symbols.keys) - ed.completionCallback = proc(ed: LineEditor): seq[string] = - return ed.getCompletions(symbols) - # evaluate prompt - i.push("prompt".newSym) - let vals = i.expect("string") - let v = vals[0] - let prompt = v.getString() - line = ed.readLine(prompt) - let r = i.interpret($line) - if $line != "": - i.printResult(r) - - proc minRepl*() = - var i = newMinInterpreter(filename = "<repl>") - i.minRepl() - -proc minSimpleRepl*() = - var i = newMinInterpreter(filename = "<repl>") - i.minSimpleRepl() - when isMainModule: import - parseopt, + parseopt, core/consts var REPL = false

@@ -361,6 +312,7 @@ var SIMPLEREPL = false

var INSTALL = false var UNINSTALL = false var COMPILE = false + var MODULEPATH = "" var libfile = "" var exeName = "min" var installOpt = "\n -—install:<lib> Install dynamic library file <lib>\n"

@@ -374,6 +326,83 @@ uninstallOpt = ""

iOpt = "" exeName = "minimin" + proc printResult(i: In, res: MinValue) = + if res.isNil: + return + if i.stack.len > 0: + let n = $i.stack.len + if res.isQuotation and res.qVal.len > 1: + echo " (" + for item in res.qVal: + echo " " & $item + echo " ".repeat(n.len) & ")" + elif res.isDictionary and res.dVal.len > 1: + echo " {" + for item in res.dVal.pairs: + var v = "" + if item.val.kind == minProcOp: + v = "<native>" + else: + v = $item.val.val + echo " " & v & " :" & $item.key + if res.objType == "": + echo " ".repeat(n.len) & "}" + else: + echo " ".repeat(n.len) & " ;" & res.objType + echo " ".repeat(n.len) & "}" + else: + echo " $1" % [$i.stack[i.stack.len - 1]] + + proc minSimpleRepl*(i: var MinInterpreter) = + i.stdLib() + when not defined(mini): + i.dynLib() + var s = newStringStream("") + i.open(s, "<repl>") + var line: string + while true: + i.push("prompt".newSym) + let vals = i.expect("string") + let v = vals[0] + let prompt = v.getString() + stdout.write(prompt) + stdout.flushFile() + line = stdin.readLine() + let r = i.interpret($line) + if $line != "": + i.printResult(r) + + when not defined(mini): + proc minRepl*(i: var MinInterpreter) = + i.stdLib() + i.dynLib() + var s = newStringStream("") + i.open(s, "<repl>") + var line: string + var ed = initEditor(historyFile = MINHISTORY) + while true: + let symbols = toSeq(i.scope.symbols.keys) + ed.completionCallback = proc(ed: LineEditor): seq[string] = + return ed.getCompletions(symbols) + # evaluate prompt + i.push("prompt".newSym) + let vals = i.expect("string") + let v = vals[0] + let prompt = v.getString() + line = ed.readLine(prompt) + let r = i.interpret($line) + if $line != "": + i.printResult(r) + + proc minRepl*() = + var i = newMinInterpreter(filename = "<repl>") + i.minRepl() + + proc minSimpleRepl*() = + var i = newMinInterpreter(filename = "<repl>") + i.minSimpleRepl() + + let usage* = """ $exe v$version - a tiny concatenative programming language (c) 2014-2020 Fabio Cevasco

@@ -403,9 +432,13 @@ ]

var file, s: string = "" var args = newSeq[string](0) - setLogFilter(lvlNotice) + when defined(mini): + minilogger.setLogFilter(minilogger.lvlNotice) + else: + logging.setLogFilter(logging.lvlNotice) + var p = initOptParser() - for kind, key, val in getopt(): + for kind, key, val in getopt(p): case kind: of cmdArgument: args.add key

@@ -416,15 +449,16 @@ case key:

of "compile", "c": COMPILE = true of "module-path", "m": - for f in walkDirRec(val): - if f.endsWith(".min"): - MINMODULES.add f + MODULEPATH = val of "prelude", "p": customPrelude = val of "log", "l": if file == "": var val = val - setLogLevel(val) + when defined(mini): + minilogger.setLogLevel(val) + else: + niftylogger.setLogLevel(val) of "passN", "n": NIMOPTIONS = val of "evaluate", "e":

@@ -439,17 +473,17 @@ if file == "":

echo pkgVersion quit(0) of "interactive", "i": - if file == "": + if file == "" and not defined(mini): REPL = true of "interactive-simple", "j": if file == "": SIMPLEREPL = true of "install": - if file == "": + if file == "" and not defined(mini): INSTALL = true libfile = val of "uninstall": - if file == "": + if file == "" and not defined(mini): UNINSTALL = true libfile = val else:

@@ -459,38 +493,41 @@ discard

var op = "interpret" if COMPILE: op = "compile" + + when not defined(mini): + if MODULEPATH.len > 0: + for f in walkDirRec(MODULEPATH): + if f.endsWith(".min"): + MINMODULES.add f + if INSTALL: + if not libfile.fileExists: + logging.fatal("Dynamic library file not found:" & libfile) + quit(4) + try: + libfile.copyFile(MINLIBS/libfile.extractFilename) + except: + logging.fatal("Unable to install library file: " & libfile) + quit(5) + logging.notice("Dynamic linbrary installed successfully: " & libfile.extractFilename) + quit(0) + elif UNINSTALL: + if not (MINLIBS/libfile.extractFilename).fileExists: + logging.fatal("Dynamic library file not found:" & libfile) + quit(4) + try: + removeFile(MINLIBS/libfile.extractFilename) + except: + logging.fatal("Unable to uninstall library file: " & libfile) + quit(6) + logging.notice("Dynamic linbrary uninstalled successfully: " & libfile.extractFilename) + quit(0) + elif REPL: + minRepl() + quit(0) if s != "": minStr(s) elif file != "": minFile file, op - elif INSTALL: - if not libfile.fileExists: - fatal("Dynamic library file not found:" & libfile) - quit(4) - try: - libfile.copyFile(MINLIBS/libfile.extractFilename) - except: - fatal("Unable to install library file: " & libfile) - quit(5) - notice("Dynamic linbrary installed successfully: " & libfile.extractFilename) - quit(0) - elif UNINSTALL: - if not (MINLIBS/libfile.extractFilename).fileExists: - fatal("Dynamic library file not found:" & libfile) - quit(4) - try: - removeFile(MINLIBS/libfile.extractFilename) - except: - fatal("Unable to uninstall library file: " & libfile) - quit(6) - notice("Dynamic linbrary uninstalled successfully: " & libfile.extractFilename) - quit(0) - elif REPL: - when defined(mini): - minSimpleRepl() - else: - minRepl() - quit(0) elif SIMPLEREPL: minSimpleRepl() quit(0)
M min.nimsmin.nims

@@ -10,16 +10,17 @@ switch("amd64.linux.gcc.path", "/usr/local/bin")

switch("amd64.linux.gcc.exe", "x86_64-linux-musl-gcc") switch("amd64.linux.gcc.linkerexe", "x86_64-linux-musl-gcc") -switch("define", "ssl") switch("opt", "size") -switch("threads", "on") -when defined(windows): - # TODO", change once issue nim#15220 is resolved - switch("define", "noOpenSSLHacks") - switch("dynlibOverride", "ssl-") - switch("dynlibOverride", "crypto-") - switch("define", "sslVersion:(") -else: - switch("dynlibOverride", "ssl") - switch("dynlibOverride", "crypto") +when not defined(mini): + switch("define", "ssl") + switch("threads", "on") + when defined(windows): + # TODO", change once issue nim#15220 is resolved + switch("define", "noOpenSSLHacks") + switch("define", "sslVersion:(") + switch("dynlibOverride", "ssl-") + switch("dynlibOverride", "crypto-") + else: + switch("dynlibOverride", "ssl") + switch("dynlibOverride", "crypto")
M min.ymlmin.yml

@@ -2,4 +2,4 @@ author: Fabio Cevasco

description: A tiny concatenative programming language and shell. id: 35077225 name: min -version: 0.24.0+version: 0.24.0
M next-release.mdnext-release.md

@@ -4,3 +4,4 @@ * Added the possibility of including a path containing additional **.min** files to compile along with the main file (**-m**, **--module-path**).

* Added the possibility to compile a bare-bones version of min specifying the **-d:mini** compilation flag. * Added **mini?** symbol which returns true if min was compiled specifying **-d:mini**. * Now distributing precompiled **litemin** and **minimin** executables as well. +* Moved **puts**, **puts!** and **gets** from io module to lang module.
M site/contents/download.mdsite/contents/download.md

@@ -7,11 +7,13 @@

You can download one of the following pre-built min binaries: -* {#release||{{$version}}||macosx||macOS||x64#} -* {#release||{{$version}}||windows||Windows||x64#} -* {#release||{{$version}}||linux||Linux||x64#} +* {#release||{{$version}}||macosx||macOS||x64#} <small>[{#lite-release||{{$version}}||macosx||macOS||x64#}, {#mini-release||{{$version}}||macosx||macOS||x64#}]</small> +* {#release||{{$version}}||windows||Windows||x64#} <small>[{#lite-release||{{$version}}||windows||Windows||x64#}, {#mini-release||{{$version}}||windows||Windows||x64#}]</small> +* {#release||{{$version}}||linux||Linux||x64#} <small>[{#lite-release||{{$version}}||linux||Linux||x64#}, {#mini-release||{{$version}}||linux||Linux||x64#}]</small> {#release -> [min v$1 for $3 ($4)](https://github.com/h3rald/min/releases/download/v$1/min\_v$1\_$2\_$4.zip) #} +{#lite-release -> [lite](https://github.com/h3rald/min/releases/download/v$1/litemin\_v$1\_$2\_$4.zip) #} +{#mini-release -> [mini](https://github.com/h3rald/min/releases/download/v$1/minimin\_v$1\_$2\_$4.zip) #} {{guide-download}}

@@ -38,7 +40,7 @@ ### Additional build options

#### -d:lite -If the **d:lite** flag is specified, a more minimal executable file ("litemin") will be generated, however the following functionalities will not be available: +If the **d:lite** flag is specified, a more minimal executable file will be generated (typically, it should be called "litemin"), however the following functionalities will not be available: * The {#link-module||crypto#} * The {#link-module||net#}

@@ -48,32 +50,41 @@ * The {#link-operator||sys||zip#} and {#link-operator||sys||unzip#} operators.

#### -d:mini -If the **d:mini** flag is specified, an even more minimal executable file ("minimin") will be generated, however the following functionalities will not be available: +If the **d:mini** flag is specified, an even more minimal executable file will be generated (typically, it should be called litemin), however the following functionalities will not be available: +* The {#link-module||crypto#} +* The {#link-module||net#} +* The {#link-module||http#} +* The {#link-module||math#} * The {#link-module||io#} * The {#link-module||fs#} * The {#link-module||sys#} -* The {#link-module||net#} -* The {#link-module||http#} -* The {#link-module||math#} * The following operators: * {#link-operator||lang||load#} * {#link-operator||lang||read#} * {#link-operator||lang||raw-args#} + * {#link-operator||lang||save-symbol#} + * {#link-operator||lang||load-symbol#} + * {#link-operator||lang||saved-symbol#} + * {#link-operator||lang||loaded-symbol#} * {#link-operator||str||search#} * {#link-operator||str||match#} * {#link-operator||str||replace#} * {#link-operator||str||regex#} * {#link-operator||str||semver?#} * {#link-operator||str||from-semver#} + * {#link-operator||sys||zip#} + * {#link-operator||sys||unzip#} Additionally: -* No checks will be performed when defining symbols -* Only the simple REPL will be available -* There will be no support for dynamic libraries +* No checks will be performed when defining symbols. +* Only the simple REPL will be available. +* There will be no support for dynamic libraries. +* The **-m, \-\-module-path** option has no effect. +* No environment configuration files ([.minrc](class.file), [.min_symbols](class:file)) are used. -## Running then min Shell +## Running the min Shell To start min shell, run [min -i](class:cmd). You will be presented with a prompt displaying the path to the current directory:
M site/contents/reference-io.mdsite/contents/reference-io.md

@@ -43,9 +43,6 @@

{#op||getchr||{{null}}||{{i}}|| Reads single character from STDIN without waiting for ENTER key and places its ASCII code on top of the stack.#} -{#op||gets||{{null}}||{{s}}|| -Reads a line from STDIN and places it on top of the stack as a string.#} - {#op||info||{{any}}||{{any}}|| Prints {{any}} and a new line to STDOUT, if logging level is set to [info](class:kwd) or lower.#}

@@ -66,12 +63,6 @@ Prints {{any}} to STDOUT and removes {{any}} from the stack.#}

{#op||putchr||{{s}}||{{any}}|| Prints {{s}} to STDOUT without printing a new line ({{s}} must contain only one character).#} - -{#op||puts||{{any}}||{{any}}|| -Prints {{any}} and a new line to STDOUT.#} - -{#op||puts!||{{any}}||{{null}}|| -Prints {{any}} and a new line to STDOUT, removing {{any}} from the stack.#} {#op||type||{{any}}||{{s}}|| Puts the data type of {{any}} on the stack. In cased of typed dictionaries, the type name is prefixed by `dict:`, e.g. `dict:module`, `dict:socket`, etc.#}
M site/contents/reference-lang.mdsite/contents/reference-lang.md

@@ -162,6 +162,9 @@ > > Note

> > > > At present, only YAML objects containing string values are supported.#} +{#op||gets||{{null}}||{{s}}|| +Reads a line from STDIN and places it on top of the stack as a string.#} + {#op||if||{{q1}} {{q2}} {{q3}}||{{a0p}}|| If {{q1}} evaluates to {{t}} then evaluates {{q2}}, otherwise evaluates {{q3}}.#}

@@ -272,6 +275,12 @@ > > Example

> > > Publish symbol [my-local-symbol](class:kwd) to [ROOT](class:kwd) scope: > > `'my-local-symbol ROOT publish` #} + +{#op||puts||{{any}}||{{any}}|| +Prints {{any}} and a new line to STDOUT.#} + +{#op||puts!||{{any}}||{{null}}|| +Prints {{any}} and a new line to STDOUT, removing {{any}} from the stack.#} {#op||quit||{{null}}||{{null}}|| Exits the program or shell with 0 as return code. #}
A tasks/_helpers.min

@@ -0,0 +1,10 @@

+ +"min.yml" fread from-yaml :config +".env.yml" fread from-yaml :env + +; Module symbols +{} +( + :prog (prog which "" ==) ("$# is not available" (prog) =% error 1 exit) when +) %required ++helpers
M tasks/build.mintasks/build.min

@@ -1,11 +1,8 @@

#!/usr/bin/env min -"min.yml" fread from-yaml :config +"_helpers" load +'helpers import -; Helpers -( - :prog (prog which "" ==) ("$# is not available" (prog) =% error 1 exit) when -) :required ( :target-os :variant

@@ -54,7 +51,7 @@ "Building - min.vim" notice

template ("date" date "version" min-version "symbols" symbols) =% out-file fwrite ) :build-vim -; Define module +; Module symbols {} ( "lite" os cz
M tasks/github.mintasks/github.min

@@ -1,16 +1,15 @@

#!/usr/bin/env min -; Read files -".env.yml" fread from-yaml :vars -"min.yml" fread from-yaml :config +"_helpers" load +'helpers import + "next-release.md" fread escape :release-body "tasks/templates/draft-release.json" fread :draft-release-template config /version :min-version -vars /github-token :token +env /github-token :token draft-release-template ("version" min-version "body" release-body) =% :draft-release-body -; Helper symbols {} "application/vnd.github.v3+json" %Accept "token $#" (token) =% %Authorization
A tasks/h3rald.min

@@ -0,0 +1,32 @@

+#!/usr/bin/env min + +"_helpers" load +'helpers import + +config /version :min-version +"Min_DeveloperGuide.htm" :guide-file +"../h3rald/assets/min/$#" guide-file suffix :h3rald-guide +"../h3rald/contents/min.md" :h3rald-min-md +"version:\\s+\\d+\\.\\d+\\.\\d+" :min-v-reg +"version: $#" (min-version) =% :min-v-rep + +; Module symbols +{} +( + "Updating min Developer Guide and project on H3RALD.com..." notice + guide-file h3rald-guide cp + h3rald-min-md fread "s/$#/$#/m" (min-v-reg min-v-rep) =% regex 0 get :updated-contents + updated-contents h3rald-min-md fwrite + "Done." notice +) %update +( + "git" required + "hastysite" required + "Pulling content and rebuilding H3RALD.com web site..." notice + ; Assuming min and h3rald are siblings + .. cd "h3rald" cd + "git pull" ! + "hastysite build" ! + .. cd "min" cd +) %build ++h3rald-tasks
M tasks/release.mintasks/release.min

@@ -4,6 +4,7 @@ "build" load

"github" load "ssh" load +; Module symbols {} ( build-tasks ^guide

@@ -20,8 +21,8 @@ build-tasks ^macosx-lite

build-tasks ^macosx-mini github-tasks ^update github-tasks ^upload - ;ssh-tasks ^build - ;ssh-tasks ^h3rald + ssh-tasks ^build + ssh-tasks ^h3rald "All done!" notice ) %default +release-tasks
M tasks/ssh.mintasks/ssh.min

@@ -1,46 +1,26 @@

#!/usr/bin/env min -".env.yml" fread from-yaml :env -"min.yml" fread from-yaml :config +"_helpers" load +'helpers import env /ssh-host :host -env /ssh-h3rald-dir :h3rald-dir env /ssh-min-dir :min-dir -"Min_DeveloperGuide.htm" :guide-file -"$#/assets/min/$#" (h3rald-dir guide-file) =% :h3rald-guide "cd " min-dir suffix :min-cd -"cd " h3rald-dir suffix :h3rald-cd -"$#/contents/min.md" (min-dir) =% :min-md -"min.yml" fread from-yaml :config -config /version :min-version -"version:\\s+\\d+\\.\\d+\\.\\d+" :min-v-reg -"version: $#" (min-version) =% :min-v-rep - -; Helpers -( - :prog (prog which "" ==) ("$# is not available" (prog) =% error 1 exit) when -) :required ( "export PATH=~/bin:~/.nimble/bin:$PATH" min-cd ) :init +; Module symbols {} -( - ; This should be executed on the remote host - guide-file h3rald-guide cp - min-md fread "s/$#/$#/m" (min-v-reg min-v-rep) =% regex min-md fwrite -) :h3rald-update ( "ssh" required - "ssh - build ($#)" (host) =% notice + "ssh - h3rald ($#)" (host) =% notice ( init - "min run ssh:h3rald-update" - h3rald-cd - "git pull" - "hastysite build" + "min run h3rald:update" + "min run h3rald:build" ) => "; " join :cmds "ssh $# \"$#\"" (host cmds) =% !! ) %h3rald