all repos — min @ 53f5cbd09de52317cd20e1f31bbb8469616ce35d

A small but practical concatenative programming language.

Implemented proper stack trace.
h3rald h3rald@h3rald.com
Fri, 21 Oct 2016 22:21:28 +0200
commit

53f5cbd09de52317cd20e1f31bbb8469616ce35d

parent

5cf62b0c55e466b9ff9f47aadeaeb88f8993f541

4 files changed, 57 insertions(+), 43 deletions(-)

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

@@ -3,7 +3,8 @@ streams,

strutils, critbits, os, - oids + oids, + algorithm import value, parser

@@ -51,9 +52,6 @@ if not scope.isNil and scope.symbols.hasKey(key):

if scope.symbols[key].sealed: raiseInvalid("Symbol '$1' is sealed." % key) scope.symbols[key] = value - # TODO remove - #if key == "total": - # echo "[bind] (scope: $1) $2 -> $3" % [scope.fullname, key, $value] result = true else: # Go up the scope chain and attempt to find the symbol

@@ -124,15 +122,10 @@ i.scope = added

body i.scope = scope -proc copystack*(i: MinInterpreter): MinStack = - return i.stack - #var s = newSeq[MinValue](0) - #for i in i.stack: - # s.add i - #return s - proc newMinInterpreter*(debugging = false): MinInterpreter = - var st:MinStack = newSeq[MinValue](0) + var stack:MinStack = newSeq[MinValue](0) + var trace:MinStack = newSeq[MinValue](0) + var stackcopy:MinStack = newSeq[MinValue](0) var pr:MinParser var scope = new MinScope scope.name = "ROOT"

@@ -140,7 +133,9 @@ var i:MinInterpreter = MinInterpreter(

filename: "input", pwd: "", parser: pr, - stack: st, + stack: stack, + trace: trace, + stackcopy: stackcopy, scope: scope, debugging: debugging, unsafe: false,

@@ -153,30 +148,48 @@ proc copy*(i: MinInterpreter, filename: string): MinInterpreter =

result = newMinInterpreter(debugging = i.debugging) result.filename = filename result.pwd = filename.parentDir - result.stack = i.copystack + result.stack = i.stack + result.trace = i.trace + result.stackcopy = i.stackcopy result.scope = i.scope result.currSym = MinValue(column: 1, line: 1, kind: minSymbol, symVal: "") -proc error(i: In, message: string) = - if i.currSym.filename.isNil or i.currSym.filename == "": - stderr.writeLine("`$1`: $2" % [i.currSym.symVal, message]) +proc formatError(sym: MinValue, message: string): string = + if sym.filename.isNil or sym.filename == "": + return "(!) `$1`: $2" % [sym.symVal, message] else: - stderr.writeLine("$1 [$2,$3] `$4`: $5" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, i.currSym.symVal, message]) - #if not i.currSym.parent.isNil: - # i.currSym = i.currSym.parent - # i.error("...") + return "(!) $1:$2,$3 `$4`: $5" % [sym.filename, $sym.line, $sym.column, sym.symVal, message] + +proc formatTrace(sym: MinValue): string = + if sym.filename.isNil or sym.filename == "": + return " - [native] in symbol: $1" % [sym.symVal] + else: + return " - $1:$2,$3 in symbol: $4" % [sym.filename, $sym.line, $sym.column, sym.symVal] + +proc stackTrace(i: In) = + var trace = i.trace + trace.reverse() + for sym in trace: + stderr.writeLine sym.formatTrace + +proc error(i: In, message: string) = + stderr.writeLine i.currSym.formatError(message) template execute(i: In, body: untyped) = - let stack = i.copystack + var stack: MinStack + if i.trace.len == 0: + i.stackcopy = i.stack try: body except MinRuntimeError: - i.stack = stack - stderr.writeLine("$1 [$2,$3]: $4" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, getCurrentExceptionMsg()]) + i.stack = i.stackcopy + stderr.writeLine("(!) $1:$2,$3 $4" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, getCurrentExceptionMsg()]) + i.stackTrace i.halt = true except: - i.stack = stack + i.stack = i.stackcopy i.error(getCurrentExceptionMsg()) + i.stackTrace i.halt = true proc open*(i: In, stream:Stream, filename: string) =

@@ -207,8 +220,8 @@ if i.halt:

return i.debug val if val.kind == minSymbol: + i.trace.add val if not i.evaluating: - val.parent = i.currSym i.currSym = val let symbol = val.symVal let sigil = "" & symbol[0]

@@ -216,7 +229,7 @@ let found = i.scope.hasSymbol(symbol)

if found: let sym = i.scope.getSymbol(symbol) if i.unsafe: - let stack = i.copystack + let stack = i.stack try: i.apply(sym) except:

@@ -232,7 +245,7 @@ let sig = i.scope.getSigil(sigil)

let sym = symbol[1..symbol.len-1] i.stack.add(MinValue(kind: minString, strVal: sym)) if i.unsafe: - let stack = i.copystack + let stack = i.stack try: i.apply(sig) except:

@@ -243,6 +256,7 @@ i.execute:

i.apply(sig) else: raiseUndefined("Undefined symbol '$1' in scope '$2'" % [val.symVal, i.scope.fullname]) + discard i.trace.pop else: i.stack.add(val)

@@ -281,6 +295,8 @@ var i2 = i.copy(name)

i2.open(newStringStream(s), name) discard i2.parser.getToken() i2.interpret() + i.trace = i2.trace + i.stackcopy = i2.stackcopy i.stack = i2.stack i.scope = i2.scope except:

@@ -295,6 +311,8 @@ var i2 = i.copy(s)

i2.open(newStringStream(s.readFile), s) discard i2.parser.getToken() i2.interpret() + i.trace = i2.trace + i.stackcopy = i2.stackcopy i.stack = i2.stack i.scope = i2.scope except:
M core/parser.nimcore/parser.nim

@@ -60,7 +60,6 @@ MinValueObject* = object

line*: int column*: int filename*: string - parent*: MinValue case kind*: MinKind of minInt: intVal*: BiggestInt of minFloat: floatVal*: BiggestFloat

@@ -91,6 +90,8 @@ MinStack* = seq[MinValue]

In* = var MinInterpreter MinInterpreter* = object stack*: MinStack + trace*: MinStack + stackcopy*: MinStack pwd*: string scope*: ref MinScope parser*: MinParser
M lib/min_lang.nimlib/min_lang.nim

@@ -170,9 +170,6 @@ i.debug "[define] (scope: $1) $2 = $3" % [i.scope.name, symbol, $q1]

if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: raiseUndefined("Attempting to redefine sealed symbol '$1' on scope '$2'" % [symbol, i.scope.name]) i.newScope("$1#$2" % [symbol, $genOid()], q1) - # TODO remove - #if symbol == "total": - # echo "[define] (scope: $1) $2 -> $3" % [i.scope.fullname, symbol, $q1, $q1.qVal[0]] i.scope.symbols[symbol] = MinOperator(kind: minValOp, val: q1, sealed: false) .symbol("bind") do (i: In):

@@ -184,12 +181,6 @@ if not q1.isQuotation:

q1 = @[q1].newVal symbol = sym.getString i.debug "[bind] " & symbol & " = " & $q1 - # TODO remove - #if symbol == "total": - # echo "[bind] (scope: $1) $2 " % [i.scope.fullname, symbol, $q1] - #let p = proc (i: In) = - # i.push q1.qVal - #let res = i.scope.setSymbol(symbol, MinOperator(kind: minProcOp, prc: p)) let res = i.scope.setSymbol(symbol, MinOperator(kind: minValOp, val: q1)) if not res: raiseUndefined("Attempting to bind undefined symbol: " & symbol)

@@ -522,7 +513,7 @@

.symbol("ifte") do (i: In): var fpath, tpath, check: MinValue i.reqThreeQuotations fpath, tpath, check - var stack = i.copystack + var stack = i.stack i.unquote("<ifte-check>", check) let res = i.pop i.stack = stack
M tests/lang.mintests/lang.min

@@ -1,10 +1,14 @@

'test load 'test import -"lang" describe +newline +symbols size :total-symbols +sigils size :total-sigils +"Total Symbols: $1" (total-symbols) % puts! +"Total Sigils: $1" (total-sigils) % puts! +newline - "Total Symbols: " print! symbols size puts! - " Total Sigils: " print! sigils size puts! +"lang" describe (debug? false ==) assert (1 id 1 ==) assert

@@ -119,7 +123,7 @@ (1 at)

) try "Test Message" ==) assert ( - (("test" (1 2) :)) try get-stack ("test" (1 2)) ==) assert + (("test" (1 2) :)) try get-stack ("test") ==) assert ( (