all repos — min @ 34b86a45897b8a5ed75dded2a200a3c22f040a8b

A small but practical concatenative programming language.

refactor(diagnostics) Diagnostics improvements & other improvements.
h3rald h3rald@h3rald.com
Sun, 29 May 2016 16:13:07 +0200
commit

34b86a45897b8a5ed75dded2a200a3c22f040a8b

parent

63c0bddb422750a51c5f951c7727501157632da0

M core/interpreter.nimcore/interpreter.nim

@@ -1,4 +1,4 @@

-import streams, strutils, critbits +import streams, strutils, critbits, os import types, parser,

@@ -26,6 +26,7 @@ if not scope.parent.isNil:

result = scope.parent.fullname & ":" & result proc getSymbol*(scope: ref MinScope, key: string): MinOperator = + #echo key, " - ", scope.symbols.hasKey(key) if scope.symbols.hasKey(key): return scope.symbols[key] elif not scope.parent.isNil:

@@ -84,6 +85,7 @@ var q = MinValue(kind: minQuotation, qVal: newSeq[MinValue](0))

q.scope = new MinScope q.scope.name = id q.scope.parent = i.scope + q.scope.disposable = true #i.debug "[scope] " & q.scope.fullname let scope = i.scope i.scope = q.scope

@@ -96,6 +98,7 @@ var st:MinStack = newSeq[MinValue](0)

var pr:MinParser var i:MinInterpreter = MinInterpreter( filename: "input", + pwd: "", parser: pr, stack: st, scope: ROOT,

@@ -104,12 +107,20 @@ currSym: MinValue(column: 1, line: 1, kind: minSymbol, symVal: "")

) return i +proc copy(i: MinInterpreter, filename = "input"): MinInterpreter = + result = newMinInterpreter(debugging = i.debugging) + result.filename = filename + result.pwd = filename.parentDir + result.stack = i.stack + result.scope = i.scope + result.currSym = MinValue(column: 1, line: 1, kind: minSymbol, symVal: "") + proc error*(i: MinInterpreter, status: MinError, message = "") = var msg = if message == "": ERRORS[status] else: message - if i.filename == "": + if i.currSym.filename == "": stderr.writeLine("`$1`: Error - $2" % [i.currSym.symVal, msg]) else: - stderr.writeLine("$1 [$2,$3] `$4`: Error - $5" % [i.filename, $i.currSym.line, $i.currSym.column, i.currSym.symVal, msg]) + stderr.writeLine("$1 [$2,$3] `$4`: Error - $5" % [i.currSym.filename, $i.currSym.line, $i.currSym.column, i.currSym.symVal, msg]) quit(int(status)) proc open*(i: var MinInterpreter, stream:Stream, filename: string) =

@@ -128,22 +139,30 @@ let symbol = val.symVal

let sigil = "" & symbol[0] let symbolProc = i.scope.getSymbol(symbol) if not symbolProc.isNil: + #let filename = i.filename try: i.newDisposableScope("<" & symbol & ">"): + #i.debug "SCOPE: " & i.scope.fullname symbolProc(i) except: i.error(errSystem, getCurrentExceptionMsg()) + #finally: + # i.filename = filename # filename may change when evaluating quotations else: let sigilProc = i.scope.getSigil(sigil) if symbol.len > 1 and not sigilProc.isNil: let sym = symbol[1..symbol.len-1] + #let filename = i.filename try: i.stack.add(MinValue(kind: minString, strVal: sym)) sigilProc(i) except: i.error(errSystem, getCurrentExceptionMsg()) + #finally: + # i.filename = filename # Filename may change when evaluating quotations else: i.error(errUndefined, "Undefined symbol: '"&val.symVal&"'") + return else: i.stack.add(val)

@@ -186,9 +205,12 @@

proc load*(i: var MinInterpreter, s: string) = let fn = i.filename try: - i.open(newStringStream(s.readFile), s) - discard i.parser.getToken() - i.interpret() + var i2 = i.copy(s) + i2.open(newStringStream(s.readFile), s) + discard i2.parser.getToken() + i2.interpret() + i.stack = i2.stack + i.scope = i2.scope except: stderr.writeLine getCurrentExceptionMsg() finally:
M core/parser.nimcore/parser.nim

@@ -382,6 +382,7 @@ p.a = ""

discard getToken(p) else: raiseUndefinedError(p, "Undefined value: '"&p.a&"'") + result.filename = p.filename proc `$`*(a: MinValue): string = case a.kind:
M core/types.nimcore/types.nim

@@ -23,11 +23,13 @@ MinScope* = object

symbols*: CritBitTree[MinOperator] sigils*: CritBitTree[MinOperator] parent*: ref MinScope + disposable*: bool name*: string stack*: MinStack MinValue* = object line*: int column*: int + filename*: string case kind*: MinKind of minInt: intVal*: int of minFloat: floatVal*: float

@@ -71,6 +73,7 @@ EMinParsingError* = ref object of ValueError

EMinUndefinedError* = ref object of ValueError MinInterpreter* = object stack*: MinStack + pwd*: string scope*: ref MinScope parser*: MinParser currSym*: MinValue
M core/utils.nimcore/utils.nim

@@ -65,6 +65,15 @@ return ROOT

else: return scope.parent +proc ancestor*(scope: ref MinScope): ref MinScope = + if scope.parent.isNil: + return ROOT + else: + if scope.parent.disposable: + return scope.parent.ancestor + else: + return scope.parent + proc define*(name: string): ref MinScope = var scope = new MinScope scope.name = name
M lib/io.nimlib/io.nim

@@ -24,7 +24,7 @@ .symbol("print") do (i: In):

let a = i.peek a.print - .symbol("read") do (i: In): + .symbol("fread") do (i: In): let a = i.pop if a.isString: if a.strVal.fileExists:

@@ -37,7 +37,7 @@ warn "File '$1' not found" % [a.strVal]

else: i.error(errIncorrect, "A string is required on the stack") - .symbol("write") do (i: In): + .symbol("fwrite") do (i: In): let a = i.pop let b = i.pop if a.isString and b.isString:

@@ -47,5 +47,20 @@ except:

warn getCurrentExceptionMsg() else: i.error(errIncorrect, "Two strings are required on the stack") + + .symbol("fappend") do (i: In): + let a = i.pop + let b = i.pop + if a.isString and b.isString: + try: + var f:File + discard f.open(a.strVal, fmAppend) + f.write(b.strVal) + f.close() + except: + warn getCurrentExceptionMsg() + else: + i.error(errIncorrect, "Two strings are required on the stack") + .finalize()
M lib/lang.nimlib/lang.nim

@@ -1,4 +1,4 @@

-import critbits, strutils +import critbits, strutils, os import ../core/types, ../core/parser,

@@ -48,9 +48,10 @@ else:

i.error errIncorrect, "The top quotation must contain only one symbol value" i.debug "[let] " & symbol & " = " & $q1 i.scope.parent.symbols[symbol] = proc(i: var MinInterpreter) = - i.evaluating = true + #i.evaluating = true + #i.filename = q1.filename # filename will be reset automatically by interpreter i.push q1.qVal - i.evaluating = false + #i.evaluating = false .symbol("bind") do (i: In): var q2 = i.pop # new (can be a quoted symbol or a string)

@@ -58,17 +59,25 @@ var q1 = i.pop # existing (auto-quoted)

var symbol: string if not q1.isQuotation: q1 = @[q1].newVal - if q2.isString: - symbol = q2.strVal + if q2.isStringLike: + symbol = q2.getString elif q2.isQuotation and q2.qVal.len == 1 and q2.qVal[0].kind == minSymbol: symbol = q2.qVal[0].symVal else: i.error errIncorrect, "The top quotation must contain only one symbol value" i.debug "[bind] " & symbol & " = " & $q1 + #if not i.filename.isNil and i.filename != "eval": + # echo "BIND $1 - fn: $2" % [symbol, i.filename] + # q1.filename = i.filename # Save filename for diagnostic purposes i.scope.setSymbol(symbol) do (i: In): - i.evaluating = true + #i.evaluating = true + let fn = i.filename + #if not q1.filename.isNil: + # i.filename = q1.filename + #echo "BIND '$1' FN: $2" % [symbol, i.filename] i.push q1.qVal - i.evaluating = false + #i.filename = fn + #i.evaluating = false .symbol("module") do (i: In): let name = i.pop

@@ -78,14 +87,19 @@ i.error(errIncorrect, "A string and a quotation are require on the stack")

let id = name.strVal let scope = i.scope let stack = i.copystack - i.newScope(id, code): #<-- + code.filename = i.filename + i.newScope(id, code): for item in code.qVal: i.push item let p = proc(i: In) = i.evaluating = true + #let fn = i.filename + #if not code.filename.isNil: + #i.filename = code.filename # filename will be reset automatically by interpreter i.push code + #i.filename = fn i.evaluating = false - i.scope.parent.symbols[id] = p + i.scope.ancestor.symbols[id] = p i.stack = stack .symbol("import") do (i: In):

@@ -103,12 +117,12 @@ for sym, val in mdl.scope.symbols.pairs:

i.debug "[$1 - import] $2:$3" % [i.scope.parent.name, i.scope.name, sym] i.scope.parent.symbols[sym] = val - #.sigil("'") do (i: In): - # i.push(@[MinValue(kind: minSymbol, symVal: i.pop.strVal)].newVal) + .sigil("'") do (i: In): + i.push(@[MinValue(kind: minSymbol, symVal: i.pop.strVal)].newVal) .symbol("sigil") do (i: In): var q1 = i.pop - let q2 = i.pop + var q2 = i.pop if q1.isString: q1 = @[q1].newVal if q1.isQuotation and q2.isQuotation:

@@ -117,8 +131,10 @@ var symbol = q1.qVal[0].symVal

if symbol.len == 1: if i.scope.parent.sigils.hasKey(symbol): i.error errSystem, "Sigil '$1' already exists" % [symbol] + #q2.filename = i.filename # Save filename for diagnostic purposes i.scope.parent.sigils[symbol] = proc(i: var MinInterpreter) = i.evaluating = true + #i.filename = q2.filename # filename will be reset automatically by interpreter i.push q2.qVal i.evaluating = false else:

@@ -138,7 +154,10 @@

.symbol("load") do (i: In): let s = i.pop if s.isString: - i.load s.strVal + var file = s.strVal + if not file.endsWith(".min"): + file = file & ".min" + i.load i.pwd.joinPath(file) else: i.error(errIncorrect, "A string is required on the stack")

@@ -150,14 +169,35 @@ let vals = fqn.qVal

var q: MinValue if vals.len == 0: i.error(errIncorrect, "No symbol to call") + return + var symScope = i.scope + var symFilename = i.filename for c in 0..vals.len-1: if not vals[c].isStringLike: i.error(errIncorrect, "Quotation must contain only symbols or strings") - i.scope.getSymbol(vals[c].getString)(i) + return + let qProc = i.scope.getSymbol(vals[c].getString) + if qProc.isNil: + i.error(errUndefined, "Symbol '$1' not found" % [vals[c].getString]) + return + let currScope = i.scope + let currFilename = i.filename + # Execute operator in "parent" symbol scope + #echo "CALL - executing '$1' fn: $2" % [vals[c].getString, "-"] + i.scope = symScope + #i.filename = symFilename + #echo ">>> CALL: ", vals[c].getString, " - ", symFilename + qProc(i) + i.scope = currScope + #echo "<<< CALL: ", currFilename + #i.filename = currFilename if vals.len > 1 and c < vals.len-1: q = i.pop if not q.isQuotation: i.error(errIncorrect, "Unable to evaluate symbol '$1'" % [vals[c-1].getString]) + return + symScope = q.scope + symFilename = q.filename else: i.error(errIncorrect, "A quotation is required on the stack")

@@ -224,6 +264,7 @@ var q = i.pop

let v = i.pop if not q.isQuotation: i.error errNoQuotation + return q.qVal.add v i.push q
M lib/prelude.minlib/prelude.min

@@ -10,7 +10,6 @@ #sys

#time ; Common sigils -(quote) (') sigil (bind) (:) sigil (let) (.) sigil (getenv) ($) sigil
M minim.nimminim.nim

@@ -1,4 +1,4 @@

-import streams, critbits, parseopt2, strutils +import streams, critbits, parseopt2, strutils, os import core/parser, core/interpreter,

@@ -13,12 +13,12 @@ lib/time,

lib/io, lib/sys -const version* = "0.1.0" -var repl = false +const version* = "1.0.0" +var REPL = false const prelude = "lib/prelude.min".slurp.strip const - USE_LINENOISE = (defined(i386) or defined(amd64))# and not defined(windows) + USE_LINENOISE = true#(defined(i386) or defined(amd64)) let usage* = " MiNiM v" & version & " - a tiny concatenative system programming language" & """

@@ -58,6 +58,7 @@ return stdin.readLine

proc minimStream(s: Stream, filename: string) = var i = INTERPRETER + i.pwd = filename.parentDir i.eval prelude i.open(s, filename) discard i.parser.getToken()

@@ -125,7 +126,7 @@ quit(0)

of "version", "v": echo version of "interactive", "i": - repl = true + REPL = true else: discard else:

@@ -135,7 +136,7 @@ if s != "":

minimString(s) elif file != "": minimFile file -elif repl: +elif REPL: minimRepl() quit(0) else:
M tests/test.mintests/test.min

@@ -30,6 +30,7 @@ ) :assert

;; report ( + newline ' .results ; save the results collected so far 0 .total 0 .failed

@@ -51,14 +52,11 @@ ) :report

) =test -#test - ; Sample unit test program -"maths" describe - (2 3 ==) assert - (2 1 >) assert - (2 1 <) assert - (4 4 ==) assert - (7 7 ==) assert - newline - report +;"maths" describe +; (2 3 ==) assert +; (2 1 >) assert +; (2 1 <) assert +; (4 4 ==) assert +; (7 7 ==) assert +; report