all repos — min @ 07f25b45167f854ccf7e72939d63f703cc1039c6

A small but practical concatenative programming language.

Refactoring & minor performance improvements.
h3rald h3rald@h3rald.com
Sun, 08 Jul 2018 11:55:07 +0200
commit

07f25b45167f854ccf7e72939d63f703cc1039c6

parent

c54da464f0b1d2eea3619b8cb6e75c9187322866

M core/interpreter.nimcore/interpreter.nim

@@ -5,6 +5,7 @@ critbits,

os, algorithm, ospaths, + times, logging import value,

@@ -129,9 +130,8 @@ op.prc(i)

else: if op.val.kind == minQuotation: var newscope = newScopeRef(i.scope) - var q = op.val i.withScope(newscope): - for e in q.quot: + for e in op.val.quot: i.push e else: i.push(op.val)

@@ -164,7 +164,7 @@ except:

i.currSym = i2.currSym i.trace = i2.trace raise - i.push i2.stack.newVal(i.scope) + i.push i2.stack.newVal proc call*(i: In, q: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= var i2 = newMinInterpreter("<call>")

@@ -178,7 +178,7 @@ except:

i.currSym = i2.currSym i.trace = i2.trace raise - return i2.stack.newVal(i2.scope) + return i2.stack.newVal proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".}= if val.kind == minSymbol:

@@ -220,7 +220,9 @@ raiseEmptyStack()

proc interpret*(i: In, parseOnly=false): MinValue {.discardable, extern:"min_exported_symbol_$1".} = var val: MinValue - var q = newSeq[MinValue](0).newVal(i.scope) + var q: MinValue + if parseOnly: + q = newSeq[MinValue](0).newVal while i.parser.token != tkEof: if i.trace.len == 0: i.stackcopy = i.stack
M core/parser.nimcore/parser.nim

@@ -10,6 +10,8 @@ critbits,

math, logging +var DEV* = false + type MinTokenKind* = enum tkError,

@@ -568,25 +570,17 @@ result = MinValue(kind: minFloat, floatVal: parseFloat(p.a))

discard getToken(p) of tkBracketLe: var q = newSeq[MinValue](0) - #var oldscope = i.scope - #var newscope = newScopeRef(i.scope) - #i.scope = newscope discard getToken(p) while p.token != tkBracketRi: q.add p.parseMinValue(i) eat(p, tkBracketRi) - #i.scope = oldscope result = MinValue(kind: minQuotation, qVal: q)#, scope: newscope) of tkBraceLe: var q = newSeq[MinValue](0) - #var oldscope = i.scope - #var newscope = newScopeRef(i.scope) - #i.scope = newscope discard getToken(p) while p.token != tkBraceRi: q.add p.parseMinValue(i) eat(p, tkBraceRi) - #i.scope = oldscope result = MinValue(kind: minDictionary, q: q, scope: newScopeRef(nil)) of tkSymbol: result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber, filename: p.filename)

@@ -596,7 +590,7 @@ else:

raiseUndefined(p, "Undefined value: '"&p.a&"'") result.filename = p.filename -proc `$`*(a: MinValue): string {.extern:"min_exported_symbol_$1".}= +proc `$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= case a.kind: of minBool: return $a.boolVal

@@ -630,7 +624,7 @@ d = d & ";" & a.objType

d = d.strip & "}" return d -proc `$$`*(a: MinValue): string {.extern:"min_exported_symbol_$1".}= +proc `$$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= case a.kind: of minBool: return $a.boolVal

@@ -706,7 +700,7 @@ if q.isTypedDictionary:

return q.objType == t return false -proc `==`*(a: MinValue, b: MinValue): bool {.extern:"min_exported_symbol_eqeq".}= +proc `==`*(a: MinValue, b: MinValue): bool {.inline, extern:"min_exported_symbol_eqeq".}= if not (a.kind == b.kind or (a.isNumber and b.isNumber)): return false if a.kind == minSymbol and b.kind == minSymbol:
M core/scope.nimcore/scope.nim

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

import strutils, + sequtils, ## critbits import parser

@@ -11,13 +12,13 @@ scope.sigils = s.sigils

new(result) result[] = scope -proc getSymbol*(scope: ref MinScope, key: string): MinOperator {.extern:"min_exported_symbol_$1".}= +proc getSymbol*(scope: ref MinScope, key: string, acc=0): MinOperator {.extern:"min_exported_symbol_$1".}= if scope.symbols.hasKey(key): return scope.symbols[key] - elif not scope.parent.isNil: - return scope.parent.getSymbol(key) else: - raiseUndefined("Symbol '$1' not found." % key) + if scope.parent.isNil: + raiseUndefined("Symbol '$1' not found." % key) + return scope.parent.getSymbol(key, acc + 1) proc hasSymbol*(scope: ref MinScope, key: string): bool {.extern:"min_exported_symbol_$1".}= if scope.isNil:
M core/utils.nimcore/utils.nim

@@ -96,7 +96,7 @@ if not p.isDictionary:

raiseInvalid("Value is not a dictionary") var q = m if not q.isQuotation: - q = @[q].newVal(i.scope) + q = @[q].newVal p.scope.symbols[s.getString] = MinOperator(kind: minValOp, val: q, sealed: false) return p

@@ -105,7 +105,7 @@ if not p.isDictionary:

raiseInvalid("Value is not a dictionary") var q = m if not q.isQuotation: - q = @[q].newVal(i.scope) + q = @[q].newVal p.scope.symbols[s] = MinOperator(kind: minValOp, val: q, sealed: false) return p

@@ -114,7 +114,7 @@ # Assumes q is a dictionary

var r = newSeq[MinValue](0) for i in q.dVal.keys: r.add newVal(i) - return r.newVal(i.scope) + return r.newVal proc values*(i: In, q: MinValue): MinValue {.extern:"min_exported_symbol_$1".}= # Assumes q is a dictionary

@@ -127,7 +127,7 @@ var val = i.call(v)

if val.qVal.len == 1 and val.qVal[0].kind != minQuotation: val = val.qVal[0] r.add val - return r.newVal(i.scope) + return r.newVal # JSON interop

@@ -155,7 +155,7 @@

proc fromJson*(i: In, json: JsonNode): MinValue {.extern:"min_exported_symbol_$1".}= case json.kind: of JNull: - result = newSeq[MinValue](0).newVal(i.scope) + result = newSeq[MinValue](0).newVal of JBool: result = json.getBool.newVal of JInt:

@@ -183,7 +183,7 @@ of JArray:

var res = newSeq[MinValue](0) for value in json.items: res.add i.fromJson(value) - return res.newVal(i.scope) + return res.newVal # Validators

@@ -218,6 +218,11 @@ return true

return false proc expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] {.extern:"min_exported_symbol_$1".}= + if not DEV: + result = newSeq[MinValue](0) + for e in elements: + result.add i.pop + return let stack = elements.reverse.join(" ") let sym = i.currSym.getString var valid = newSeq[string](0)

@@ -247,6 +252,8 @@ valid.add element

proc reqQuotationOfQuotations*(i: var MinInterpreter, a: var MinValue) {.extern:"min_exported_symbol_$1".}= a = i.pop + if not DEV: + return if not a.isQuotation: raiseInvalid("A quotation is required on the stack") for s in a.qVal:

@@ -255,6 +262,8 @@ raiseInvalid("A quotation of quotations is required on the stack")

proc reqQuotationOfNumbers*(i: var MinInterpreter, a: var MinValue) {.extern:"min_exported_symbol_$1".}= a = i.pop + if not DEV: + return if not a.isQuotation: raiseInvalid("A quotation is required on the stack") for s in a.qVal:

@@ -263,6 +272,8 @@ raiseInvalid("A quotation of numbers is required on the stack")

proc reqQuotationOfSymbols*(i: var MinInterpreter, a: var MinValue) {.extern:"min_exported_symbol_$1".}= a = i.pop + if not DEV: + return if not a.isQuotation: raiseInvalid("A quotation is required on the stack") for s in a.qVal:

@@ -272,16 +283,22 @@

proc reqTwoNumbersOrStrings*(i: var MinInterpreter, a, b: var MinValue) {.extern:"min_exported_symbol_$1".}= a = i.pop b = i.pop + if not DEV: + return if not (a.isString and b.isString or a.isNumber and b.isNumber): raiseInvalid("Two numbers or two strings are required on the stack") proc reqStringOrQuotation*(i: var MinInterpreter, a: var MinValue) {.extern:"min_exported_symbol_$1".}= a = i.pop + if not DEV: + return if not a.isQuotation and not a.isString: raiseInvalid("A quotation or a string is required on the stack") proc reqTwoQuotationsOrStrings*(i: var MinInterpreter, a, b: var MinValue) {.extern:"min_exported_symbol_$1".}= a = i.pop b = i.pop + if not DEV: + return if not (a.isQuotation and b.isQuotation or a.isString and b.isString): raiseInvalid("Two quotations or two strings are required on the stack")
M core/value.nimcore/value.nim

@@ -32,11 +32,11 @@

proc newVal*(s: cstring): MinValue {.extern:"min_exported_symbol_$1_2".}= return MinValue(kind: minString, strVal: $s) -proc newVal*(q: seq[MinValue], parentScope: ref MinScope, dictionary = false): MinValue {.extern:"min_exported_symbol_$1_3".}= +proc newVal*(q: seq[MinValue], dictionary = false): MinValue {.extern:"min_exported_symbol_$1_3".}= if dictionary: - return MinValue(kind: minDictionary, q: q)#, scope: newScopeRef(parentScope)) + return MinValue(kind: minDictionary, q: q) else: - return MinValue(kind: minQuotation, qVal: q)#, scope: newScopeRef(parentScope)) + return MinValue(kind: minQuotation, qVal: q) proc newVal*(i: BiggestInt): MinValue {.extern:"min_exported_symbol_$1_4".}= return MinValue(kind: minInt, intVal: i)
M lib/min_http.nimlib/min_http.nim

@@ -96,7 +96,7 @@ i.push qreq

i.dequote qhandler let qres = i.pop var body = "".newVal - var rawHeaders = newSeq[MinValue]().newVal(i.scope) + var rawHeaders = newSeq[MinValue]().newVal var v = "1.1" var status = 200.newVal if not qres.isDictionary():
M lib/min_lang.nimlib/min_lang.nim

@@ -36,7 +36,7 @@ while not scope.isNil:

for s in scope.symbols.keys: q.add s.newVal scope = scope.parent - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("defined?") do (i: In): let vals = i.expect("'sym")

@@ -49,7 +49,7 @@ while not scope.isNil:

for s in scope.sigils.keys: q.add s.newVal scope = scope.parent - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("scope-symbols") do (i: In): let vals = i.expect("dict")

@@ -57,7 +57,7 @@ let m = vals[0]

var q = newSeq[MinValue](0) for s in m.scope.symbols.keys: q.add s.newVal - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("scope-sigils") do (i: In): let vals = i.expect("dict")

@@ -65,7 +65,7 @@ let m = vals[0]

var q = newSeq[MinValue](0) for s in m.scope.sigils.keys: q.add s.newVal - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("lite?") do (i: In): i.push defined(lite).newVal

@@ -98,7 +98,7 @@ var q1 = vals[1] # existing (auto-quoted)

var symbol: string var isQuot = true if not q1.isQuotation: - q1 = @[q1].newVal(i.scope) + q1 = @[q1].newVal isQuot = false symbol = sym.getString if not symbol.match "^[a-zA-Z_][a-zA-Z0-9/!?+*._-]*$":

@@ -115,7 +115,7 @@ var q1 = vals[1] # existing (auto-quoted)

var symbol: string var isQuot = true if not q1.isQuotation: - q1 = @[q1].newVal(i.scope) + q1 = @[q1].newVal isQuot = false symbol = sym.getString info "[bind] $1 = $2" % [symbol, $q1]

@@ -325,7 +325,7 @@

def.symbol("quote") do (i: In): let vals = i.expect("a") let a = vals[0] - i.push @[a].newVal(i.scope) + i.push @[a].newVal def.symbol("dequote") do (i: In): let vals = i.expect("quot")

@@ -509,7 +509,7 @@ var q = newSeq[MinValue](0)

let json = MINSYMBOLS.readFile.parseJson for k,v in json.pairs: q.add k.newVal - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("remove-symbol") do (i: In): let vals = i.expect("'sym")

@@ -539,7 +539,7 @@ def.symbol("quote-bind") do (i: In):

let vals = i.expect("string", "a") let s = vals[0] let m = vals[1] - i.push @[m].newVal(i.scope) + i.push @[m].newVal i.push s i.push "bind".newSym

@@ -547,7 +547,7 @@ def.symbol("quote-define") do (i: In):

let vals = i.expect("string", "a") let s = vals[0] let m = vals[1] - i.push @[m].newVal(i.scope) + i.push @[m].newVal i.push s i.push "define".newSym

@@ -560,7 +560,7 @@ of cmdArgument:

args.add key.newVal else: discard - i.push args.newVal(i.scope) + i.push args.newVal def.symbol("opts") do (i: In): var opts = newDict(i.scope)

@@ -579,12 +579,12 @@ def.symbol("raw-args") do (i: In):

var args = newSeq[MinValue](0) for par in commandLineParams(): args.add par.newVal - i.push args.newVal(i.scope) + i.push args.newVal def.symbol("expect") do (i: In): var q: MinValue i.reqQuotationOfSymbols q - i.push(i.expect(q.qVal.mapIt(it.getString())).reversed.newVal(i.scope)) + i.push(i.expect(q.qVal.mapIt(it.getString())).reversed.newVal) # Converters

@@ -644,7 +644,7 @@

def.sigil("'") do (i: In): let vals = i.expect("string") let s = vals[0] - i.push(@[s.strVal.newSym].newVal(i.scope)) + i.push(@[s.strVal.newSym].newVal) def.sigil(":") do (i: In): i.push("define".newSym)
M lib/min_seq.nimlib/min_seq.nim

@@ -18,7 +18,7 @@ let vals = i.expect("quot", "quot")

let q1 = vals[0] let q2 = vals[1] let q = q2.qVal & q1.qVal - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("first") do (i: In): let vals = i.expect("quot")

@@ -39,19 +39,19 @@ let vals = i.expect("quot")

let q = vals[0] if q.qVal.len == 0: raiseOutOfBounds("Quotation is empty") - i.push q.qVal[1..q.qVal.len-1].newVal(i.scope) + i.push q.qVal[1..q.qVal.len-1].newVal def.symbol("append") do (i: In): let vals = i.expect("quot", "a") let q = vals[0] let v = vals[1] - i.push newVal(q.qVal & v, i.scope) + i.push newVal(q.qVal & v) def.symbol("prepend") do (i: In): let vals = i.expect("quot", "a") let q = vals[0] let v = vals[1] - i.push newVal(v & q.qVal, i.scope) + i.push newVal(v & q.qVal) def.symbol("get") do (i: In): let vals = i.expect("int", "quot")

@@ -85,7 +85,7 @@ for x in 0..q.qVal.len-1:

if x == ix: continue res.add q.qVal[x] - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("insert") do (i: In): let vals = i.expect("int", "a", "quot")

@@ -100,7 +100,7 @@ for x in 0..q.qVal.len-1:

if x == ix: res.add val res.add q.qVal[x] - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("size") do (i: In): let vals = i.expect("quot")

@@ -122,15 +122,15 @@ for litem in list.qVal:

i.push litem i.dequote(prog) res.add i.pop - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("quote-map") do (i: In): let vals = i.expect("quot") let list = vals[0] var res = newSeq[MinValue](0) for litem in list.qVal: - res.add @[litem].newVal(i.scope) - i.push res.newVal(i.scope) + res.add @[litem].newVal + i.push res.newVal def.symbol("reverse") do (i: In): let vals = i.expect("quot")

@@ -138,7 +138,7 @@ let q = vals[0]

var res = newSeq[MinValue](0) for c in countdown(q.qVal.len-1, 0): res.add q.qVal[c] - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("filter") do (i: In): let vals = i.expect("quot", "quot")

@@ -151,7 +151,7 @@ i.dequote(filter)

var check = i.pop if check.isBool and check.boolVal == true: res.add e - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("reject") do (i: In): let vals = i.expect("quot", "quot")

@@ -164,7 +164,7 @@ i.dequote(filter)

var check = i.pop if check.isBool and check.boolVal == false: res.add e - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("any?") do (i: In): let vals = i.expect("quot", "quot")

@@ -211,7 +211,7 @@ else:

raiseInvalid("Predicate quotation must return a boolean value") var qList = list.qVal sort[MinValue](qList, minCmp) - i.push qList.newVal(i.scope) + i.push qList.newVal def.symbol("shorten") do (i: In): let vals = i.expect("int", "quot")

@@ -219,7 +219,7 @@ let n = vals[0]

let q = vals[1] if n.intVal > q.qVal.len: raiseInvalid("Quotation is too short") - i.push q.qVal[0..n.intVal.int-1].newVal(i.scope) + i.push q.qVal[0..n.intVal.int-1].newVal def.symbol("take") do (i: In): let vals = i.expect("int", "quot")

@@ -228,7 +228,7 @@ let q = vals[1]

var nint = n.intVal if nint > q.qVal.len: nint = q.qVal.len - i.push q.qVal[0..nint-1].newVal(i.scope) + i.push q.qVal[0..nint-1].newVal def.symbol("drop") do (i: In): let vals = i.expect("int", "quot")

@@ -237,7 +237,7 @@ let q = vals[1]

var nint = n.intVal if nint > q.qVal.len: nint = q.qVal.len - i.push q.qVal[nint..q.qVal.len-1].newVal(i.scope) + i.push q.qVal[nint..q.qVal.len-1].newVal def.symbol("find") do (i: In): let vals = i.expect("quot", "quot")

@@ -300,8 +300,8 @@ if res.isBool and res.boolVal == true:

tseq.add el else: fseq.add el - i.push tseq.newVal(i.scope) - i.push fseq.newVal(i.scope) + i.push tseq.newVal + i.push fseq.newVal def.symbol("slice") do (i: In): let vals = i.expect("int", "int", "quot")

@@ -315,7 +315,7 @@ raiseOutOfBounds("Index out of bounds")

elif fn < st: raiseInvalid("End index must be greater than start index") let rng = q.qVal[st.int..fn.int] - i.push rng.newVal(i.scope) + i.push rng.newVal def.symbol("harvest") do (i: In): let vals = i.expect("quot")

@@ -325,7 +325,7 @@ for el in q.qVal:

if el.isQuotation and el.qVal.len == 0: continue res.add el - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("flatten") do (i: In): let vals = i.expect("quot")

@@ -337,6 +337,6 @@ for el2 in el.qVal:

res.add el2 else: res.add el - i.push res.newVal(i.scope) + i.push res.newVal def.finalize("seq")
M lib/min_stack.nimlib/min_stack.nim

@@ -17,7 +17,7 @@ while i.stack.len > 0:

discard i.pop def.symbol("get-stack") do (i: In): - i.push i.stack.newVal(i.scope) + i.push i.stack.newVal def.symbol("set-stack") do (i: In): let vals = i.expect("quot")
M lib/min_str.nimlib/min_str.nim

@@ -47,7 +47,7 @@ q.add ($c).newVal

else: for e in s.split(sep): q.add e.newVal - i.push q.newVal(i.scope) + i.push q.newVal def.symbol("join") do (i: In): let vals = i.expect("'sym", "quot")

@@ -107,7 +107,7 @@ 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(i.scope) + i.push res.newVal def.symbol("match") do (i: In): let vals = i.expect("string", "string")

@@ -133,7 +133,7 @@ let results = str.strVal =~ reg.strVal

var res = newSeq[MinValue](0) for r in results: res.add(r.newVal) - i.push res.newVal(i.scope) + i.push res.newVal def.symbol("=~") do (i: In): i.push("regex".newSym)
M lib/min_sys.nimlib/min_sys.nim

@@ -40,7 +40,7 @@ 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(i.scope) + i.push list.newVal def.symbol("ls-r") do (i: In): let vals = i.expect("'sym")

@@ -48,7 +48,7 @@ let a = vals[0]

var list = newSeq[MinValue](0) for i in walkDirRec(a.getString): list.add newVal(i.unix) - i.push list.newVal(i.scope) + i.push list.newVal def.symbol("system") do (i: In): let vals = i.expect("'sym")
M min.nimmin.nim

@@ -289,6 +289,8 @@ -—install:<lib> Install dynamic library file <lib>

—-uninstall:<lib> Uninstall dynamic library file <lib> -l, --log Set log level (debug|info|notice|warn|error|fatal) Default: notice + -d, --dev Run in development mode + Default: false -e, --evaluate Evaluate a $1 program inline -h, —-help Print this help -v, —-version Print the program version

@@ -306,6 +308,8 @@ if file == "":

file = key of cmdLongOption, cmdShortOption: case key: + of "dev", "d": + DEV = true of "log", "l": if file == "": var val = val
M tests/lang.mintests/lang.min

@@ -104,7 +104,7 @@ (/error)

) try "TestError" ==) assert ( - (("test" (1 2) :)) try get-stack ("test") ==) assert + (("test" °test :)) try get-stack ("test") ==) assert ( (