Now actually restoring stack contents on error.
@@ -116,11 +116,14 @@ stderr.writeLine("$1 [$2,$3] `$4`: Error - $5" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, i.currSym.symVal, message])
quit(100) template execute(i: In, body: stmt) {.immediate.}= + let stack = i.copystack try: body except MinRuntimeError: + i.stack = stack stderr.writeLine("$1 [$2,$3]: $4" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, getCurrentExceptionMsg()]) except: + i.stack = stack i.error(getCurrentExceptionMsg()) proc open*(i: In, stream:Stream, filename: string) =@@ -140,7 +143,12 @@ let sigil = "" & symbol[0]
let symbolProc = i.scope.getSymbol(symbol) if symbolProc.isNotNil: if i.unsafe: - symbolProc(i) + let stack = i.copystack + try: + symbolProc(i) + except: + i.stack = stack + raise else: i.execute: i.symbolProc@@ -150,7 +158,12 @@ if symbol.len > 1 and sigilProc.isNotNil:
let sym = symbol[1..symbol.len-1] i.stack.add(MinValue(kind: minString, strVal: sym)) if i.unsafe: - sigilProc(i) + let stack = i.copystack + try: + sigilProc(i) + except: + i.stack = stack + raise else: i.execute: i.sigilProc
@@ -114,85 +114,67 @@
proc reqBool*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isBool: - i.push a raiseInvalid("A bool value is required on the stack") proc reqTwoBools*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not a.isBool or not b.isBool: - i.push b - i.push a raiseInvalid("Two bool values are required on the stack") proc reqInt*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isInt: - i.push a raiseInvalid("An integer is required on the stack") proc reqTwoInts*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not a.isInt or not b.isInt: - i.push b - i.push a raiseInvalid("Two integers are required on the stack") proc reqQuotation*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isQuotation: - i.push a raiseInvalid("A quotation is required on the stack") proc reqIntAndQuotation*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not (a.isInt and b.isQuotation): - i.push b - i.push a raiseInvalid("An integer and a quotation are required on the stack") proc reqTwoNumbers*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not (a.isNumber and b.isNumber): - i.push b - i.push a raiseInvalid("Two numbers are required on the stack") proc reqTwoNumbersOrStrings*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not (a.isString and b.isString or a.isNumber and b.isNumber): - i.push b - i.push a raiseInvalid("Two numbers or two strings are required on the stack") proc reqString*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isString: - i.push a raiseInvalid("A string is required on the stack") proc reqStringOrQuotation*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isQuotation and not a.isString: - i.push a raiseInvalid("A quotation or a string is required on the stack") proc reqStringOrSymbol*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isStringLike: - i.push a raiseInvalid("A symbol or a string is required on the stack") proc reqTwoStrings*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not a.isString or not b.isString: - i.push b - i.push a raiseInvalid("Two strings are required on the stack") proc reqThreeStrings*(i: var MinInterpreter, a, b, c: var MinValue) =@@ -200,25 +182,18 @@ a = i.pop
b = i.pop c = i.pop if not a.isString or not b.isString or not c.isString: - i.push c - i.push b - i.push a raiseInvalid("Three strings are required on the stack") proc reqTwoQuotations*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not a.isQuotation or not b.isQuotation: - i.push b - i.push a raiseInvalid("Two quotations are required on the stack") proc reqTwoQuotationsOrStrings*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not (a.isQuotation and b.isQuotation or a.isString and b.isString): - i.push b - i.push a raiseInvalid("Two quotations or two strings are required on the stack") proc reqThreeQuotations*(i: var MinInterpreter, a, b, c: var MinValue) =@@ -226,9 +201,6 @@ a = i.pop
b = i.pop c = i.pop if not a.isQuotation or not b.isQuotation or not c.isQuotation: - i.push c - i.push b - i.push a raiseInvalid("Three quotations are required on the stack") proc reqFourQuotations*(i: var MinInterpreter, a, b, c, d: var MinValue) =@@ -237,22 +209,15 @@ b = i.pop
c = i.pop d = i.pop if not a.isQuotation or not b.isQuotation or not c.isQuotation or not d.isQuotation: - i.push d - i.push c - i.push b - i.push a raiseInvalid("Four quotations are required on the stack") proc reqTwoSimilarTypesNonSymbol*(i: var MinInterpreter, a, b: var MinValue) = a = i.pop b = i.pop if not ((a.kind == a.kind or (a.isNumber and a.isNumber)) and not a.isSymbol): - i.push b - i.push a raiseInvalid("Two non-symbol values of similar type are required on the stack") proc reqObject*(i: var MinInterpreter, t: string, a: var MinValue) = a = i.pop if not a.isQuotation or a.objType.isNil or a.objType != t: - i.push a raiseInvalid("A $1 object is required" % [t])
@@ -51,8 +51,6 @@ symbol = q2.strVal
elif q2.isQuotation and q2.qVal.len == 1 and q2.qVal[0].kind == minSymbol: symbol = q2.qVal[0].symVal else: - i.push rawQ1 - i.push q2 raiseInvalid("The top quotation must contain only one symbol value") i.debug "[define] " & symbol & " = " & $q1 i.scope.symbols[symbol] = proc(i: In) =@@ -70,15 +68,11 @@ symbol = q2.getString
elif q2.isQuotation and q2.qVal.len == 1 and q2.qVal[0].kind == minSymbol: symbol = q2.qVal[0].symVal else: - i.push rawQ1 - i.push q2 raiseInvalid("The top quotation must contain only one symbol value") i.debug "[bind] " & symbol & " = " & $q1 let res = i.scope.setSymbol(symbol) do (i: In): i.push q1.qVal if not res: - i.push rawQ1 - i.push q2 raiseUndefined("Attempting to bind undefined symbol: " & symbol) .symbol("delete") do (i: In):@@ -86,7 +80,6 @@ var sym: MinValue
i.reqStringOrSymbol sym let res = i.scope.delSymbol(sym.getString) if not res: - i.push sym raiseUndefined("Attempting to delete undefined symbol: " & sym.getString) .symbol("scope") do (i: In):@@ -119,20 +112,14 @@ if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol:
var symbol = q1.qVal[0].symVal if symbol.len == 1: if i.scope.getSigil(symbol).isNotNil: - i.push q2 - i.push q1 raiseInvalid("Sigil '$1' already exists" % [symbol]) i.scope.sigils[symbol] = proc(i: In) = i.evaluating = true i.push q2.qVal i.evaluating = false else: - i.push q2 - i.push q1 raiseInvalid("A sigil can only have one character") else: - i.push q2 - i.push q1 raiseInvalid("The top quotation must contain only one symbol value") .symbol("eval") do (i: In):@@ -154,27 +141,20 @@ i.reqTwoQuotations symbols, target
let vals = symbols.qVal var q: MinValue if vals.len == 0: - i.push target - i.push symbols raiseInvalid("No symbol to call") let origScope = i.scope i.scope = target.scope for c in 0..vals.len-1: if not vals[c].isStringLike: - i.push target - i.push symbols raiseInvalid("Quotation must contain only symbols or strings") let symbol = vals[c].getString let qProc = i.scope.getSymbol(symbol) if qProc.isNil: - i.push target - i.push symbols raiseUndefined("Symbol '$1' not found in scope '$2'" % [symbol, i.scope.fullname]) qProc(i) if vals.len > 1 and c < vals.len-1: q = i.pop if not q.isQuotation: - i.push q raiseInvalid("Unable to evaluate symbol '$1'" % [symbol]) i.scope = q.scope i.scope = origScope@@ -196,7 +176,6 @@ .symbol("try") do (i: In):
var prog: MinValue i.reqQuotation prog if prog.qVal.len == 0: - i.push prog raiseInvalid("Quotation must contain at least one element") var code = prog.qVal[0] var final, catch: MinValue@@ -209,7 +188,6 @@ if prog.qVal.len > 2:
final = prog.qVal[2] hasFinally = true if (not code.isQuotation) or (hasCatch and not catch.isQuotation) or (hasFinally and not final.isQuotation): - i.push prog raiseInvalid("Quotation must contain at one quotation") i.unsafe = true try:@@ -242,7 +220,6 @@ results[][c] = i.stack[0]
var results = newSeq[MinValue](q.qVal.len) for c in 0..q.qVal.high: if not q.qVal[c].isQuotation: - i.push q raiseInvalid("Item #$1 is not a quotation" % [$(c+1)]) var i2 = i.copy(i.filename) var res: MinStack = newSeq[MinValue](0)@@ -285,12 +262,10 @@ var q: MinValue
i.reqStringOrQuotation q if q.isQuotation: if q.qVal.len == 0: - i.push q raiseOutOfBounds("Quotation is empty") i.push q.qVal[0] elif q.isString: if q.strVal.len == 0: - i.push q raiseOutOfBounds("String is empty") i.push newVal($q.strVal[0])@@ -299,12 +274,10 @@ var q: MinValue
i.reqStringOrQuotation q if q.isQuotation: if q.qVal.len == 0: - i.push q raiseOutOfBounds("Quotation is empty") i.push newVal(q.qVal[1..q.qVal.len-1]) elif q.isString: if q.strVal.len == 0: - i.push q raiseOutOfBounds("String is empty") i.push newVal(q.strVal[1..q.strVal.len-1])@@ -335,8 +308,6 @@ .symbol("at") do (i: In):
var index, q: MinValue i.reqIntAndQuotation index, q if q.qVal.len-1 < index.intVal: - i.push q - i.push index raiseOutOfBounds("Insufficient items in quotation") i.push q.qVal[index.intVal.int]@@ -368,8 +339,6 @@ .symbol("times") do (i: In):
var t, prog: MinValue i.reqIntAndQuotation t, prog if t.intVal < 1: - i.push prog - i.push t raiseInvalid("A non-zero natural number is required") for c in 1..t.intVal: i.unquote("<times-quotation>", prog)
@@ -14,17 +14,13 @@ var q: MinValue
i.reqQuotation q # (ipv4 stream tcp) if q.qVal.len < 3 or not (q.qVal[0].isSymbol and q.qVal[1].isSymbol and q.qVal[2].isSymbol): - i.push q raiseInvalid("Quotation must contain three symbols for <domain> <type> <protocol>") let vals = q.qVal if not ["ipv4", "ipv6"].contains(vals[0].symVal): - i.push q raiseInvalid("Domain symbol must be 'ipv4' or 'ipv6'") if not ["stream", "dgram"].contains(vals[1].symVal): - i.push q raiseInvalid("Type symbol must be 'stream' or 'dgram'") if not ["tcp", "udp"].contains(vals[2].symVal): - i.push q raiseInvalid("Protocol symbol must be 'tcp' or 'udp'") var domain: Domain
@@ -68,7 +68,7 @@ (false false != false ==) assert
(1 1 != false ==) assert ("aaa" "aaa" != false ==) assert (1.0 1 != false ==) assert - ((1 2 3.0) (1.0 2 3) != false ==) assert + ((1 2 3.0) (1 2 3) != false ==) assert (("a" "b") ("a" "b") != false ==) assert (("a" "b" 3) ("a" "b" 4) !=) assert ((1 "b" 3 myrandomsymbol) (1 "b" 3.0 myrandomsymbol) != false ==) assert