all repos — min @ 51b45c837088518a7a9d006ccb21a447b3b042a7

A small but practical concatenative programming language.

Now parsing dictionary values; introduced apply for dictionaries.
h3rald h3rald@h3rald.com
Sun, 12 Aug 2018 11:49:16 +0200
commit

51b45c837088518a7a9d006ccb21a447b3b042a7

parent

b4eebd2ecdd98d7056b1388f4f28bf7a2af00b25

M core/interpreter.nimcore/interpreter.nim

@@ -124,6 +124,43 @@ i.parser.close();

proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".} +proc call*(i: In, q: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + var i2 = newMinInterpreter("<call>") + i2.trace = i.trace + i2.scope = i.scope + try: + i2.withScope(): + for v in q.qVal: + i2.push v + except: + i.currSym = i2.currSym + i.trace = i2.trace + raise + return i2.stack.newVal + +proc callValue*(i: In, v: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + var i2 = newMinInterpreter("<call-value>") + i2.trace = i.trace + i2.scope = i.scope + try: + i2.withScope(): + i2.push v + except: + i.currSym = i2.currSym + i.trace = i2.trace + raise + return i2.stack[0] + +proc applyDict*(i: In, val: MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= + # Assuming val is a dictionary + var v = newDict(i.scope) + for item in val.scope.symbols.pairs: + v.scope.symbols[item.key] = item.val + for item in v.dVal.pairs: + var value = item.val.val + v.scope.symbols[item.key] = MinOperator(kind: minValOp, val: i.callValue(value), sealed: false) + return v + proc apply*(i: In, op: MinOperator) {.gcsafe, extern:"min_exported_symbol_$1".}= if op.kind == minProcOp: op.prc(i)

@@ -131,19 +168,15 @@ else:

if op.val.kind == minQuotation: var newscope = newScopeRef(i.scope) i.withScope(newscope): - for e in op.val.quot: + for e in op.val.qVal: i.push e else: i.push(op.val) proc dequote*(i: In, q: var MinValue) {.extern:"min_exported_symbol_$1".}= - if q.kind == minDictionary: - i.withScope(q.scope): - for v in q.quot: - i.push v - elif q.kind == minQuotation: + if q.kind == minQuotation: i.withScope(): - for v in q.quot: + for v in q.qVal: i.push v else: i.push(q)

@@ -154,7 +187,7 @@ i2.trace = i.trace

i2.scope = i.scope try: i2.withScope(): - for v in q.quot: + for v in q.qVal: if (v.kind == minQuotation): var v2 = v i2.dequote(v2)

@@ -166,20 +199,6 @@ i.trace = i2.trace

raise i.push i2.stack.newVal -proc call*(i: In, q: var MinValue): MinValue {.gcsafe, extern:"min_exported_symbol_$1".}= - var i2 = newMinInterpreter("<call>") - i2.trace = i.trace - i2.scope = i.scope - try: - i2.withScope(): - for v in q.quot: - i2.push v - except: - i.currSym = i2.currSym - i.trace = i2.trace - raise - return i2.stack.newVal - proc push*(i: In, val: MinValue) {.gcsafe, extern:"min_exported_symbol_$1".}= if val.kind == minSymbol: i.debug(val)

@@ -198,12 +217,6 @@ else:

raiseUndefined("Undefined symbol '$1'" % [val.symVal]) discard i.trace.pop else: - if (val.kind == minDictionary): - if val.scope.symbols.len == 0: - var v = val - i.dequote(v) - # Clear the initial quotation; only used when parsing a dictionary for the first time - v.quot = @[] i.stack.add(val) proc pop*(i: In): MinValue {.extern:"min_exported_symbol_$1".}=

@@ -229,7 +242,7 @@ i.stackcopy = i.stack

try: val = i.parser.parseMinValue(i) if parseOnly: - q.quot.add val + q.qVal.add val else: i.push val except MinRuntimeError:
M core/parser.nimcore/parser.nim

@@ -74,7 +74,6 @@ case kind*: MinKind

of minInt: intVal*: BiggestInt of minFloat: floatVal*: BiggestFloat of minDictionary: - q*: seq[MinValue] scope*: ref MinScope obj*: pointer objType*: string

@@ -141,22 +140,6 @@ raiseInvalid("dVal - Dictionary expected, got " & $v.kind)

if v.scope.isNil: return CritBitTree[MinOperator]() return v.scope.symbols - -proc quot*(v: MinValue): var seq[MinValue] {.inline, extern:"min_exported_symbol_$1".}= - if v.kind != minDictionary and v.kind != minQuotation: - raiseInvalid("quot - Dictionary or quotation expected, got " & $v.kind) - if v.kind == minDictionary: - return v.q - else: - return v.qVal - -proc `quot=`*(v: var MinValue, quot: seq[MinValue]) {.inline, extern:"min_exported_symbol_qValEq".}= - if v.kind != minDictionary and v.kind != minQuotation: - raiseInvalid("quot= - Dictionary or quotation expected, got " & $v.kind) - if v.kind == minDictionary: - v.q = quot - else: - v.qVal = quot const errorMessages: array[MinParserError, string] = [

@@ -549,47 +532,6 @@ proc eat(p: var MinParser, token: MinTokenKind) {.extern:"min_exported_symbol_$1".}=

if p.token == token: discard getToken(p) else: raiseParsing(p, tokToStr[token]) -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) - discard getToken(p) - of tkFalse: - result = MinValue(kind: minBool, boolVal: false) - discard getToken(p) - of tkString: - result = MinValue(kind: minString, strVal: p.a) - p.a = "" - discard getToken(p) - of tkInt: - result = MinValue(kind: minInt, intVal: parseint(p.a)) - discard getToken(p) - of tkFloat: - result = MinValue(kind: minFloat, floatVal: parseFloat(p.a)) - discard getToken(p) - of tkBracketLe: - var q = newSeq[MinValue](0) - discard getToken(p) - while p.token != tkBracketRi: - q.add p.parseMinValue(i) - eat(p, tkBracketRi) - result = MinValue(kind: minQuotation, qVal: q)#, scope: newscope) - of tkBraceLe: - var q = newSeq[MinValue](0) - discard getToken(p) - while p.token != tkBraceRi: - q.add p.parseMinValue(i) - eat(p, tkBraceRi) - 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) - p.a = "" - discard getToken(p) - else: - raiseUndefined(p, "Undefined value: '"&p.a&"'") - result.filename = p.filename - proc `$`*(a: MinValue): string {.inline, extern:"min_exported_symbol_$1".}= case a.kind: of minBool:

@@ -616,8 +558,6 @@ if i.val.kind == minProcOp:

v = "<native>" else: v = $i.val.val - if (not i.val.quotation): - v = v[1 .. v.len-2] d = d & v & " :" & $i.key & " " if not a.objType.isNil: d = d & ";" & a.objType

@@ -650,13 +590,64 @@ if i.val.kind == minProcOp:

v = "<native>" else: v = $i.val.val - if (not i.val.quotation): - v = v[1 .. v.len-2] d = d & v & " :" & $i.key & " " if not a.objType.isNil: d = d & ";" & a.objType 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) + discard getToken(p) + of tkFalse: + result = MinValue(kind: minBool, boolVal: false) + discard getToken(p) + of tkString: + result = MinValue(kind: minString, strVal: p.a) + p.a = "" + discard getToken(p) + of tkInt: + result = MinValue(kind: minInt, intVal: parseint(p.a)) + discard getToken(p) + of tkFloat: + result = MinValue(kind: minFloat, floatVal: parseFloat(p.a)) + discard getToken(p) + of tkBracketLe: + var q = newSeq[MinValue](0) + discard getToken(p) + while p.token != tkBracketRi: + q.add p.parseMinValue(i) + eat(p, tkBracketRi) + result = MinValue(kind: minQuotation, qVal: q)#, scope: newscope) + of tkBraceLe: + var scope = newScopeRef(nil) + var val: MinValue + discard getToken(p) + while p.token != tkBraceRi: + let v = p.parseMinValue(i) + if val.isNil: + val = v + elif v.kind == minSymbol: + let key = v.symVal + if key[0] == ':': + scope.symbols[key[1 .. key.len-1]] = MinOperator(kind: minValOp, val: val, sealed: false) + val = nil + else: + raiseInvalid("Invalid dictionary key: " & key) + else: + raiseInvalid("Invalid dictionary key: " & $v) + eat(p, tkBraceRi) + result = MinValue(kind: minDictionary, scope: scope) + of tkSymbol: + result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber, filename: p.filename) + p.a = "" + discard getToken(p) + else: + raiseUndefined(p, "Undefined value: '"&p.a&"'") + result.filename = p.filename proc print*(a: MinValue) {.extern:"min_exported_symbol_$1".}= stdout.write($$a)
M core/utils.nimcore/utils.nim

@@ -39,7 +39,6 @@ proc finalize*(scope: ref MinScope, name: string = "") {.extern:"min_exported_symbol_$1".}=

var mdl = newDict(scope) mdl.scope = scope mdl.objType = "module" - mdl.quot = @[] let op = proc(i: In) {.closure.} = i.evaluating = true i.push mdl

@@ -49,25 +48,21 @@ 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: raiseInvalid("Value is not a dictionary") if q.dVal[s.getString].kind == minProcOp: raiseInvalid("Key '$1' is set to a native value that cannot be retrieved." % [s.getString]) - var val = q.dVal[s.getString].val - result = i.call(val) - if result.qVal.len == 1: - result = result.qVal[0] + result = q.dVal[s.getString].val proc dget*(i: In, q: MinValue, s: string): MinValue {.extern:"min_exported_symbol_$1_2".}= if not q.isDictionary: raiseInvalid("Value is not a dictionary") if q.dVal[s].kind == minProcOp: raiseInvalid("Key $1 is set to a native value that cannot be retrieved." % [s]) - var val = q.dVal[s].val - result = i.call(val) - if result.qVal.len == 1 and result.qVal[0].kind != minQuotation: - result = result.qVal[0] + result = q.dVal[s].val proc dhas*(q: MinValue, s: MinValue): bool {.extern:"min_exported_symbol_$1".}= if not q.isDictionary:

@@ -95,8 +90,6 @@ proc dset*(i: In, p: var MinValue, s: MinValue, m: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1".}=

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

@@ -104,8 +97,6 @@ proc dset*(i: In, p: var MinValue, s: string, m: MinValue): MinValue {.discardable, extern:"min_exported_symbol_$1_2".}=

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

@@ -122,11 +113,7 @@ var r = newSeq[MinValue](0)

for item in q.dVal.values: if item.kind == minProcOp: raiseInvalid("Dictionary contains native values that cannot be accessed.") - var v = item.val - var val = i.call(v) - if val.qVal.len == 1 and val.qVal[0].kind != minQuotation: - val = val.qVal[0] - r.add val + r.add item.val return r.newVal # JSON interop

@@ -175,7 +162,7 @@ var first = $key[0]

var rest = "" if key.len > 1: rest = key[1..key.len-1] - first = sgregex.replace(first, "[^a-zA-Z_]", "_") + 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
M core/value.nimcore/value.nim

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

proc newVal*(s: cstring): MinValue {.extern:"min_exported_symbol_$1_2".}= return MinValue(kind: minString, strVal: $s) -proc newVal*(q: seq[MinValue], dictionary = false): MinValue {.extern:"min_exported_symbol_$1_3".}= - if dictionary: - return MinValue(kind: minDictionary, q: q) - else: - return MinValue(kind: minQuotation, qVal: q) +proc newVal*(q: seq[MinValue]): MinValue {.extern:"min_exported_symbol_$1_3".}= + return MinValue(kind: minQuotation, qVal: q) proc newVal*(i: BiggestInt): MinValue {.extern:"min_exported_symbol_$1_4".}= return MinValue(kind: minInt, intVal: i)

@@ -48,7 +45,7 @@ proc newVal*(s: bool): MinValue {.extern:"min_exported_symbol_$1_6".}=

return MinValue(kind: minBool, boolVal: s) proc newDict*(parentScope: ref MinScope): MinValue {.extern:"min_exported_symbol_$1".}= - return MinValue(kind: minDictionary, q: newSeq[MinValue](0), scope: newScopeRef(parentScope)) + return MinValue(kind: minDictionary, scope: newScopeRef(parentScope)) proc newSym*(s: string): MinValue {.extern:"min_exported_symbol_$1".}= return MinValue(kind: minSymbol, symVal: s)
M lib/min_lang.nimlib/min_lang.nim

@@ -25,9 +25,12 @@ let vals = i.expect("int")

quit(vals[0].intVal.int) def.symbol("apply") do (i: In): - let vals = i.expect("quot") + let vals = i.expect("quot|dict") var prog = vals[0] - i.apply prog + if prog.kind == minQuotation: + i.apply prog + else: + i.push i.applyDict(prog) def.symbol("symbols") do (i: In): var q = newSeq[MinValue](0)

@@ -236,6 +239,7 @@ let q = vals[1]

let s = symbol.getString let origScope = i.scope i.scope = q.scope + i.scope.parent = origScope let sym = i.scope.getSymbol(s) i.apply(sym) i.scope = origScope

@@ -270,7 +274,7 @@ if err.dhas("line"):

list.add i.dget(err, "line") if err.dhas("column"): list.add i.dget(err, "column") - if list.len <= 1: + if list.len <= 3: msg = "$1" % $$list[0] else: msg = "$3($4,$5) `$2`: $1" % [$$list[0], $$list[1], $$list[2], $$list[3], $$list[4]]

@@ -312,8 +316,9 @@ let err = sgregex.replace($e.name, ":.+$", "")

res.objType = "error" i.dset(res, "error", err.newVal) i.dset(res, "message", e.msg.newVal) - i.dset(res, "symbol", i.currSym) - i.dset(res, "filename", i.currSym.filename.newVal) + if i.currSym.getString != "": # TODO investigate when this happens + i.dset(res, "symbol", i.currSym) + i.dset(res, "filename", i.currSym.filename.newVal) i.dset(res, "line", i.currSym.line.newVal) i.dset(res, "column", i.currSym.column.newVal) i.push res
M min.nimmin.nim

@@ -215,11 +215,7 @@ if res.isQuotation and res.qVal.len > 1:

echo "{$1} -> (" % n for item in res.qVal: echo " " & $item - if res.objType.isNil: - echo " ".repeat(n.len) & " )" - else: - echo " ".repeat(n.len) & " ;" & res.objType - echo " ".repeat(n.len) & " )" + echo " ".repeat(n.len) & " )" elif res.isDictionary and res.dVal.len > 1: echo "{$1} -> {" % n for item in res.dVal.pairs:

@@ -228,8 +224,6 @@ if item.val.kind == minProcOp:

v = "<native>" else: v = $item.val.val - if (not item.val.quotation): - v = v[1 .. v.len-2] echo " " & v & " :" & $item.key if res.objType.isNil: echo " ".repeat(n.len) & " }"
M tests/dict.mintests/dict.min

@@ -21,9 +21,20 @@ ({1 :a 2 :b 3 :c} dvalues (1 2 3) ==) assert

({1 :a 2 :b 3 :c 4 :d} ("b" "c") dpick {2 :b 3 :c} ==) assert - (2 2 {'+ :plus} ^plus 4 ==) assert + (2 2 {+ :plus} ^plus 4 ==) assert + + (2 {(2 3 +) :sum} /sum -> + 7 ==) assert - (2 {(2 3 +) :sum} /sum + 7 ==) assert + ( + {} :archives + ({"a" :a 2 :b} {"aa" :a 4 :b} {"aaa" :a 6 :b}) + ( + :article + article /a :code + {code :code} => + archives swap code dset @archives + ) foreach + archives {{"a" :code} :a {"aa" :code} :aa {"aaa" :code} :aaa} ==) assert report clear-stack
M tests/test.mintests/test.min

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

;; test module { - 'OK =ok + 'OK :ok (" " print!) :padding