all repos — min @ ef1b2d45d7faa0065d4321ae4982c406200285f0

A small but practical concatenative programming language.

Merge branch 'next'
h3rald h3rald@h3rald.com
Sat, 12 Dec 2020 01:21:46 +0000
commit

ef1b2d45d7faa0065d4321ae4982c406200285f0

parent

55bddeb1e0244d56289057cabf2eb47813ef6fe9

M .gitignore.gitignore

@@ -1,7 +1,9 @@

nimcache/ build/ -min.exe +*.exe min +litemin +minimin nakefile Min_DeveloperGuide.htm nimsuggest.log

@@ -14,4 +16,5 @@ site/temp/

dynlibs/ .env.yml response.json -_* +run +run.nim
A core/baseutils.nim

@@ -0,0 +1,50 @@

+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] + + proc escapeEx*(s: string, unquoted = false): string = + for c in s: + case c + of '\L': result.add("\\n") + of '\b': result.add("\\b") + of '\f': result.add("\\f") + of '\t': result.add("\\t") + of '\v': result.add("\\u000b") + of '\r': result.add("\\r") + of '"': result.add("\\\"") + of '\0'..'\7': result.add("\\u000" & $ord(c)) + of '\14'..'\31': result.add("\\u00" & toHex(ord(c), 2)) + of '\\': result.add("\\\\") + else: result.add(c) + if unquoted: + return result + return "\"" & result & "\"" + +else: + import os, json + + proc parentDirEx*(s: string): string = + return s.parentDir + + proc escapeEx*(s: string, unquoted = false): string = + if unquoted: + return s.escapeJsonUnquoted + return s.escapeJson
M core/env.nimcore/env.nim

@@ -1,16 +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

@@ -1,11 +1,18 @@

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

@@ -15,6 +22,8 @@ MinTrappedException* = ref object of CatchableError

MinRuntimeError* = ref object of CatchableError data*: MinValue +var MINCOMPILEDFILES* {.threadvar.}: CritBitTree[MinOperatorProc] + proc raiseRuntime*(msg: string, data: MinValue) {.extern:"min_exported_symbol_$1".}= data.objType = "error" raise MinRuntimeError(msg: msg, data: data)

@@ -58,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)

@@ -79,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

@@ -278,6 +289,46 @@ if parseOnly:

return q if i.stack.len > 0: return i.stack[i.stack.len - 1] + +proc rawCompile*(i: In, indent = ""): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = + while i.parser.token != tkEof: + if i.trace.len == 0: + i.stackcopy = i.stack + try: + result.add i.parser.compileMinValue(i, push = true, indent) + except MinRuntimeError: + let msg = getCurrentExceptionMsg() + i.stack = i.stackcopy + error("$1:$2,$3 $4" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, msg]) + i.stackTrace + i.trace = @[] + raise MinTrappedException(msg: msg) + except MinTrappedException: + raise + except: + let msg = getCurrentExceptionMsg() + i.stack = i.stackcopy + i.error(msg) + i.stackTrace + i.trace = @[] + raise MinTrappedException(msg: msg) + +proc compileFile*(i: In, main: bool): seq[string] {.discardable, extern:"min_exported_symbol_$1".} = + result = newSeq[string](0) + if not main: + result.add "MINCOMPILEDFILES[\"$#\"] = proc(i: In) {.gcsafe.}=" % i.filename + result = result.concat(i.rawCompile(" ")) + else: + 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" + if files.len > 0: + result.add "import critbits" + result.add "MINCOMPILED = true" + 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".}= var i2 = i.copy(name)
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/parser.nimcore/parser.nim

@@ -2,9 +2,12 @@ # Adapted from: https://github.com/Araq/Nimrod/blob/v0.9.6/lib/pure/json.nim

import lexbase, strutils, + sequtils, + oids, streams, - critbits - + critbits, + baseutils + import unicode except strip type

@@ -594,7 +597,6 @@ d = d.strip & "}"

return d proc parseMinValue*(p: var MinParser, i: In): MinValue {.extern:"min_exported_symbol_$1".}= - #echo p.a, " (", p.token, ")" case p.token of tkTrue: result = MinValue(kind: minBool, boolVal: true)

@@ -618,7 +620,7 @@ discard getToken(p)

while p.token != tkBracketRi: q.add p.parseMinValue(i) eat(p, tkBracketRi) - result = MinValue(kind: minQuotation, qVal: q)#, scope: newscope) + result = MinValue(kind: minQuotation, qVal: q) of tkBraceLe: var scope = newScopeRef(nil) var val: MinValue

@@ -649,6 +651,79 @@ discard getToken(p)

else: raiseUndefined(p, "Undefined value: '"&p.a&"'") result.filename = p.filename + +proc compileMinValue*(p: var MinParser, i: In, push = true, indent = ""): seq[string] {.extern:"min_exported_symbol_$1".}= + var op = indent + if push: + op = indent&"i.push " + result = newSeq[string](0) + case p.token + of tkTrue: + result = @[op&"MinValue(kind: minBool, boolVal: true)"] + discard getToken(p) + of tkFalse: + result = @[op&"MinValue(kind: minBool, boolVal: false)"] + discard getToken(p) + of tkString: + result = @[op&"MinValue(kind: minString, strVal: "&p.a.escapeEx&")"] + p.a = "" + discard getToken(p) + of tkInt: + result = @[op&"MinValue(kind: minInt, intVal: "&p.a&")"] + discard getToken(p) + of tkFloat: + result = @[op&"MinValue(kind: minFloat, floatVal: "&p.a&")"] + discard getToken(p) + of tkBracketLe: + var qvar = "q" & $genOid() + result.add indent&"var "&qvar&" = newSeq[MinValue](0)" + discard getToken(p) + while p.token != tkBracketRi: + var instructions = p.compileMinValue(i, false, indent) + let v = instructions.pop + result = result.concat(instructions) + result.add indent&qvar&".add "&v + eat(p, tkBracketRi) + result.add op&"MinValue(kind: minQuotation, qVal: "&qvar&")" + of tkBraceLe: + result = newSeq[string](0) + var val: MinValue + discard getToken(p) + var c = 0 + var valInitialized = false + var scopevar = "scope" & $genOid() + var valvar = "val" & $genOid() + while p.token != tkBraceRi: + c = c+1 + var instructions = p.compileMinValue(i, false, indent) + let v = p.parseMinValue(i) + let vs = instructions.pop + result = result.concat(instructions) + if val.isNil: + if not valInitialized: + result.add indent&"var "&valvar&": MinValue" + valInitialized = true + result.add indent&valvar&" = "&vs + elif v.kind == minSymbol: + let key = v.symVal + if key[0] == ':': + result.add indent&scopevar&".symbols["&key[1 .. key.len-1]&"] = MinOperator(kind: minValOp, val: "&valvar&", sealed: false)" + val = nil + else: + raiseInvalid("Invalid dictionary key: " & key) + else: + raiseInvalid("Invalid dictionary key: " & $v) + eat(p, tkBraceRi) + if c mod 2 != 0: + raiseInvalid("Invalid dictionary") + result.add indent&"var "&scopevar&" = newScopeRef(nil)" + result.add op&"MinValue(kind: minDictionary, scope: "&scopevar&")" + of tkSymbol: + result = @[op&"MinValue(kind: minSymbol, symVal: "&p.a.escapeEx&", column: " & $p.getColumn & ", line: " & $p.lineNumber & ", filename: "&p.filename.escapeEx&")"] + p.a = "" + discard getToken(p) + else: + raiseUndefined(p, "Undefined value: '"&p.a&"'") proc print*(a: MinValue) {.extern:"min_exported_symbol_$1".}= stdout.write($$a)
M core/utils.nimcore/utils.nim

@@ -1,18 +1,16 @@

import strutils, - critbits, - json + critbits import - ../packages/nim-sgregex/sgregex, + 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 + +when not defined(mini): + import + json # Library methods

@@ -45,8 +43,6 @@ if name != "":

scope.previous.symbols[name] = MinOperator(kind: minProcOp, prc: op) # Dictionary Methods - -proc `%`*(i: In, a: MinValue): JsonNode {.extern:"min_exported_symbol_percent_2".} proc dget*(i: In, q: MinValue, s: MinValue): MinValue {.extern:"min_exported_symbol_$1".}= if not q.isDictionary:

@@ -124,61 +120,63 @@ r.add key.newVal

r.add value.val return r.newVal -# JSON interop +when not defined(mini): -proc `%`*(i: In, a: MinValue): JsonNode {.extern:"min_exported_symbol_percent_2".}= - case a.kind: - of minBool: - return %a.boolVal - of minSymbol: - return %(";sym:$1" % [a.getstring]) - of minString: - return %a.strVal - of minInt: - return %a.intVal - of minFloat: - return %a.floatVal - of minQuotation: - result = newJArray() - for it in a.qVal: - result.add(i%it) - of minDictionary: - result = newJObject() - for it in a.dVal.pairs: - result[it.key] = i%i.dget(a, it.key) + # JSON interop -proc fromJson*(i: In, json: JsonNode): MinValue {.extern:"min_exported_symbol_$1".}= - case json.kind: - of JNull: - result = newSeq[MinValue](0).newVal - of JBool: - result = json.getBool.newVal - of JInt: - result = json.getBiggestInt.newVal - of JFloat: - result = json.getFloat.newVal - of JString: - let s = json.getStr - if s.match("^;sym:"): - result = sgregex.replace(s, "^;sym:", "").newSym - else: - result = json.getStr.newVal - of JObject: - var res = newDict(i.scope) - for key, value in json.pairs: - var first = $key[0] - var rest = "" - if key.len > 1: - rest = key[1..key.len-1] - first = sgregex.replace(first, "[^a-zA-Z0-9_]", "_") - rest = sgregex.replace(rest, "[^a-zA-Z0-9/!?+*._-]", "_") - discard i.dset(res, first&rest, i.fromJson(value)) - return res - of JArray: - var res = newSeq[MinValue](0) - for value in json.items: - res.add i.fromJson(value) - return res.newVal + proc `%`*(i: In, a: MinValue): JsonNode {.extern:"min_exported_symbol_percent_2".}= + case a.kind: + of minBool: + return %a.boolVal + of minSymbol: + return %(";sym:$1" % [a.getstring]) + of minString: + return %a.strVal + of minInt: + return %a.intVal + of minFloat: + return %a.floatVal + of minQuotation: + result = newJArray() + for it in a.qVal: + result.add(i%it) + of minDictionary: + result = newJObject() + for it in a.dVal.pairs: + result[it.key] = i%i.dget(a, it.key) + + proc fromJson*(i: In, json: JsonNode): MinValue {.extern:"min_exported_symbol_$1".}= + case json.kind: + of JNull: + result = newSeq[MinValue](0).newVal + of JBool: + result = json.getBool.newVal + of JInt: + result = json.getBiggestInt.newVal + of JFloat: + result = json.getFloat.newVal + of JString: + let s = json.getStr + if s.startsWith(";sym:"): + result = s.replace(";sym:", "").newSym + else: + result = json.getStr.newVal + of JObject: + var res = newDict(i.scope) + for key, value in json.pairs: + var first = $key[0] + var rest = "" + if key.len > 1: + rest = key[1..key.len-1] + #first = sgregex.replace(first, peg"[^a-zA-Z0-9_]", "_") + #rest = sgregex.replace(rest, peg"[^a-zA-Z0-9/!?+*._-]", "_") + discard i.dset(res, first&rest, i.fromJson(value)) + return res + of JArray: + var res = newSeq[MinValue](0) + for value in json.items: + res.add i.fromJson(value) + return res.newVal # Validators
M lib/min_http.nimlib/min_http.nim

@@ -6,6 +6,14 @@ ../core/value,

../core/interpreter, ../core/utils +when defined(amd64): + when defined(windows): + {.passL: "-static -Lvendor/openssl/windows -lssl -lcrypto -lws2_32".} + elif defined(linux): + {.passL: "-static -Lvendor/openssl/linux -lssl -lcrypto".} + elif defined(macosx): + {.passL: "-Bstatic -Lvendor/openssl/macosx -lssl -lcrypto -Bdynamic".} + var minUserAgent {.threadvar.} : string minUserAgent = "$1 http-module/$2" % [pkgName, pkgVersion]
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

@@ -3,10 +3,19 @@ critbits,

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

@@ -14,8 +23,6 @@ ../core/parser,

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

@@ -23,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")

@@ -73,6 +94,9 @@

def.symbol("lite?") do (i: In): i.push defined(lite).newVal + def.symbol("mini?") do (i: In): + i.push defined(mini).newVal + def.symbol("from-yaml") do (i: In): let vals = i.expect("string") let s = vals[0]

@@ -88,10 +112,7 @@ i.push(dict)

except: raiseInvalid("Invalid/unsupported YAML object (only dictionaries with string values are supported)") - def.symbol("from-json") do (i: In): - let vals = i.expect("string") - let s = vals[0] - i.push i.fromJson(s.getString.parseJson) + def.symbol("to-yaml") do (i: In): let vals = i.expect "a"

@@ -110,19 +131,20 @@ i.push(yaml.strip.newVal)

except: raiseInvalid(err) - def.symbol("to-json") do (i: In): - let vals = i.expect "a" - let q = vals[0] - i.push(($((i%q).pretty)).newVal) - 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

@@ -136,8 +158,9 @@ if not q1.isQuotation:

q1 = @[q1].newVal isQuot = false symbol = sym.getString - if not symbol.match "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$": - raiseInvalid("Symbol identifier '$1' contains invalid characters." % symbol) + when not defined(mini): + if not symbol.match "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$": + raiseInvalid("Symbol identifier '$1' contains invalid characters." % symbol) info "[define] $1 = $2" % [symbol, $q1] if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: raiseUndefined("Attempting to redefine sealed symbol '$1'" % [symbol])

@@ -220,28 +243,91 @@ let vals = i.expect("string")

let s = vals[0] i.push i.parse s.strVal - def.symbol("load") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - var file = s.getString - if not file.endsWith(".min"): - file = file & ".min" - file = i.pwd.joinPath(file) - info("[load] File: ", file) - if not file.fileExists: - raiseInvalid("File '$1' does not exist." % file) - i.load file + when not defined(mini): + + def.symbol("from-json") do (i: In): + let vals = i.expect("string") + let s = vals[0] + i.push i.fromJson(s.getString.parseJson) + + def.symbol("to-json") do (i: In): + let vals = i.expect "a" + let q = vals[0] + i.push(($((i%q).pretty)).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("load") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + var file = s.getString + if not file.endsWith(".min"): + file = file & ".min" + info("[load] File: ", file) + if MINCOMPILED: + var compiledFile = strutils.replace(strutils.replace(file, "\\", "/"), "./", "") + if MINCOMPILEDFILES.hasKey(compiledFile): + MINCOMPILEDFILES[compiledFile](i) + return + file = i.pwd.joinPath(file) + if not file.fileExists: + raiseInvalid("File '$1' does not exist." % file) + i.load file + + def.symbol("read") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + var file = s.getString + if not file.endsWith(".min"): + file = file & ".min" + info("[read] File: ", file) + if not file.fileExists: + raiseInvalid("File '$1' does not exist." % file) + i.push i.read file - def.symbol("read") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - var file = s.getString - if not file.endsWith(".min"): - file = file & ".min" - info("[read] File: ", file) - if not file.fileExists: - raiseInvalid("File '$1' does not exist." % file) - i.push i.read file + def.symbol("raw-args") do (i: In): + var args = newSeq[MinValue](0) + for par in commandLineParams(): + args.add par.newVal + i.push args.newVal def.symbol("with") do (i: In): let vals = i.expect("dict", "quot")

@@ -359,7 +445,10 @@ if not hasCatch:

return let e = getCurrentException() var res = newDict(i.scope) - let err = sgregex.replace($e.name, ":.+$", "") + var err = $e.name + let col = err.find(":") + if col >= 0: + err = err[0..col-1] res.objType = "error" i.dset(res, "error", err.newVal) i.dset(res, "message", e.msg.newVal)

@@ -533,46 +622,6 @@

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") let sym = vals[0]

@@ -627,12 +676,6 @@ else:

discard i.push opts - def.symbol("raw-args") do (i: In): - var args = newSeq[MinValue](0) - for par in commandLineParams(): - args.add par.newVal - i.push args.newVal - def.symbol("expect") do (i: In): var q: MinValue i.reqQuotationOfSymbols q

@@ -664,6 +707,9 @@ let vals = i.expect("quot")

var q = vals[0] q.qVal.reverse i.dequote(q) + + def.symbol("compiled?") do (i: In): + i.push MINCOMPILED.newVal # Converters

@@ -716,7 +762,10 @@ else:

raiseInvalid("Cannot convert a quotation to float.") def.symbol("prompt") do (i: In): - i.eval(""""[$1]\n$$ " (.) => %""") + when defined(mini): + i.push "$ ".newVal + else: + i.eval(""""[$1]\n$$ " (.) => %""") # Sigils
M lib/min_str.nimlib/min_str.nim

@@ -1,13 +1,16 @@

import strutils, - sequtils, - json + sequtils import ../core/parser, ../core/value, ../core/interpreter, - ../core/utils, - ../packages/nim-sgregex/sgregex + ../core/baseutils, + ../core/utils + +when not defined(mini): + import + ../packages/nim-sgregex/sgregex proc str_module*(i: In) =

@@ -116,58 +119,59 @@ let str = vals[1]

let index = str.strVal.find(reg.strVal) i.push index.newVal - def.symbol("search") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - var matches = str.strVal.search(reg.strVal) - var res = newSeq[MinValue](matches.len) - for i in 0..matches.len-1: - res[i] = matches[i].newVal - i.push res.newVal + when not defined(mini): + def.symbol("search") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + var matches = str.strVal.search(reg.strVal) + var res = newSeq[MinValue](matches.len) + for i in 0..matches.len-1: + res[i] = matches[i].newVal + i.push res.newVal - def.symbol("match") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - if str.strVal.match(reg.strVal): - i.push true.newVal - else: - i.push false.newVal + def.symbol("match") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + if str.strVal.match(reg.strVal): + i.push true.newVal + else: + i.push false.newVal - def.symbol("replace") do (i: In): - let vals = i.expect("string", "string", "string") - let s_replace = vals[0] - let reg = vals[1] - let s_find = vals[2] - i.push sgregex.replace(s_find.strVal, reg.strVal, s_replace.strVal).newVal + def.symbol("replace") do (i: In): + let vals = i.expect("string", "string", "string") + let s_replace = vals[0] + let reg = vals[1] + let s_find = vals[2] + i.push sgregex.replace(s_find.strVal, reg.strVal, s_replace.strVal).newVal - def.symbol("regex") do (i: In): - let vals = i.expect("string", "string") - let reg = vals[0] - let str = vals[1] - let results = str.strVal =~ reg.strVal - var res = newSeq[MinValue](0) - for r in results: - res.add(r.newVal) - i.push res.newVal + def.symbol("regex") do (i: In): + let vals = i.expect("string", "string") + let reg = vals[0] + let str = vals[1] + let results = str.strVal =~ reg.strVal + var res = newSeq[MinValue](0) + for r in results: + res.add(r.newVal) + i.push res.newVal - def.symbol("semver?") do (i: In): - let vals = i.expect("string") - let v = vals[0].strVal - i.push v.match("^\\d+\\.\\d+\\.\\d+$").newVal - - def.symbol("from-semver") do (i: In): - let vals = i.expect("string") - let v = vals[0].strVal - let parts = v.search("^(\\d+)\\.(\\d+)\\.(\\d+)$") - if parts[0].len == 0: - raiseInvalid("String '$1' is not a basic semver" % v) - var d = newDict(i.scope) - i.dset(d, "major", parts[1].parseInt.newVal) - i.dset(d, "minor", parts[2].parseInt.newVal) - i.dset(d, "patch", parts[3].parseInt.newVal) - i.push d + def.symbol("semver?") do (i: In): + let vals = i.expect("string") + let v = vals[0].strVal + i.push v.match("^\\d+\\.\\d+\\.\\d+$").newVal + + def.symbol("from-semver") do (i: In): + let vals = i.expect("string") + let v = vals[0].strVal + let parts = v.search("^(\\d+)\\.(\\d+)\\.(\\d+)$") + if parts[0].len == 0: + raiseInvalid("String '$1' is not a basic semver" % v) + var d = newDict(i.scope) + i.dset(d, "major", parts[1].parseInt.newVal) + i.dset(d, "minor", parts[2].parseInt.newVal) + i.dset(d, "patch", parts[3].parseInt.newVal) + i.push d def.symbol("to-semver") do (i: In): let vals = i.expect("dict")

@@ -211,9 +215,7 @@

def.symbol("escape") do (i: In): let vals = i.expect("'sym") let a = vals[0].getString - var s = "" - a.escapeJsonUnquoted(s) - i.push s.newVal + i.push a.escapeEx(true).newVal def.symbol("prefix") do (i: In): let vals = i.expect("'sym", "'sym")
M lib/min_sys.nimlib/min_sys.nim

@@ -1,231 +1,232 @@

-import - os, - osproc, - strutils, - sequtils, - logging -import - ../core/parser, - ../core/value, - ../core/interpreter, - ../core/utils, - ../core/fileutils - -when not defined(lite): - import ../packages/nim-miniz/src/nim_miniz - -proc unix(s: string): string = - return s.replace("\\", "/") - -proc sys_module*(i: In)= - let def = i.define() - - def.symbol(".") do (i: In): - i.push newVal(getCurrentDir().unix) - - def.symbol("..") do (i: In): - i.push newVal(getCurrentDir().parentDir.unix) - - def.symbol("cd") do (i: In): - let vals = i.expect("'sym") - let f = vals[0].getString - i.pwd = joinPath(getCurrentDir(), f) - info("Current directory changed to: ", i.pwd) - f.setCurrentDir - - def.symbol("ls") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - var list = newSeq[MinValue](0) - for i in walkDir(a.getString): - list.add newVal(i.path.unix) - i.push list.newVal - - def.symbol("ls-r") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - var list = newSeq[MinValue](0) - for i in walkDirRec(a.getString): - list.add newVal(i.unix) - i.push list.newVal - - def.symbol("system") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - i.push execShellCmd(a.getString).newVal - - def.symbol("system!") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - discard execShellCmd(a.getString) - - def.symbol("run") do (i: In): - let vals = i.expect("'sym") - let cmd = vals[0] - let res = execCmdEx(cmd.getString) - var d = newDict(i.scope) - i.dset(d, "output", res.output.newVal) - i.dset(d, "code", res.exitCode.newVal) - i.push(d) - - def.symbol("get-env") do (i: In): - let vals = i.expect("'sym") - let a = vals[0] - i.push a.getString.getEnv.newVal - - def.symbol("put-env") do (i: In): - let vals = i.expect("'sym", "'sym") - let key = vals[0] - let value = vals[1] - key.getString.putEnv value.getString - - def.symbol("env?") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.existsEnv.newVal - - def.symbol("which") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.findExe.newVal - - def.symbol("os") do (i: In): - i.push hostOS.newVal - - def.symbol("cpu") do (i: In): - 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) - - def.symbol("file?") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.fileExists.newVal - - def.symbol("dir?") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.dirExists.newVal - - def.symbol("rm") do (i: In): - let vals = i.expect("'sym") - let v = vals[0] - let f = v.getString - if f.fileExists: - f.removeFile - else: - raiseInvalid("File '$1' does not exist." % f) - - def.symbol("cp") do (i: In): - let vals = i.expect("'sym", "'sym") - let a = vals[0] - let b = vals[1] - let src = b.getString - var dest = a.getString - if src.dirExists: - copyDirWithPermissions src, dest - elif dest.dirExists: - if src.dirExists: - copyDirWithPermissions src, dest - else: - copyFileWithPermissions src, dest / src.extractFilename - else: - copyFileWithPermissions src, dest - - def.symbol("mv") do (i: In): - let vals = i.expect("'sym", "'sym") - let a = vals[0] - let b = vals[1] - let src = b.getString - var dest = a.getString - if dest.dirExists: - dest = dest / src.extractFilename - moveFile src, dest - - def.symbol("rmdir") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - f.getString.removeDir - - def.symbol("mkdir") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - f.getString.createDir - - def.symbol("sleep") do (i: In): - let vals = i.expect("int") - let ms = vals[0] - sleep ms.intVal.int - - def.symbol("chmod") do (i: In): - let vals = i.expect("int", "string") - let perms = vals[0] - let s = vals[1] - s.getString.setFilePermissions(perms.intVal.toFilePermissions) - - def.symbol("symlink?") do (i: In): - let vals = i.expect("'sym") - let s = vals[0] - i.push s.getString.symlinkExists.newVal - - def.symbol("symlink") do (i: In): - let vals = i.expect("'sym", "'sym") - let dest = vals[0] - let src = vals[1] - src.getString.createSymlink dest.getString - - def.symbol("hardlink") do (i: In): - let vals = i.expect("'sym", "'sym") - let dest = vals[0] - let src = vals[1] - src.getString.createHardlink dest.getString - - def.symbol("filename") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.extractFilename.unix.newVal - - def.symbol("dirname") do (i: In): - let vals = i.expect("'sym") - let f = vals[0] - i.push f.getString.parentDir.unix.newVal - - def.symbol("$") do (i: In): - i.push("get-env".newSym) - - def.symbol("!") do (i: In): - i.push("system".newSym) - - def.symbol("!!") do (i: In): - i.push("system!".newSym) - - def.symbol("&") do (i: In): - i.push("run".newSym) - - def.sigil("$") do (i: In): - i.push("get-env".newSym) - - def.sigil("!") do (i: In): - i.push("system".newSym) - - def.sigil("&") do (i: In): - i.push("run".newSym) - - when not defined(lite): - def.symbol("unzip") do (i: In): - let vals = i.expect("'sym", "'sym") - let dir = vals[0] - let f = vals[1] - nim_miniz.unzip(f.getString, dir.getString) - - def.symbol("zip") do (i: In): - let vals = i.expect("'sym", "quot") - let file = vals[0] - let files = vals[1] - nim_miniz.zip(files.qVal.mapIt(it.getString), file.getString) - - def.finalize("sys") - +import + os, + osproc, + strutils, + logging +when not defined(lite): + import sequtils +import + ../core/parser, + ../core/value, + ../core/interpreter, + ../core/utils, + ../core/fileutils + +when not defined(lite): + import ../packages/nim-miniz/src/nim_miniz + +proc unix(s: string): string = + return s.replace("\\", "/") + +proc sys_module*(i: In)= + let def = i.define() + + def.symbol(".") do (i: In): + i.push newVal(getCurrentDir().unix) + + def.symbol("..") do (i: In): + i.push newVal(getCurrentDir().parentDir.unix) + + def.symbol("cd") do (i: In): + let vals = i.expect("'sym") + let f = vals[0].getString + i.pwd = joinPath(getCurrentDir(), f) + info("Current directory changed to: ", i.pwd) + f.setCurrentDir + + def.symbol("ls") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + var list = newSeq[MinValue](0) + for i in walkDir(a.getString): + list.add newVal(i.path.unix) + i.push list.newVal + + def.symbol("ls-r") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + var list = newSeq[MinValue](0) + for i in walkDirRec(a.getString): + list.add newVal(i.unix) + i.push list.newVal + + def.symbol("system") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + i.push execShellCmd(a.getString).newVal + + def.symbol("system!") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + discard execShellCmd(a.getString) + + def.symbol("run") do (i: In): + let vals = i.expect("'sym") + let cmd = vals[0] + let res = execCmdEx(cmd.getString) + var d = newDict(i.scope) + i.dset(d, "output", res.output.newVal) + i.dset(d, "code", res.exitCode.newVal) + i.push(d) + + def.symbol("get-env") do (i: In): + let vals = i.expect("'sym") + let a = vals[0] + i.push a.getString.getEnv.newVal + + def.symbol("put-env") do (i: In): + let vals = i.expect("'sym", "'sym") + let key = vals[0] + let value = vals[1] + key.getString.putEnv value.getString + + def.symbol("env?") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.existsEnv.newVal + + def.symbol("which") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.findExe.newVal + + def.symbol("os") do (i: In): + i.push hostOS.newVal + + def.symbol("cpu") do (i: In): + 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) + + def.symbol("file?") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.fileExists.newVal + + def.symbol("dir?") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.dirExists.newVal + + def.symbol("rm") do (i: In): + let vals = i.expect("'sym") + let v = vals[0] + let f = v.getString + if f.fileExists: + f.removeFile + else: + raiseInvalid("File '$1' does not exist." % f) + + def.symbol("cp") do (i: In): + let vals = i.expect("'sym", "'sym") + let a = vals[0] + let b = vals[1] + let src = b.getString + var dest = a.getString + if src.dirExists: + copyDirWithPermissions src, dest + elif dest.dirExists: + if src.dirExists: + copyDirWithPermissions src, dest + else: + copyFileWithPermissions src, dest / src.extractFilename + else: + copyFileWithPermissions src, dest + + def.symbol("mv") do (i: In): + let vals = i.expect("'sym", "'sym") + let a = vals[0] + let b = vals[1] + let src = b.getString + var dest = a.getString + if dest.dirExists: + dest = dest / src.extractFilename + moveFile src, dest + + def.symbol("rmdir") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + f.getString.removeDir + + def.symbol("mkdir") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + f.getString.createDir + + def.symbol("sleep") do (i: In): + let vals = i.expect("int") + let ms = vals[0] + sleep ms.intVal.int + + def.symbol("chmod") do (i: In): + let vals = i.expect("int", "string") + let perms = vals[0] + let s = vals[1] + s.getString.setFilePermissions(perms.intVal.toFilePermissions) + + def.symbol("symlink?") do (i: In): + let vals = i.expect("'sym") + let s = vals[0] + i.push s.getString.symlinkExists.newVal + + def.symbol("symlink") do (i: In): + let vals = i.expect("'sym", "'sym") + let dest = vals[0] + let src = vals[1] + src.getString.createSymlink dest.getString + + def.symbol("hardlink") do (i: In): + let vals = i.expect("'sym", "'sym") + let dest = vals[0] + let src = vals[1] + src.getString.createHardlink dest.getString + + def.symbol("filename") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.extractFilename.unix.newVal + + def.symbol("dirname") do (i: In): + let vals = i.expect("'sym") + let f = vals[0] + i.push f.getString.parentDir.unix.newVal + + def.symbol("$") do (i: In): + i.push("get-env".newSym) + + def.symbol("!") do (i: In): + i.push("system".newSym) + + def.symbol("!!") do (i: In): + i.push("system!".newSym) + + def.symbol("&") do (i: In): + i.push("run".newSym) + + def.sigil("$") do (i: In): + i.push("get-env".newSym) + + def.sigil("!") do (i: In): + i.push("system".newSym) + + def.sigil("&") do (i: In): + i.push("run".newSym) + + when not defined(lite): + def.symbol("unzip") do (i: In): + let vals = i.expect("'sym", "'sym") + let dir = vals[0] + let f = vals[1] + nim_miniz.unzip(f.getString, dir.getString) + + def.symbol("zip") do (i: In): + let vals = i.expect("'sym", "quot") + let file = vals[0] + let files = vals[1] + nim_miniz.zip(files.qVal.mapIt(it.getString), file.getString) + + def.finalize("sys") +
M min.nimmin.nim

@@ -1,22 +1,23 @@

-when not defined(windows): - {.passL: "-rdynamic".} import streams, critbits, - parseopt, strutils, - os, - json, - sequtils, - algorithm, - logging, - dynlib + sequtils +when defined(mini): + import + core/minilogger +else: + import + json, + os, + algorithm, + dynlib, + logging, + packages/niftylogger import - packages/nimline/nimline, - packages/niftylogger, + core/baseutils, core/env, - core/consts, core/parser, core/value, core/scope,

@@ -30,118 +31,152 @@ lib/min_dict,

lib/min_num, lib/min_str, lib/min_logic, - lib/min_time, - lib/min_io, - lib/min_sys, - lib/min_fs + lib/min_time + +when not defined(mini): + import + packages/nimline/nimline, + lib/min_io, + lib/min_sys, + lib/min_fs -when not defined(lite): - import lib/min_http - import lib/min_net - import lib/min_crypto - import lib/min_math +when not defined(lite) and not defined(mini): + import + lib/min_http, + lib/min_net, + lib/min_crypto, + lib/min_math export + env, parser, interpreter, utils, - niftylogger, value, - scope, - min_lang + scope#, + #min_lang +when defined(mini): + export minilogger +else: + export niftylogger - - -#-d:ssl -p:. -d:noOpenSSLHacks --dynlibOverride:ssl- --dynlibOverride:crypto- -d:sslVersion:"(" --passL:-Lpath/to/openssl/lib -#--passL:-Bstatic --passL:-lssl --passL:-lcrypto --passL:-Bdynamic const PRELUDE* = "prelude.min".slurp.strip +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() -proc getExecs(): seq[string] = - var res = newSeq[string](0) - let getFiles = proc(dir: string) = - for c, s in walkDir(dir, true): - if (c == pcFile or c == pcLinkToFile) and not res.contains(s): - res.add s - getFiles(getCurrentDir()) - for dir in "PATH".getEnv.split(PathSep): - getFiles(dir) - res.sort(system.cmp) - return res + proc getExecs(): seq[string] = + var res = newSeq[string](0) + let getFiles = proc(dir: string) = + for c, s in walkDir(dir, true): + if (c == pcFile or c == pcLinkToFile) and not res.contains(s): + res.add s + getFiles(getCurrentDir()) + for dir in "PATH".getEnv.split(PathSep): + getFiles(dir) + res.sort(system.cmp) + return res -proc getCompletions(ed: LineEditor, symbols: seq[string]): seq[string] = - var words = ed.lineText.split(" ") - var word: string - if words.len == 0: - word = ed.lineText - else: - word = words[words.len-1] - if word.startsWith("’"): - return symbols.mapIt("’" & $it) - elif word.startsWith("~"): - return symbols.mapIt("~" & $it) - if word.startsWith("@"): - return symbols.mapIt("@" & $it) - if word.startsWith("#"): - return symbols.mapIt("#" & $it) - if word.startsWith(">"): - return symbols.mapIt(">" & $it) - if word.startsWith("*"): - return symbols.mapIt("*" & $it) - if word.startsWith("("): - return symbols.mapIt("(" & $it) - if word.startsWith("<"): - return toSeq(MINSYMBOLS.readFile.parseJson.pairs).mapIt("<" & $it[0]) - if word.startsWith("$"): - return toSeq(envPairs()).mapIt("$" & $it[0]) - if word.startsWith("!"): - return getExecs().mapIt("!" & $it) - if word.startsWith("&"): - return getExecs().mapIt("&" & $it) - if word.startsWith("\""): - var f = word[1..^1] - if f == "": - f = getCurrentDir().replace("\\", "/") - return toSeq(walkDir(f, true)).mapIt("\"$1" % it.path.replace("\\", "/")) - elif f.dirExists: - f = f.replace("\\", "/") - if f[f.len-1] != '/': - f = f & "/" - return toSeq(walkDir(f, true)).mapIt("\"$1$2" % [f, it.path.replace("\\", "/")]) + proc getCompletions(ed: LineEditor, symbols: seq[string]): seq[string] = + var words = ed.lineText.split(" ") + var word: string + if words.len == 0: + word = ed.lineText else: - var dir: string - if f.contains("/") or dir.contains("\\"): - dir = f.parentDir - let file = f.extractFileName - return toSeq(walkDir(dir, true)).filterIt(it.path.toLowerAscii.startsWith(file.toLowerAscii)).mapIt("\"$1/$2" % [dir, it.path.replace("\\", "/")]) + word = words[words.len-1] + if word.startsWith("’"): + return symbols.mapIt("’" & $it) + elif word.startsWith("~"): + return symbols.mapIt("~" & $it) + if word.startsWith("@"): + return symbols.mapIt("@" & $it) + if word.startsWith("#"): + return symbols.mapIt("#" & $it) + if word.startsWith(">"): + return symbols.mapIt(">" & $it) + if word.startsWith("*"): + return symbols.mapIt("*" & $it) + if word.startsWith("("): + return symbols.mapIt("(" & $it) + if word.startsWith("<"): + return toSeq(MINSYMBOLS.readFile.parseJson.pairs).mapIt("<" & $it[0]) + if word.startsWith("$"): + return toSeq(envPairs()).mapIt("$" & $it[0]) + if word.startsWith("!"): + return getExecs().mapIt("!" & $it) + if word.startsWith("&"): + return getExecs().mapIt("&" & $it) + if word.startsWith("\""): + var f = word[1..^1] + if f == "": + f = getCurrentDir().replace("\\", "/") + return toSeq(walkDir(f, true)).mapIt("\"$1" % it.path.replace("\\", "/")) + elif f.dirExists: + f = f.replace("\\", "/") + if f[f.len-1] != '/': + f = f & "/" + return toSeq(walkDir(f, true)).mapIt("\"$1$2" % [f, it.path.replace("\\", "/")]) + else: + var dir: string + if f.contains("/") or dir.contains("\\"): + dir = f.parentDir + let file = f.extractFileName + return toSeq(walkDir(dir, true)).filterIt(it.path.toLowerAscii.startsWith(file.toLowerAscii)).mapIt("\"$1/$2" % [dir, it.path.replace("\\", "/")]) + else: + 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: - dir = getCurrentDir() - return toSeq(walkDir(dir, true)).filterIt(it.path.toLowerAscii.startsWith(f.toLowerAscii)).mapIt("\"$1" % [it.path.replace("\\", "/")]) - return symbols + logging.warn("Unable to load dynamic library: " & libfile) + proc stdLib*(i: In) = - 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 i.dict_module - i.io_module i.logic_module i.num_module i.str_module - i.sys_module i.time_module - i.fs_module - when not defined(lite): + when not defined(mini): + i.sys_module + i.fs_module + i.io_module + when not defined(lite) and not defined(mini): i.crypto_module i.net_module i.math_module

@@ -152,34 +187,17 @@ else:

try: i.eval customPrelude.readFile, customPrelude except: - warn("Unable to process custom prelude code in $1" % customPrelude) - i.eval MINRC.readFile() - -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]) + when defined(mini): + minilogger.warn("Unable to process custom prelude code in $1" % customPrelude) else: - warn("[$1] Library does not contain symbol $2" % [libfile, modname]) - 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() - i.dynLib() + when not defined(mini): + i.dynLib() i.open(s, i.filename) discard i.parser.getToken() try:

@@ -196,16 +214,64 @@ result = i.interpret()

except: discard i.close() + +proc minFile*(filename: string, op = "interpret", main = true): seq[string] {.discardable.} -proc minStream(s: Stream, filename: string) = +proc compile*(i: In, s: Stream, main = true): seq[string] = + 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 dotindex = i.filename.rfind(".") + let nimFile = i.filename[0..dotindex-1] & ".nim" + if main: + 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 + 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")) + 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: + discard + 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.interpret(s) + i.pwd = filename.parentDirEx + if op == "interpret": + i.interpret(s) + newSeq[string](0) + else: + i.compile(s, main) -proc minString*(buffer: string) = +proc minStr*(buffer: string) = minStream(newStringStream(buffer), "input") -proc minFile*(filename: string) = +proc minFile*(filename: string, op = "interpret", main = true): seq[string] {.discardable.} = var fn = filename if not filename.endsWith(".min"): fn &= ".min"

@@ -214,56 +280,86 @@ 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") else: contents = fileLines.join("\n") - minStream(newStringStream(contents), fn) + minStream(newStringStream(contents), fn, op, main) -proc minFile*(file: File, filename="stdin") = +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) + minStream(stream, filename, op) + +when isMainModule: + + import + parseopt, + core/consts -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>" + var REPL = false + 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" + var uninstallOpt = "\n —-uninstall:<lib> Uninstall dynamic library file <lib>\n" + var iOpt = "\n -i, --interactive Start $1 shell (with advanced prompt)\n" + when defined(lite): + exeName = "litemin" + when defined(mini): + installOpt = "" + 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: - v = $item.val.val - echo " " & v & " :" & $item.key - if res.objType == "": - echo " ".repeat(n.len) & "}" + echo " ".repeat(n.len) & " ;" & 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]] + echo " $1" % [$i.stack[i.stack.len - 1]] -proc minRepl*(i: var MinInterpreter, simple = false) = - i.stdLib() - i.dynLib() - var s = newStringStream("") - i.open(s, "<repl>") - var line: string - var ed = initEditor(historyFile = MINHISTORY) - if simple: + 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")

@@ -275,58 +371,74 @@ line = stdin.readLine()

let r = i.interpret($line) if $line != "": i.printResult(r) - else: - 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) + + 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*(simple = false) = - var i = newMinInterpreter(filename = "<repl>") - i.minRepl(simple) - -when isMainModule: + proc minRepl*() = + var i = newMinInterpreter(filename = "<repl>") + i.minRepl() - var REPL = false - var SIMPLEREPL = false - var INSTALL = false - var UNINSTALL = false - var libfile = "" + proc minSimpleRepl*() = + var i = newMinInterpreter(filename = "<repl>") + i.minSimpleRepl() + - let usage* = """ $1 v$2 - a tiny concatenative shell and programming language + let usage* = """ $exe v$version - a tiny concatenative programming language (c) 2014-2020 Fabio Cevasco Usage: - min [options] [filename] + $exe [options] [filename] Arguments: - filename A $1 file to interpret (default: STDIN). - Options: - -—install:<lib> Install dynamic library file <lib> - —-uninstall:<lib> Uninstall dynamic library file <lib> - -e, --evaluate Evaluate a $1 program inline - -h, —-help Print this help - -i, —-interactive Start $1 shell (with advanced prompt) - -j, --interactive-simple Start $1 shell (without advanced prompt) + filename A $exe file to interpret or compile (default: STDIN). + Options:$installOpt$uninstallOpt + -c, --compile Compile the specified file + -e, --evaluate Evaluate a $exe program inline + -h, --help Print this help$iOpt + -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 - -v, —-version Print the program version""" % [pkgName, pkgVersion] + -v, —-version Print the program version""" % [ + "exe", exeName, + "version", pkgVersion, + "installOpt", installOpt, + "uninstallOpt", uninstallOpt, + "iOpt", iOpt + ] 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

@@ -334,12 +446,21 @@ if file == "":

file = key of cmdLongOption, cmdShortOption: case key: + of "compile", "c": + COMPILE = true + of "module-path", "m": + 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": if file == "": s = val

@@ -352,52 +473,63 @@ 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: discard else: 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 != "": - minString(s) + minStr(s) elif file != "": - minFile file - 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 or SIMPLEREPL: - minRepl(SIMPLEREPL) + minFile file, op + elif SIMPLEREPL: + minSimpleRepl() quit(0) else: - minFile stdin, "stdin" + minFile stdin, "stdin", op
M min.nimsmin.nims

@@ -10,33 +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): - switch("passL","-static") - # TODO", change once issue nim#15220 is resolved - switch("define", "noOpenSSLHacks") - switch("dynlibOverride", "ssl-") - switch("dynlibOverride", "crypto-") - switch("passL","-Lvendor/openssl/windows") - switch("passL","-lssl") - switch("passL","-lcrypto") - switch("passL","-lws2_32") - switch("define", "sslVersion:(") -else: - switch("dynlibOverride", "ssl") - switch("dynlibOverride", "crypto") - if defined(linux): - switch("passL","-static") - switch("passL","-Lvendor/openssl/linux") - switch("passL","-lssl") - switch("passL","-lcrypto") - elif defined(macosx): - switch("passL","-Bstatic") - switch("passL","-Lvendor/openssl/macosx") - switch("passL","-lssl") - switch("passL","-lcrypto") - switch("passL","-Bdynamic") +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.vimmin.vim

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

" Vim syntax file " Language: min " Maintainer: Fabio Cevasco -" Last Change: 07 Dec 2020 -" Version: 0.23.0 +" Last Change: 11 Dec 2020 +" Version: 0.24.0 if exists("b:current_syntax") finish

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

setl iskeyword=@,36-39,+,-,*,.,/,:,~,!,48-57,60-65,94-95,192-255 setl iskeyword+=^ -syntax keyword minDefaultSymbol ! !! != # $ % & ' * + - -> -inf . .. / : < <= = =% == => =~ > >< >= >> @ ROOT ^ accept acos aes all? and any? append apply apply-interpolate args asin ask atan atime bind bool boolean? call capitalize case cd ceil chmod choose chr clear-stack cleave close column-print concat confirm connect cons cos cosh cp cpu crypto ctime d2r datetime ddel ddup debug decode define defined? delete dequote dequote-and dequote-or dget dhas? dict dictionary? dip dir? dirname div dkeys download dpairs dpick drop dset dtype dup dvalues e encode env? error escape eval even? exists? exit expect fappend fatal file? filename filter find first flatten float float? floor foreach format-error fperms fread from-json from-semver from-yaml fs fsize fstats ftype fwrite get get-content get-env get-stack getchr gets hardlink harvest hidden? http id if import in? indent indexof inf infix-dequote info insert int integer? interpolate io join keep last length linrec listen lite? ln load load-symbol log10 log2 logic loglevel loglevel? lowercase ls ls-r map map-reduce match math md5 mkdir mod module mtime mv nan net newline nip not notice now num number? odd? opts or ord os over parse partition password pi pick pop pow pred prefix prefix-dequote prepend print print! prompt publish put-env putchr puts puts! quit quotation? quote quote-bind quote-define quote-map r2g raise random randomize raw-args read recv recv-line reduce regex reject remove remove-symbol repeat replace request rest reverse rm rmdir rolldown rollup round run save-symbol saved-symbols scope scope-sigils scope-symbols seal search semver-inc-major semver-inc-minor semver-inc-patch semver? send seq set set-stack set-type sha1 sha224 sha256 sha384 sha512 shorten sigils sin sinh sip size sleep slice socket sort source split spread sqrt stack start-server stop-server str string string? strip substr succ suffix sum swap swons symbols symlink symlink? sys system system! take tan tanh tap tap! tau tformat time timeinfo times timestamp titleize to-json to-semver to-timestamp to-yaml trunc try type type? unless unseal unzip uppercase version warn when which while with xor zip +syntax keyword minDefaultSymbol ! !! != # $ % & ' * + - -> -inf . .. / : < <= = =% == => =~ > >< >= >> @ ROOT ^ accept acos aes all? and any? append apply apply-interpolate args asin ask atan atime bind bool boolean? call capitalize case cd ceil chmod choose chr clear-stack cleave close column-print compiled? concat confirm connect cons cos cosh cp cpu crypto ctime d2r datetime ddel ddup debug decode define defined? delete dequote dequote-and dequote-or dget dhas? dict dictionary? dip dir? dirname div dkeys download dpairs dpick drop dset dtype dup dvalues e encode env? error escape eval even? exists? exit expect fappend fatal file? filename filter find first flatten float float? floor foreach format-error fperms fread from-json from-semver from-yaml fs fsize fstats ftype fwrite get get-content get-env get-stack getchr gets hardlink harvest hidden? http id if import in? indent indexof inf infix-dequote info insert int integer? interpolate io join keep last length linrec listen lite? ln load load-symbol log10 log2 logic loglevel loglevel? lowercase ls ls-r map map-reduce match math md5 mini? mkdir mod module mtime mv nan net newline nip not notice now num number? odd? opts or ord os over parse partition password pi pick pop pow pred prefix prefix-dequote prepend print print! prompt publish put-env putchr puts puts! quit quotation? quote quote-bind quote-define quote-map r2g raise random randomize raw-args read recv recv-line reduce regex reject remove remove-symbol repeat replace request rest reverse rm rmdir rolldown rollup round run save-symbol saved-symbols scope scope-sigils scope-symbols seal search semver-inc-major semver-inc-minor semver-inc-patch semver? send seq set set-stack set-type sha1 sha224 sha256 sha384 sha512 shorten sigils sin sinh sip size sleep slice socket sort source split spread sqrt stack start-server stop-server str string string? strip substr succ suffix sum swap swons symbols symlink symlink? sys system system! take tan tanh tap tap! tau tformat time timeinfo times timestamp titleize to-json to-semver to-timestamp to-yaml trunc try type type? unless unseal unzip uppercase version warn when which while with xor zip syntax match minDefaultSigil ;\<[/:@'~!?$%&=<>#^*#+]; contained syntax match minQuote ;\<['];
M min.ymlmin.yml

@@ -1,5 +1,5 @@

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

@@ -1,13 +1,7 @@

-* Added **apply-interpolate** (alias: **=%**) operator. -* Documented that it is possible also to interpolate with named placeholders, like this: `"Current Directory: $pwd" ("pwd" .) =%` -* Added **from-yaml** and **to-yaml** operators. Note that they only support dictionaries containing string values (primarily intended to access extremely simple YAML files containing just key/value pairs). -* Added **from-semver**, **to-semver**, **semver-major**, **semver-minor**, **semver-patch**, **semver** operators to manage version strings conforming to [Semantic Versioning](https://semver.org/) (additional labels are not yet supported). -* Automatically adding **.min** to files supplied to the min executable if they don't already end in .min. -* Fixed GC safety issues. -* Now statically linking libssl and libcrypto on all platform to provide HTTPS support out of the box. -* Now using a set of min tasks to perform a min release and other common operations. -* Added **escape** operator to escape quotes and special characters in a string. -* Added **quit** operator to exit with a 0 code. -* Addes **prefix** and **suffix** operators to prepens and append one string to another. - -**BONUS!** Are you using Visual Studio Code? check out the new [official extension](https://marketplace.visualstudio.com/items?itemName=h3rald.vscode-min-lang) for the min programming language! +* Added the possibility to "compile" min files into single executables. This is achieved by converting the specified min file to its raw Nim code equivalent and then calling the Nim compiler (which in turns calls the C compiler). +* Added **compiled?** symbol which returns true if the program has been compiled. +* 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 prelude.minprelude.min

@@ -1,20 +1,24 @@

; Imports 'str import -'io import 'logic import 'num import -'sys import 'stack import 'seq import 'dict import 'time import -'fs import -'lite? ( +(mini?) ( ( - 'crypto import - 'math import - 'net import - 'http import + 'io import + 'fs import + 'sys import + ) ROOT with +) unless +(lite? mini? or) ( + ( + 'crypto import + 'math import + 'net import + 'http import ) ROOT with ) unless ; Unseal prompt symbol
M run.minrun.min

@@ -3,17 +3,25 @@

;Capture a reference of default symbols before more are added symbols =min-symbols +2 :n-args +1 :taskspec-arg + +(compiled?) +( + 1 @n-args + 0 @taskspec-arg +) +when + ; Validation -(args size 2 <) ("No task specified" error 1 exit) when +(args size n-args <) ("No task specified" error 1 exit) when -args 1 get ":" split =taskspec +args taskspec-arg get ":" split =taskspec taskspec 0 get :task "default" :subtask (taskspec size 1 >) (taskspec 1 get @subtask) when "./tasks/$#.min" (task) =% :task-file - -(task-file exists? not) ("Task '$1' does not exist" (task) =% error 2 exit) when ; Load task module task-file load
M site/assets/styles/min-lang.csssite/assets/styles/min-lang.css

@@ -114,11 +114,11 @@ b, strong, dt {

font-weight: 400; } -code, pre, .kwd, .cmd, .file, .dir, kbd, .kbd, .min-terminal { +code, pre, .kwd, .cmd, .file, .dir, kbd, .kbd, .ext, .min-terminal { font-family: 'Inconsolata', monospace; } -.kwd, .kwd, .cmd, .file, .dir, kbd, .kbd { +.kwd, .kwd, .cmd, .file, .dir, .ext, kbd, .kbd { font-weight: bold; }
M site/contents/donate.mdsite/contents/donate.md

@@ -5,20 +5,11 @@ -----

min is an open source project developed by volunteers during their free time. Although min is and will always be free, we do accept donations! -You can donate any amount of money in the following ways: - - <div class="pure-g"> <section class="pure-u-1 pure-u-lg-1-3"> <div class="donate-content-area"> - <h3>BountySource Salt Campaign</h3> - <a class="pure-button pure-button-primary" href="https://salt.bountysource.com/teams/min-lang">Support min</a> - </div> - </section> - <section class="pure-u-1 pure-u-lg-1-3"> - <div class="donate-content-area"> <h3>PayPal</h3> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top"> <input type="hidden" name="cmd" value="_s-xclick">

@@ -28,11 +19,6 @@ <input type="image" src="https://www.paypalobjects.com/en_US/IT/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">

<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1"> </form> </div> - </section> - <section class="pure-u-1 pure-u-lg-1-3"> - <div class="donate-content-area"> - <h3>BitCoin</h3> - <code>1GoP7D2T5cf1ohXKnPV1GuxvoV5fxmFxKs</code> </section> <div>
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, an even more minimal executable file 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#}

@@ -46,7 +48,45 @@ * The {#link-module||http#}

* The {#link-module||math#} * The {#link-operator||sys||zip#} and {#link-operator||sys||unzip#} operators. -## Running then min Shell +#### -d:mini + +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 following operators: + * {#link-operator||lang||load#} + * {#link-operator||lang||read#} + * {#link-operator||lang||to-json#} + * {#link-operator||lang||from-json#} + * {#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. +* The **-m, \-\-module-path** option has no effect. +* No environment configuration files ([.minrc](class.file), [.min_symbols](class:file)) are used. + +## 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:

@@ -86,6 +126,38 @@

> %min-terminal% > > [$](class:prompt) cat myfile.min | min + +## Compiling a min Program + +min programs can be compiled to a single executable simply by specifying the `-c` (or `--compile`) flag when executing a min file: + +> %min-terminal% +> +> [$](class:prompt) min -c myfile.min + +Essentially, this will: + +1. Generate a [myfile.nim](class:file) containing the equivalent Nim code of your min program. +2. Call the Nim compiler to do the rest ;) + +If you want to pass any options to the Nim compiler (like `-d:release` for example) you can do so by using the `-n` (or `--passN`) option: + +> %min-terminal% +> +> [$](class:prompt) min -c myfile.min -n:-d:release + +Additionally, you can also use `-m:<path>` (or `--module-path`) to specify one path containing [.min](class:ext) files which will be compiled as well (but not executed) along with the specified file. Whenever a {#link-operator||lang||load#} symbol is used to load an external [.min](class:ext) file, it will attempt to load from the pre-loaded files first before searching the filesystem. + +For example, the following command executed in the root folder of the min project will compile [run.min](class:file) along with all [.min](class:ext) files included in the [task](class:dir) and its subfolders: + +> %min-terminal% +> +> [$](class:prompt) min -c run.min -m:tasks + +> %note% +> Note +> +> In order to successfully compile [.min](class.ext) files, Nim must be installed on your system and min must be installed via nimble. ## Syntax Highlighting
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

@@ -86,6 +86,9 @@ > > ((< 3) ("Smaller than 3" put!))

> > ((true) ("Exactly 3" put!)) > > ) case #} +{#op||compiled?||{{null}}||{{b}}|| +Returns {{t}} if the current program has been compiled.#} + {#op||define||{{any}} {{sl}}||{{null}}|| Defines a new symbol {{sl}}, containing the specified value (auto-quoted if not already a quotation).#}

@@ -158,6 +161,9 @@ > > %note%

> > 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}}.#}

@@ -269,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. #}
M site/settings.jsonsite/settings.json

@@ -6,5 +6,5 @@ "rules": "rules.min",

"temp": "temp", "templates": "templates", "title": "min language", - "version": "0.23.0" + "version": "0.24.0" }
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,26 +1,31 @@

#!/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 + " " :d-variant + "min" :o-variant + (variant length 0 >) ( + "-d:$# " (variant) =% @d-variant + "$#min" (variant) =% @o-variant + ) when "nim" required - "Building - $# (x64)" (target-os) =% notice - "nim c -d:release --cpu:amd64 --os:$# min" (target-os) =% ! + "Building $# - $# (x64)" (o-variant target-os) =% notice + "nim c -d:release --cpu:amd64 --os:$# $#-o:$# min" (target-os d-variant o-variant) =% puts ! {} target-os %os config /version %version + o-variant %exe pack ) :cz ( :vdata - "min" :exe - (vdata /os "windows" ==) ("min.exe" @exe) when - "min_v$version:_$os:_x64.zip" :fn + vdata /exe :exe + (vdata /os "windows" ==) ("$#.exe" (exe) =% @exe) when + "$exe:_v$version:_$os:_x64.zip" :fn fn vdata dpairs % ":" "" replace @fn "Compressing: $#" (fn) =% notice (exe) => fn zip

@@ -46,20 +51,44 @@ "Building - min.vim" notice

template ("date" date "version" min-version "symbols" symbols) =% out-file fwrite ) :build-vim -; Define module +; Module symbols {} ( - os cz + "lite" os cz +) %lite +( + "mini" os cz +) %mini +( + "" os cz ) %default ( - "linux" cz + "" "linux" cz ) %linux ( - "macosx" cz + "lite" "linux" cz +) %linux-lite +( + "mini" "linux" cz +) %linux-mini +( + "" "macosx" cz ) %macosx ( - "windows" cz + "lite" "macosx" cz +) %macosx-lite +( + "mini" "macosx" cz +) %macosx-mini +( + "" "windows" cz ) %windows +( + "lite" "windows" cz +) %windows-lite +( + "mini" "windows" cz +) %windows-mini 'build-guide %guide 'build-site %site (
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,18 +4,26 @@ "build" load

"github" load "ssh" load +; Module symbols {} ( build-tasks ^guide build-tasks ^site build-tasks ^vim build-tasks ^windows + build-tasks ^windows-lite + build-tasks ^windows-mini build-tasks ^linux + build-tasks ^linux-lite + build-tasks ^linux-mini build-tasks ^macosx + build-tasks ^macosx-lite + build-tasks ^macosx-mini github-tasks ^update github-tasks ^upload +) %default +( ssh-tasks ^build ssh-tasks ^h3rald - "All done!" notice -) %default +) %sites +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