Refactoring & fixed closure support.
@@ -33,51 +33,19 @@ proc debug*(i: In, value: string) =
if i.debugging: stderr.writeLine("-- " & value) -template withScope*(i: In, q: MinValue, body: untyped): untyped = +template withScope*(i: In, q: MinValue, res:ref MinScope, body: untyped): untyped = #i.debug "[scope] " & q.scope.fullname let origScope = i.scope - # TODO verify - if q.scope.parent.isNil: - q.scope.parent = i.scope - i.scope = q.scope + i.scope = q.scope.copy + i.scope.parent = origScope body - #i.debug "[scope] " & scope.fullname + res = i.scope i.scope = origScope -when false: - # TODO Remove - proc newScope*(i: In, id: string, q: var MinValue) = - q.scope = new MinScope - q.scope.name = id - q.scope.parent = i.scope - - template createScope*(i: In, id: string, q: MinValue, body: untyped): untyped = - q.scope = new MinScope - q.scope.name = id - q.scope.parent = i.scope - let scope = i.scope - i.scope = q.scope +template withScope*(i: In, q: MinValue, body: untyped): untyped = + var scope = newScopeRef(i.scope) + i.withScope(q, scope): body - i.scope = scope - - template withScope*(i: In, q: MinValue, body: untyped): untyped = - #i.debug "[scope] " & q.scope.fullname - let origScope = i.scope - i.scope = q.scope - body - #i.debug "[scope] " & scope.fullname - i.scope = origScope - - template addScope*(i: In, id: string, q: MinValue, body: untyped): untyped = - var added = new MinScope - added.name = id - if q.scope.isNil: - q.scope = i.scope - added.parent = q.scope - let scope = i.scope - i.scope = added - body - i.scope = scope proc newMinInterpreter*(debugging = false): MinInterpreter = var stack:MinStack = newSeq[MinValue](0)@@ -139,22 +107,26 @@ i.parser.close();
proc push*(i: In, val: MinValue) {.gcsafe.} -proc apply*(i: In, op: MinOperator, name="apply") = + +proc apply*(i: In, op: MinOperator, s: var ref MinScope, name="apply") = case op.kind of minProcOp: op.prc(i) of minValOp: if op.val.kind == minQuotation: var q = op.val - i.withScope(q): + i.withScope(q, s): #echo "a1: ", i.scope.fullname for e in q.qVal: i.push e else: i.push(op.val) +proc apply*(i: In, op: MinOperator, name="apply") = + var scope = newScopeRef(i.scope) + i.apply(op, scope) + proc push*(i: In, val: MinValue) = - i.debug val if val.kind == minSymbol: i.trace.add val if not i.evaluating:@@ -200,7 +172,7 @@ while i.parser.token != tkEof:
if i.trace.len == 0: i.stackcopy = i.stack try: - val = i.parser.parseMinValue(i.scope) + val = i.parser.parseMinValue(i) i.push val except MinRuntimeError: let msg = getCurrentExceptionMsg()@@ -219,10 +191,15 @@ raise MinTrappedException(msg: msg)
if i.stack.len > 0: return i.stack[i.stack.len - 1] -proc unquote*(i: In, name: string, q: var MinValue) = - i.withScope(q): +proc unquote*(i: In, name: string, q: var MinValue, scope: var ref MinScope) = + i.withScope(q, scope): for v in q.qVal: i.push v + + +proc unquote*(i: In, name: string, q: var MinValue) = + var scope = newScopeRef(i.scope) + i.unquote(name, q, scope) proc eval*(i: In, s: string, name="<eval>") = var i2 = i.copy(name)
@@ -152,6 +152,11 @@ proc newScopeRef*(parent: ref MinScope, name="scope"): ref MinScope =
new(result) result[] = newScope(parent, name) +proc fullname*(scope: ref MinScope): string = + result = scope.name + if not scope.parent.isNil: + result = scope.parent.fullname & ":" & result + proc open*(my: var MinParser, input: Stream, filename: string) = lexbase.open(my, input) my.filename = filename@@ -472,7 +477,7 @@ proc eat(p: var MinParser, token: MinTokenKind) =
if p.token == token: discard getToken(p) else: raiseParsing(p, tokToStr[token]) -proc parseMinValue*(p: var MinParser, parentScope: ref MinScope): MinValue = +proc parseMinValue*(p: var MinParser, i: In): MinValue = #echo p.a, " (", p.token, ")" case p.token of tkTrue:@@ -493,12 +498,15 @@ result = MinValue(kind: minFloat, floatVal: parseFloat(p.a), column: p.getColumn, line: p.lineNumber)
discard getToken(p) of tkBracketLe: var q = newSeq[MinValue](0) - var scope = newScopeRef(parentScope, "quotation") + var oldscope = i.scope + var newscope = newScopeRef(i.scope, "quotation") + i.scope = newscope discard getToken(p) while p.token != tkBracketRi: - q.add p.parseMinValue(scope) + q.add p.parseMinValue(i) eat(p, tkBracketRi) - result = MinValue(kind: minQuotation, qVal: q, column: p.getColumn, line: p.lineNumber, scope: scope) + i.scope = oldscope + result = MinValue(kind: minQuotation, qVal: q, column: p.getColumn, line: p.lineNumber, scope: newscope) of tkSymbol: result = MinValue(kind: minSymbol, symVal: p.a, column: p.getColumn, line: p.lineNumber) p.a = ""
@@ -4,11 +4,12 @@ critbits
import parser -proc fullname*(scope: ref MinScope): string = - result = scope.name - if not scope.parent.isNil: - result = scope.parent.fullname & ":" & result - +proc copy*(s: ref MinScope): ref MinScope = + var scope = newScope(s.parent, "copy($1)" % s.name) + scope.symbols = s.symbols + new(result) + result[] = scope + proc getSymbol*(scope: ref MinScope, key: string): MinOperator = if scope.symbols.hasKey(key): return scope.symbols[key]
@@ -9,7 +9,7 @@ interpreter
# Library methods -proc printKeys(syms: CritBitTree[MinOperator]) = +proc printKeys*(syms: CritBitTree[MinOperator]) = for key, value in syms.pairs: echo " - $1" % key
@@ -174,7 +174,7 @@ q1 = @[q1].newVal(i.scope)
symbol = sym.getString if not symbol.match "^[a-zA-Z0-9_][a-zA-Z0-9/!?+*._-]*$": raiseInvalid("Symbol identifier '$1' contains invalid characters." % symbol) - i.debug "[define] (scope: $1) $2 = $3" % [i.scope.name, symbol, $q1] + i.debug "[define] (scope: $1) $2 = $3" % [i.scope.fullname, 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)@@ -208,16 +208,15 @@ # i.unquote("<scope>", code)
# i.push @[code].newVal(i.scope) .symbol("current-scope") do (i: In): - var code: MinValue - i.reqQuotation code - i.push code.scope.fullname.newVal + i.push i.scope.fullname.newVal .symbol("module") do (i: In): var code, name: MinValue i.reqStringLike name i.reqQuotation code code.filename = i.filename - i.unquote("<module>", code) + i.unquote("<module>", code, code.scope) + i.debug("[module] $1 ($2 symbols)" % [name.getString, $code.scope.symbols.len]) i.scope.symbols[name.getString] = MinOperator(kind: minValOp, val: @[code].newVal(i.scope)) #.symbol("scope?") do (i: In):@@ -228,17 +227,21 @@ # i.push true.newVal
# else: # i.push false.newVal + .symbol("import") do (i: In): var mdl, rawName: MinValue var name: string i.reqStringLike rawName name = rawName.getString - i.apply(i.scope.getSymbol(name)) + var op = i.scope.getSymbol(name) + i.apply(op) i.reqQuotation mdl + #echo "Import: $1" % name + #echo ">>", mdl, "<<" # TODO Remove echos - #echo name, " - ", mdl.scope.symbols.len + i.debug("[import] Importing: $1 ($2 symbols)" % [name, $mdl.scope.symbols.len]) for sym, val in mdl.scope.symbols.pairs: - i.debug "[import] $1:$2" % [i.scope.name, sym] + i.debug "[import] $1:$2" % [i.scope.fullname, sym] #echo "[import] $1:$2" % [i.scope.name, sym] i.scope.symbols[sym] = val@@ -278,11 +281,9 @@ i.reqTwoQuotations qscope, qprog
if qscope.qVal.len > 0: # System modules are empty quotes and don't need to be unquoted i.unquote("<with-scope>", qscope) - let scope = i.scope - i.scope = qscope.scope - for v in qprog.qVal: - i.push v - i.scope = scope + i.withScope(qscope): + for v in qprog.qVal: + i.push v .symbol("publish") do (i: In): var qscope, str: MinValue@@ -290,7 +291,16 @@ i.reqQuotationAndStringLike qscope, str
let sym = str.getString if qscope.scope.symbols.hasKey(sym) and qscope.scope.symbols[sym].sealed: raiseUndefined("Attempting to redefine sealed symbol '$1' on scope '$2'" % [sym, qscope.scope.name]) - qscope.scope.symbols[sym] = i.scope.getSymbol(sym) + let scope = i.scope + i.debug("[publish] (scope: $1) -> $2" % [i.scope.fullname, sym]) + let op = proc(i: In) {.gcsafe, closure.} = + let origscope = i.scope + i.scope = scope + i.evaluating = true + i.push sym.newSym + i.evaluating = false + i.scope = origscope + qscope.scope.symbols[sym] = MinOperator(kind: minProcOp, prc: op) .symbol("source") do (i: In): var s: MinValue@@ -544,6 +554,7 @@ var prog, list: MinValue
i.reqTwoQuotations prog, list for litem in list.qVal: i.push litem + #i.debug("[foreach] $1" % prog.scope.fullname) i.unquote("<foreach-quotation>", prog) .symbol("times") do (i: In):@@ -807,9 +818,6 @@ i.push("seal".newSym)
.symbol(":") do (i: In): i.push("define".newSym) - - .symbol("@") do (i: In): - i.push("bind".newSym) .symbol("!") do (i: In): i.push("system".newSym)
@@ -28,6 +28,23 @@ (symbols "a" contains false ==) assert
5 :five (symbols "five" contains) assert + + ( + ((1 2 3 4 5 6)) :test-data + + ( + :item + "_$1" (item) % :namesym + (item dup *) namesym define + namesym ROOT publish + ) :def + + test-data (def) foreach + + _2 _5 _1 + + + 30 == + ) assert + ~five (symbols "five" contains false ==) assert@@ -45,12 +62,14 @@ ('mymath import symbols "myplus" contains) assert
('mymath import 2 3 myplus 5 ==) assert + (2 3 mymath ^myplus 5 ==) assert + ; Extend an existing scope ;( ; ('mymath import - ; (-) :myminus) => @mymath - ; 5 2 mymath ^myminus 3 ==) assert - ; + ; (-) :myminus + ; ) @mymath 5 2 mymath ^myminus 3 ==) assert + ; ;(mymath inspect ("myminus" "myplus") ==) assert ;((":mysigil" concat) ', sigil ,test "test:mysigil" ==) assert@@ -128,7 +147,7 @@ (() 1 at)
(1) ) try 1 ==) assert - ((a b +) (4 :a 5 :b) with 9 ==) assert + ;((a b +) (4 :a 5 :b) with 9 ==) assert ("{\"a\": 1, \"b\": 2.3}" from-json ((a 1) (b 2.3)) ==) assert