all repos — min @ 638cb7ac58499c87dcd1a0d232e68bb831f90a48

A small but practical concatenative programming language.

Started implementing http communication support.
h3rald h3rald@h3rald.com
Sat, 02 Jul 2016 15:01:56 +0200
commit

638cb7ac58499c87dcd1a0d232e68bb831f90a48

parent

0d73c8590831d2b9223b61d5ebc4f39207ad8060

M core/interpreter.nimcore/interpreter.nim

@@ -4,10 +4,6 @@ types,

parser, ../vendor/linenoise -var ROOT*: ref MinScope = new MinScope - -ROOT.name = "ROOT" - proc raiseUndefined(msg: string) = raise MinUndefinedError(msg: msg)

@@ -43,7 +39,7 @@ # Go up the scope chain and attempt to find the symbol

if scope.parent.isNotNil: result = scope.parent.setSymbol(key, value) -proc getSigil*(scope: ref MinScope, key: string): MinOperator = +proc getSigil*(scope: ref MinScope, key: string): MinOperator {.gcsafe.}= if scope.sigils.hasKey(key): return scope.sigils[key] elif scope.parent.isNotNil:

@@ -96,12 +92,14 @@

proc newMinInterpreter*(debugging = false): MinInterpreter = var st:MinStack = newSeq[MinValue](0) var pr:MinParser + var scope = new MinScope + scope.name = "ROOT" var i:MinInterpreter = MinInterpreter( filename: "input", pwd: "", parser: pr, stack: st, - scope: ROOT, + scope: scope, debugging: debugging, unsafe: false, currSym: MinValue(column: 1, line: 1, kind: minSymbol, symVal: "")

@@ -141,7 +139,7 @@

proc close*(i: In) = i.parser.close(); -proc push*(i: In, val: MinValue) = +proc push*(i: In, val: MinValue) {.gcsafe.}= i.debug val if val.kind == minSymbol: if not i.evaluating:

@@ -168,7 +166,7 @@ i.stack.add(MinValue(kind: minString, strVal: sym))

if i.unsafe: let stack = i.copystack try: - sigilProc(i) + i.sigilProc except: i.stack = stack raise

@@ -196,7 +194,7 @@ return i.stack[i.stack.len-1]

else: raiseEmptyStack() -proc interpret*(i: In) = +proc interpret*(i: In) {.gcsafe.}= var val: MinValue while i.parser.token != tkEof: i.execute:

@@ -238,5 +236,3 @@ i.filename = fn

proc apply*(i: In, symbol: string) = i.scope.getSymbol(symbol)(i) - -var INTERPRETER* = newMinInterpreter()
A core/server.nim

@@ -0,0 +1,60 @@

+import + asynchttpserver, + asyncdispatch, + httpclient, + streams, + critbits, + pegs + +import + types, + parser, + interpreter, + utils + + +proc validUrl(req: Request, url: string): bool = + return req.url.path == url or req.url.path == url & "/" + +proc validMethod(req: Request, meth: string): bool = + return req.reqMethod == meth + +proc reqPost(req: Request) {.async.}= + if not req.validMethod("POST"): + await req.respond(Http405, "Method Not Allowed: " & req.reqMethod) + +#proc reqUrl(req: Request, url: string) {.async.}= +# if not req.validUrl(url): +# await req.respond(Http400, "Bad Request: POST " & req.url.path) + + +proc exec(req: Request, interpreter: MinInterpreter): string {.gcsafe.}= + let filename = "request" + let s = newStringStream(req.body) + var i = interpreter + i.open(s, filename) + discard i.parser.getToken() + i.interpret() + result = i.dump() + i.close() + +proc process(req: Request, i: MinInterpreter): string {.gcsafe.} = + var matches = @["", "", ""] + template route(req, peg: expr, op: stmt): stmt {.immediate.}= + if req.url.path.find(peg, matches) != -1: + op + req.route peg"^\/exec\/?$": + return exec(req, i) + +proc serve*(port: Port, address = "", interpreter: MinInterpreter) = + var hosts: CritBitTree[string] + proc handleHttpRequest(req: Request): Future[void] {.async.} = + if not req.validMethod("POST"): + await req.respond(Http405, "Method Not Allowed: " & req.reqMethod) + await req.respond(Http200, req.process(interpreter)) + let server = newAsyncHttpServer() + asyncCheck server.serve(port, handleHttpRequest, address) + +proc post*(url, content: string): string = + url.postContent(content) +
M core/types.nimcore/types.nim

@@ -82,7 +82,7 @@ evaluating*: bool

unsafe*: bool In* = var MinInterpreter Val* = var MinValue - MinOperator* = proc (i: In) + MinOperator* = proc (i: In) {.gcsafe, closure.} MinSigil* = proc (i: In, sym: string) MinParsingError* = ref object of ValueError MinUndefinedError* = ref object of ValueError
M core/utils.nimcore/utils.nim

@@ -88,14 +88,14 @@ stderr.writeLine s

proc previous*(scope: ref MinScope): ref MinScope = if scope.parent.isNil: - return ROOT + return scope #### was: ROOT else: return scope.parent -proc define*(name: string): ref MinScope = +proc define*(i: In, name: string): ref MinScope = var scope = new MinScope scope.name = name - scope.parent = INTERPRETER.scope + scope.parent = i.scope return scope proc symbol*(scope: ref MinScope, sym: string, p: MinOperator): ref MinScope =

@@ -109,7 +109,7 @@

proc finalize*(scope: ref MinScope) = var mdl = newSeq[MinValue](0).newVal mdl.scope = scope - mdl.scope.previous.symbols[scope.name] = proc(i: In) = + mdl.scope.previous.symbols[scope.name] = proc(i: In) {.gcsafe, closure.} = i.evaluating = true i.push mdl i.evaluating = false
M lib/min_io.nimlib/min_io.nim

@@ -8,38 +8,39 @@

# I/O -define("io") +proc io_module*(i: In) = + i.define("io") + + .symbol("newline") do (i: In): + echo "" + + .symbol("put") do (i: In): + let a = i.peek + echo $$a + + .symbol("get") do (i: In): + i.push newVal(stdin.readLine()) + + .symbol("print") do (i: In): + let a = i.peek + a.print + + .symbol("fread") do (i: In): + var a: MinValue + i.reqString a + i.push newVal(a.strVal.readFile) + + .symbol("fwrite") do (i: In): + var a, b: MinValue + i.reqTwoStrings a, b + a.strVal.writeFile(b.strVal) + + .symbol("fappend") do (i: In): + var a, b: MinValue + i.reqTwoStrings a, b + var f:File + discard f.open(a.strVal, fmAppend) + f.write(b.strVal) + f.close() - .symbol("newline") do (i: In): - echo "" - - .symbol("put") do (i: In): - let a = i.peek - echo $$a - - .symbol("get") do (i: In): - i.push newVal(stdin.readLine()) - - .symbol("print") do (i: In): - let a = i.peek - a.print - - .symbol("fread") do (i: In): - var a: MinValue - i.reqString a - i.push newVal(a.strVal.readFile) - - .symbol("fwrite") do (i: In): - var a, b: MinValue - i.reqTwoStrings a, b - a.strVal.writeFile(b.strVal) - - .symbol("fappend") do (i: In): - var a, b: MinValue - i.reqTwoStrings a, b - var f:File - discard f.open(a.strVal, fmAppend) - f.write(b.strVal) - f.close() - - .finalize() + .finalize()
M lib/min_lang.nimlib/min_lang.nim

@@ -7,400 +7,405 @@ ../core/utils,

../core/regex, ../vendor/routine -ROOT - .symbol("exit") do (i: In): - quit(0) - - .symbol("symbols") do (i: In): - var q = newSeq[MinValue](0) - var scope = i.scope - while scope.isNotNil: - for s in scope.symbols.keys: - q.add s.newVal - scope = scope.parent - i.push q.newVal - - .symbol("sigils") do (i: In): - var q = newSeq[MinValue](0) - var scope = i.scope - while scope.isNotNil: - for s in scope.sigils.keys: - q.add s.newVal - scope = scope.parent - i.push q.newVal - - .symbol("debug?") do (i: In): - i.push i.debugging.newVal - - .symbol("debug") do (i: In): - i.debugging = not i.debugging - echo "Debugging: $1" % [$i.debugging] - - # Language constructs - - .symbol("define") do (i: In): - var sym, val: MinValue - i.reqStringLike sym - var q1 = i.pop # existing (auto-quoted) - var symbol: string - if not q1.isQuotation: - q1 = @[q1].newVal - symbol = sym.getString - i.debug "[define] " & symbol & " = " & $q1 - i.scope.symbols[symbol] = proc(i: In) = - i.push q1.qVal - - .symbol("bind") do (i: In): - var sym, val: MinValue - i.reqStringLike sym - var q1 = i.pop # existing (auto-quoted) - var symbol: string - if not q1.isQuotation: - q1 = @[q1].newVal - symbol = sym.getString - i.debug "[bind] " & symbol & " = " & $q1 - let res = i.scope.setSymbol(symbol) do (i: In): - i.push q1.qVal - if not res: - raiseUndefined("Attempting to bind undefined symbol: " & symbol) - - .symbol("delete") do (i: In): - var sym: MinValue - i.reqStringLike sym - let res = i.scope.delSymbol(sym.getString) - if not res: - raiseUndefined("Attempting to delete undefined symbol: " & sym.getString) - - .symbol("scope") do (i: In): - var code: MinValue - i.reqQuotation code - code.filename = i.filename - code.objType = "scope" - i.unquote("<scope>", code) - i.push @[code].newVal - - .symbol("module") do (i: In): - var code, name: MinValue - i.reqStringLike name - i.reqQuotation code - code.filename = i.filename - code.objType = "module" - i.unquote("<module>", code) - i.scope.symbols[name.getString] = proc(i: In) = +proc lang_module*(i: In) = + #var ROOT: ref MinScope = new MinScope + #ROOT.name = "ROOT" + #ROOT + i.scope + .symbol("exit") do (i: In): + quit(0) + + .symbol("symbols") do (i: In): + var q = newSeq[MinValue](0) + var scope = i.scope + while scope.isNotNil: + for s in scope.symbols.keys: + q.add s.newVal + scope = scope.parent + i.push q.newVal + + .symbol("sigils") do (i: In): + var q = newSeq[MinValue](0) + var scope = i.scope + while scope.isNotNil: + for s in scope.sigils.keys: + q.add s.newVal + scope = scope.parent + i.push q.newVal + + .symbol("debug?") do (i: In): + i.push i.debugging.newVal + + .symbol("debug") do (i: In): + i.debugging = not i.debugging + echo "Debugging: $1" % [$i.debugging] + + # Language constructs + + .symbol("define") do (i: In): + var sym, val: MinValue + i.reqStringLike sym + var q1 = i.pop # existing (auto-quoted) + var symbol: string + if not q1.isQuotation: + q1 = @[q1].newVal + symbol = sym.getString + i.debug "[define] " & symbol & " = " & $q1 + i.scope.symbols[symbol] = proc(i: In) = + i.push q1.qVal + + .symbol("bind") do (i: In): + var sym, val: MinValue + i.reqStringLike sym + var q1 = i.pop # existing (auto-quoted) + var symbol: string + if not q1.isQuotation: + q1 = @[q1].newVal + symbol = sym.getString + i.debug "[bind] " & symbol & " = " & $q1 + let res = i.scope.setSymbol(symbol) do (i: In): + i.push q1.qVal + if not res: + raiseUndefined("Attempting to bind undefined symbol: " & symbol) + + .symbol("delete") do (i: In): + var sym: MinValue + i.reqStringLike sym + let res = i.scope.delSymbol(sym.getString) + if not res: + raiseUndefined("Attempting to delete undefined symbol: " & sym.getString) + + .symbol("scope") do (i: In): + var code: MinValue + i.reqQuotation code + code.filename = i.filename + code.objType = "scope" + i.unquote("<scope>", code) + i.push @[code].newVal + + .symbol("module") do (i: In): + var code, name: MinValue + i.reqStringLike name + i.reqQuotation code + code.filename = i.filename + code.objType = "module" + i.unquote("<module>", code) + i.scope.symbols[name.getString] = proc(i: In) = + i.push code + + .symbol("object") do (i: In): + var code, t: MinValue + i.reqStringLike t + i.reqQuotation code + code.filename = i.filename + code.objType = t.getString + i.unquote("<object>", code) i.push code - - .symbol("object") do (i: In): - var code, t: MinValue - i.reqStringLike t - i.reqQuotation code - code.filename = i.filename - code.objType = t.getString - i.unquote("<object>", code) - i.push code - - .symbol("type") do (i: In): - var obj: MinValue - i.reqObject obj - i.push obj.objType.newVal - - .symbol("import") do (i: In): - var mdl, rawName: MinValue - var name: string - i.reqStringLike rawName - name = rawName.getString - i.scope.getSymbol(name)(i) - i.reqQuotation mdl - if mdl.scope.isNotNil: - for sym, val in mdl.scope.symbols.pairs: - i.debug "[import] $1:$2" % [i.scope.name, sym] - i.scope.symbols[sym] = val + + .symbol("type") do (i: In): + var obj: MinValue + i.reqObject obj + i.push obj.objType.newVal + + .symbol("import") do (i: In): + var mdl, rawName: MinValue + var name: string + i.reqStringLike rawName + name = rawName.getString + i.scope.getSymbol(name)(i) + i.reqQuotation mdl + if mdl.scope.isNotNil: + for sym, val in mdl.scope.symbols.pairs: + i.debug "[import] $1:$2" % [i.scope.name, sym] + i.scope.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, q2: MinValue - i.reqTwoQuotations q1, q2 - 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: - raiseInvalid("Sigil '$1' already exists" % [symbol]) - i.scope.sigils[symbol] = proc(i: In) = - i.evaluating = true - i.push q2.qVal - i.evaluating = false + .symbol("sigil") do (i: In): + var q1, q2: MinValue + i.reqTwoQuotations q1, q2 + 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: + raiseInvalid("Sigil '$1' already exists" % [symbol]) + i.scope.sigils[symbol] = proc(i: In) = + i.evaluating = true + i.push q2.qVal + i.evaluating = false + else: + raiseInvalid("A sigil can only have one character") else: - raiseInvalid("A sigil can only have one character") - else: - raiseInvalid("The top quotation must contain only one symbol value") - - .symbol("eval") do (i: In): - var s: MinValue - i.reqString s - i.eval s.strVal - - .symbol("load") do (i: In): - var s: MinValue - i.reqStringLike s - var file = s.getString - if not file.endsWith(".min"): - file = file & ".min" - i.load i.pwd.joinPath(file) - - .symbol("with") do (i: In): - var qscope, qprog: Minvalue - i.reqTwoQuotations qscope, qprog - i.unquote("<with-scope>", qscope) - i.withScope(qscope): - i.unquote("<with-program>", qprog) - - .symbol("call") do (i: In): - var symbol, q: Minvalue - i.reqStringLike symbol - i.reqQuotation q - let s = symbol.getString - let origScope = i.scope - i.scope = q.scope - let sProc = i.scope.getSymbol(s) - if sProc.isNil: - raiseUndefined("Symbol '$1' not found in scope '$2'" % [s, i.scope.fullname]) - # Restore original quotation - sProc(i) - i.scope = origScope - - .symbol("inspect") do (i: In): - var scope: MinValue - i.reqQuotation scope - var symbols = newSeq[MinValue](0) - for s in scope.scope.symbols.keys: - symbols.add s.newVal - i.push symbols.newVal - - .symbol("raise") do (i: In): - var err: MinValue - i.reqQuotation err - raiseRuntime("($1) $2" % [err.qVal[0].getString, err.qVal[1].getString], err.qVal) - - .symbol("try") do (i: In): - var prog: MinValue - i.reqQuotation prog - if prog.qVal.len == 0: - raiseInvalid("Quotation must contain at least one element") - var code = prog.qVal[0] - var final, catch: MinValue - var hasFinally = false - var hasCatch = false - if prog.qVal.len > 1: - catch = prog.qVal[1] - hasCatch = true - 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): - raiseInvalid("Quotation must contain at one quotation") - i.unsafe = true - try: - i.unquote("<try-code>", code) - except MinRuntimeError: - if not hasCatch: - return - i.unsafe = false - let e = (MinRuntimeError)getCurrentException() - i.push e.qVal.newVal - i.unquote("<try-catch>", catch) - except: - if not hasCatch: - return - i.unsafe = false - let e = getCurrentException() - i.push @[regex.replace($e.name, ":.+$", "").newVal, e.msg.newVal].newVal - i.unquote("<try-catch>", catch) - finally: - if hasFinally: - i.unquote("<try-finally>", final) - - .symbol("multiunquote") do (i: In): - var q: MinValue - i.reqQuotation q - let stack = i.copystack - proc coroutine(i: MinInterpreter, c: int, results: ptr MinStack) {.routine.} = - i.unquote("<multiunquote>", q.qVal[c]) - 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: - raiseInvalid("Item #$1 is not a quotation" % [$(c+1)]) - var i2 = i.copy(i.filename) - var res: MinStack = newSeq[MinValue](0) - pRun coroutine, (i2, c, results.addr) - waitAllRoutine() - i.push results.newVal - - - # Operations on the whole stack - - .symbol("clear") do (i: In): - while i.stack.len > 0: - discard i.pop - - .symbol("dump") do (i: In): - echo i.dump - - .symbol("getstack") do (i: In): - i.push i.stack.newVal - - .symbol("setstack") do (i: In): - var q: MinValue - i.reqQuotation q - i.stack = q.qVal - - # Operations on quotations or strings - - .symbol("concat") do (i: In): - var q1, q2: MinValue - i.reqTwoQuotationsOrStrings q1, q2 - if q1.isString and q2.isString: - let s = q2.strVal & q1.strVal - i.push newVal(s) - else: - let q = q2.qVal & q1.qVal - i.push newVal(q) - - .symbol("first") do (i: In): - var q: MinValue - i.reqStringOrQuotation q - if q.isQuotation: - if q.qVal.len == 0: - raiseOutOfBounds("Quotation is empty") - i.push q.qVal[0] - elif q.isString: - if q.strVal.len == 0: - raiseOutOfBounds("String is empty") - i.push newVal($q.strVal[0]) - - .symbol("rest") do (i: In): - var q: MinValue - i.reqStringOrQuotation q - if q.isQuotation: - if q.qVal.len == 0: - raiseOutOfBounds("Quotation is empty") - i.push newVal(q.qVal[1..q.qVal.len-1]) - elif q.isString: - if q.strVal.len == 0: - raiseOutOfBounds("String is empty") - i.push newVal(q.strVal[1..q.strVal.len-1]) - - .symbol("quote") do (i: In): - let a = i.pop - i.push MinValue(kind: minQuotation, qVal: @[a]) + raiseInvalid("The top quotation must contain only one symbol value") + + .symbol("eval") do (i: In): + var s: MinValue + i.reqString s + i.eval s.strVal + + .symbol("load") do (i: In): + var s: MinValue + i.reqStringLike s + var file = s.getString + if not file.endsWith(".min"): + file = file & ".min" + i.load i.pwd.joinPath(file) + + .symbol("with") do (i: In): + var qscope, qprog: Minvalue + i.reqTwoQuotations qscope, qprog + i.unquote("<with-scope>", qscope) + i.withScope(qscope): + i.unquote("<with-program>", qprog) + + .symbol("call") do (i: In): + var symbol, q: Minvalue + i.reqStringLike symbol + i.reqQuotation q + let s = symbol.getString + let origScope = i.scope + i.scope = q.scope + let sProc = i.scope.getSymbol(s) + if sProc.isNil: + raiseUndefined("Symbol '$1' not found in scope '$2'" % [s, i.scope.fullname]) + # Restore original quotation + sProc(i) + i.scope = origScope + + .symbol("inspect") do (i: In): + var scope: MinValue + i.reqQuotation scope + var symbols = newSeq[MinValue](0) + for s in scope.scope.symbols.keys: + symbols.add s.newVal + i.push symbols.newVal + + .symbol("raise") do (i: In): + var err: MinValue + i.reqQuotation err + raiseRuntime("($1) $2" % [err.qVal[0].getString, err.qVal[1].getString], err.qVal) + + .symbol("try") do (i: In): + var prog: MinValue + i.reqQuotation prog + if prog.qVal.len == 0: + raiseInvalid("Quotation must contain at least one element") + var code = prog.qVal[0] + var final, catch: MinValue + var hasFinally = false + var hasCatch = false + if prog.qVal.len > 1: + catch = prog.qVal[1] + hasCatch = true + 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): + raiseInvalid("Quotation must contain at one quotation") + i.unsafe = true + try: + i.unquote("<try-code>", code) + except MinRuntimeError: + if not hasCatch: + return + i.unsafe = false + let e = (MinRuntimeError)getCurrentException() + i.push e.qVal.newVal + i.unquote("<try-catch>", catch) + except: + if not hasCatch: + return + i.unsafe = false + let e = getCurrentException() + i.push @[regex.replace($e.name, ":.+$", "").newVal, e.msg.newVal].newVal + i.unquote("<try-catch>", catch) + finally: + if hasFinally: + i.unquote("<try-finally>", final) - .symbol("unquote") do (i: In): - var q: MinValue - i.reqQuotation q - i.unquote("<unquote>", q) + # TODO: waitAllRoutine is not gc-safe! + #.symbol("multiunquote") do (i: In) {.gcsafe.}: + # var q: MinValue + # i.reqQuotation q + # let stack = i.copystack + # proc coroutine(i: MinInterpreter, c: int, results: ptr MinStack) {.routine.} = + # i.unquote("<multiunquote>", q.qVal[c]) + # 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: + # raiseInvalid("Item #$1 is not a quotation" % [$(c+1)]) + # var i2 = i.copy(i.filename) + # var res: MinStack = newSeq[MinValue](0) + # pRun coroutine, (i2, c, results.addr) + # waitAllRoutine() + # i.push results.newVal - .symbol("append") do (i: In): - var q: MinValue - i.reqQuotation q - let v = i.pop - q.qVal.add v - i.push q - .symbol("cons") do (i: In): - var q: MinValue - i.reqQuotation q - let v = i.pop - q.qVal = @[v] & q.qVal - i.push q - - .symbol("at") do (i: In): - var index, q: MinValue - i.reqIntAndQuotation index, q - if q.qVal.len-1 < index.intVal: - raiseOutOfBounds("Insufficient items in quotation") - i.push q.qVal[index.intVal.int] - - .symbol("size") do (i: In): - var q: MinValue - i.reqStringOrQuotation q - if q.isQuotation: - i.push q.qVal.len.newVal - elif q.isString: - i.push q.strVal.len.newVal - - .symbol("contains") do (i: In): - let v = i.pop - var q: MinValue - i.reqQuotation q - i.push q.qVal.contains(v).newVal + # Operations on the whole stack - .symbol("map") do (i: In): - var prog, list: MinValue - i.reqTwoQuotations prog, list - i.push newVal(newSeq[MinValue](0)) - for litem in list.qVal: - i.push litem - i.unquote("<map-quotation>", prog) - i.apply("swap") - i.apply("append") + .symbol("clear") do (i: In): + while i.stack.len > 0: + discard i.pop - .symbol("times") do (i: In): - var t, prog: MinValue - i.reqIntAndQuotation t, prog - if t.intVal < 1: - raiseInvalid("A non-zero natural number is required") - for c in 1..t.intVal: - i.unquote("<times-quotation>", prog) + .symbol("dump") do (i: In): + echo i.dump - .symbol("ifte") do (i: In): - var fpath, tpath, check: MinValue - i.reqThreeQuotations fpath, tpath, check - var stack = i.copystack - i.unquote("<ifte-check>", check) - let res = i.pop - i.stack = stack - if res.isBool and res.boolVal == true: - i.unquote("<ifte-true>", tpath) - else: - i.unquote("<ifte-false>", fpath) + .symbol("getstack") do (i: In): + i.push i.stack.newVal - .symbol("while") do (i: In): - var d, b: MinValue - i.reqTwoQuotations d, b - i.push b.qVal - i.unquote("<while-check>", b) - var check = i.pop - while check.isBool and check.boolVal == true: - i.unquote("<while-quotation>", d) - i.unquote("<while-check>", b) - check = i.pop + .symbol("setstack") do (i: In): + var q: MinValue + i.reqQuotation q + i.stack = q.qVal - .symbol("filter") do (i: In): - var filter, list: MinValue - i.reqTwoQuotations filter, list - var res = newSeq[MinValue](0) - for e in list.qVal: - i.push e - i.unquote("<filter-check>", filter) - var check = i.pop - if check.isBool and check.boolVal == true: - res.add e - i.push res.newVal + # Operations on quotations or strings - .symbol("linrec") do (i: In): - var r2, r1, t, p: MinValue - i.reqFourQuotations r2, r1, t, p - proc linrec(i: In, p, t, r1, r2: var MinValue) = - i.unquote("<linrec-predicate>", p) - var check = i.pop - if check.isBool and check.boolVal == true: - i.unquote("<linrec-true>", t) + .symbol("concat") do (i: In): + var q1, q2: MinValue + i.reqTwoQuotationsOrStrings q1, q2 + if q1.isString and q2.isString: + let s = q2.strVal & q1.strVal + i.push newVal(s) else: - i.unquote("<linrec-r1>", r1) - i.linrec(p, t, r1, r2) - i.unquote("<linrec-r2>", r2) - i.linrec(p, t, r1, r2) + let q = q2.qVal & q1.qVal + i.push newVal(q) - .finalize() + .symbol("first") do (i: In): + var q: MinValue + i.reqStringOrQuotation q + if q.isQuotation: + if q.qVal.len == 0: + raiseOutOfBounds("Quotation is empty") + i.push q.qVal[0] + elif q.isString: + if q.strVal.len == 0: + raiseOutOfBounds("String is empty") + i.push newVal($q.strVal[0]) + + .symbol("rest") do (i: In): + var q: MinValue + i.reqStringOrQuotation q + if q.isQuotation: + if q.qVal.len == 0: + raiseOutOfBounds("Quotation is empty") + i.push newVal(q.qVal[1..q.qVal.len-1]) + elif q.isString: + if q.strVal.len == 0: + raiseOutOfBounds("String is empty") + i.push newVal(q.strVal[1..q.strVal.len-1]) + + .symbol("quote") do (i: In): + let a = i.pop + i.push MinValue(kind: minQuotation, qVal: @[a]) + + .symbol("unquote") do (i: In): + var q: MinValue + i.reqQuotation q + i.unquote("<unquote>", q) + + .symbol("append") do (i: In): + var q: MinValue + i.reqQuotation q + let v = i.pop + q.qVal.add v + i.push q + + .symbol("cons") do (i: In): + var q: MinValue + i.reqQuotation q + let v = i.pop + q.qVal = @[v] & q.qVal + i.push q + + .symbol("at") do (i: In): + var index, q: MinValue + i.reqIntAndQuotation index, q + if q.qVal.len-1 < index.intVal: + raiseOutOfBounds("Insufficient items in quotation") + i.push q.qVal[index.intVal.int] + + .symbol("size") do (i: In): + var q: MinValue + i.reqStringOrQuotation q + if q.isQuotation: + i.push q.qVal.len.newVal + elif q.isString: + i.push q.strVal.len.newVal + + .symbol("contains") do (i: In): + let v = i.pop + var q: MinValue + i.reqQuotation q + i.push q.qVal.contains(v).newVal + + .symbol("map") do (i: In): + var prog, list: MinValue + i.reqTwoQuotations prog, list + i.push newVal(newSeq[MinValue](0)) + for litem in list.qVal: + i.push litem + i.unquote("<map-quotation>", prog) + i.apply("swap") + i.apply("append") + + .symbol("times") do (i: In): + var t, prog: MinValue + i.reqIntAndQuotation t, prog + if t.intVal < 1: + raiseInvalid("A non-zero natural number is required") + for c in 1..t.intVal: + i.unquote("<times-quotation>", prog) + + .symbol("ifte") do (i: In): + var fpath, tpath, check: MinValue + i.reqThreeQuotations fpath, tpath, check + var stack = i.copystack + i.unquote("<ifte-check>", check) + let res = i.pop + i.stack = stack + if res.isBool and res.boolVal == true: + i.unquote("<ifte-true>", tpath) + else: + i.unquote("<ifte-false>", fpath) + + .symbol("while") do (i: In): + var d, b: MinValue + i.reqTwoQuotations d, b + i.push b.qVal + i.unquote("<while-check>", b) + var check = i.pop + while check.isBool and check.boolVal == true: + i.unquote("<while-quotation>", d) + i.unquote("<while-check>", b) + check = i.pop + + .symbol("filter") do (i: In): + var filter, list: MinValue + i.reqTwoQuotations filter, list + var res = newSeq[MinValue](0) + for e in list.qVal: + i.push e + i.unquote("<filter-check>", filter) + var check = i.pop + if check.isBool and check.boolVal == true: + res.add e + i.push res.newVal + + .symbol("linrec") do (i: In): + var r2, r1, t, p: MinValue + i.reqFourQuotations r2, r1, t, p + proc linrec(i: In, p, t, r1, r2: var MinValue) = + i.unquote("<linrec-predicate>", p) + var check = i.pop + if check.isBool and check.boolVal == true: + i.unquote("<linrec-true>", t) + else: + i.unquote("<linrec-r1>", r1) + i.linrec(p, t, r1, r2) + i.unquote("<linrec-r2>", r2) + i.linrec(p, t, r1, r2) + + .finalize()
M lib/min_logic.nimlib/min_logic.nim

@@ -7,152 +7,154 @@ ../core/utils

# Comparison operators -define("logic") - .symbol(">") do (i: In): - var n2, n1: MinValue - i.reqTwoNumbersOrStrings n2, n1 - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal > n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float > n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal > n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal > n2.intVal.float) - else: - i.push newVal(n1.strVal > n2.strVal) - - .symbol(">=") do (i: In): - var n2, n1: MinValue - i.reqTwoNumbersOrStrings n2, n1 - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal >= n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float >= n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal >= n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal >= n2.intVal.float) - else: - i.push newVal(n1.strVal >= n2.strVal) +proc logic_module*(i: In)= + i.define("logic") - .symbol("<") do (i: In): - var n2, n1: MinValue - i.reqTwoNumbersOrStrings n1, n2 - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal > n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float > n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal > n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal > n2.intVal.float) - else: - i.push newVal(n1.strVal > n2.strVal) + .symbol(">") do (i: In): + var n2, n1: MinValue + i.reqTwoNumbersOrStrings n2, n1 + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal > n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float > n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal > n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal > n2.intVal.float) + else: + i.push newVal(n1.strVal > n2.strVal) - .symbol("<=") do (i: In): - var n2, n1: MinValue - i.reqTwoNumbersOrStrings n1, n2 - if n1.isNumber and n2.isNumber: - if n1.isInt and n2.isInt: - i.push newVal(n1.intVal >= n2.intVal) - elif n1.isInt and n2.isFloat: - i.push newVal(n1.intVal.float >= n2.floatVal) - elif n1.isFloat and n2.isFloat: - i.push newVal(n1.floatVal >= n2.floatVal) - elif n1.isFloat and n2.isInt: - i.push newVal(n1.floatVal >= n2.intVal.float) - else: - i.push newVal(n1.strVal >= n2.strVal) - - .symbol("==") do (i: In): - var n1, n2: MinValue - i.reqTwoSimilarTypesNonSymbol n2, n1 - i.push newVal(n1 == n2) - - .symbol("!=") do (i: In): - var n1, n2: MinValue - i.reqTwoSimilarTypesNonSymbol n2, n1 - i.push newVal(not (n1 == n2)) - - # Boolean Logic - - .symbol("not") do (i: In): - var b: MinValue - i.reqBool b - i.push newVal(not b.boolVal) - - .symbol("and") do (i: In): - var a, b: MinValue - i.reqTwoBools a, b - i.push newVal(a.boolVal and b.boolVal) - - .symbol("or") do (i: In): - var a, b: MinValue - i.reqTwoBools a, b - i.push newVal(a.boolVal or b.boolVal) - - .symbol("xor") do (i: In): - var a, b: MinValue - i.reqTwoBools a, b - i.push newVal(a.boolVal xor b.boolVal) - - .symbol("string?") do (i: In): - if i.peek.kind == minString: - i.push true.newVal - else: - i.push false.newVal - - .symbol("int?") do (i: In): - if i.peek.kind == minInt: - i.push true.newVal - else: - i.push false.newVal + .symbol(">=") do (i: In): + var n2, n1: MinValue + i.reqTwoNumbersOrStrings n2, n1 + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal >= n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float >= n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal >= n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal >= n2.intVal.float) + else: + i.push newVal(n1.strVal >= n2.strVal) + + .symbol("<") do (i: In): + var n2, n1: MinValue + i.reqTwoNumbersOrStrings n1, n2 + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal > n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float > n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal > n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal > n2.intVal.float) + else: + i.push newVal(n1.strVal > n2.strVal) + + .symbol("<=") do (i: In): + var n2, n1: MinValue + i.reqTwoNumbersOrStrings n1, n2 + if n1.isNumber and n2.isNumber: + if n1.isInt and n2.isInt: + i.push newVal(n1.intVal >= n2.intVal) + elif n1.isInt and n2.isFloat: + i.push newVal(n1.intVal.float >= n2.floatVal) + elif n1.isFloat and n2.isFloat: + i.push newVal(n1.floatVal >= n2.floatVal) + elif n1.isFloat and n2.isInt: + i.push newVal(n1.floatVal >= n2.intVal.float) + else: + i.push newVal(n1.strVal >= n2.strVal) + + .symbol("==") do (i: In): + var n1, n2: MinValue + i.reqTwoSimilarTypesNonSymbol n2, n1 + i.push newVal(n1 == n2) + + .symbol("!=") do (i: In): + var n1, n2: MinValue + i.reqTwoSimilarTypesNonSymbol n2, n1 + i.push newVal(not (n1 == n2)) + + # Boolean Logic + + .symbol("not") do (i: In): + var b: MinValue + i.reqBool b + i.push newVal(not b.boolVal) + + .symbol("and") do (i: In): + var a, b: MinValue + i.reqTwoBools a, b + i.push newVal(a.boolVal and b.boolVal) + + .symbol("or") do (i: In): + var a, b: MinValue + i.reqTwoBools a, b + i.push newVal(a.boolVal or b.boolVal) + + .symbol("xor") do (i: In): + var a, b: MinValue + i.reqTwoBools a, b + i.push newVal(a.boolVal xor b.boolVal) + + .symbol("string?") do (i: In): + if i.peek.kind == minString: + i.push true.newVal + else: + i.push false.newVal + + .symbol("int?") do (i: In): + if i.peek.kind == minInt: + i.push true.newVal + else: + i.push false.newVal + + .symbol("float?") do (i: In): + if i.peek.kind == minFloat: + i.push true.newVal + else: + i.push false.newVal + + .symbol("number?") do (i: In): + if i.peek.kind == minFloat or i.peek.kind == minInt: + i.push true.newVal + else: + i.push false.newVal + + .symbol("bool?") do (i: In): + if i.peek.kind == minBool: + i.push true.newVal + else: + i.push false.newVal + + .symbol("quotation?") do (i: In): + if i.peek.kind == minQuotation: + i.push true.newVal + else: + i.push false.newVal - .symbol("float?") do (i: In): - if i.peek.kind == minFloat: - i.push true.newVal - else: - i.push false.newVal + .symbol("object?") do (i: In): + if i.peek.isObject: + i.push true.newVal + else: + i.push false.newVal - .symbol("number?") do (i: In): - if i.peek.kind == minFloat or i.peek.kind == minInt: - i.push true.newVal - else: - i.push false.newVal + .symbol("module?") do (i: In): + if i.peek.isObject and i.peek.objType == "module": + i.push true.newVal + else: + i.push false.newVal - .symbol("bool?") do (i: In): - if i.peek.kind == minBool: - i.push true.newVal - else: - i.push false.newVal + .symbol("scope?") do (i: In): + if i.peek.isObject and i.peek.objType == "scope": + i.push true.newVal + else: + i.push false.newVal - .symbol("quotation?") do (i: In): - if i.peek.kind == minQuotation: - i.push true.newVal - else: - i.push false.newVal - - .symbol("object?") do (i: In): - if i.peek.isObject: - i.push true.newVal - else: - i.push false.newVal - - .symbol("module?") do (i: In): - if i.peek.isObject and i.peek.objType == "module": - i.push true.newVal - else: - i.push false.newVal - - .symbol("scope?") do (i: In): - if i.peek.isObject and i.peek.objType == "scope": - i.push true.newVal - else: - i.push false.newVal - - .finalize() + .finalize()
M lib/min_net.nimlib/min_net.nim

@@ -7,123 +7,125 @@ ../core/utils

# Network -define("net") +proc net_module*(i: In)= - .symbol("^socket") do (i: In): - 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): - raiseInvalid("Quotation must contain three symbols for <domain> <type> <protocol>") - let vals = q.qVal - if not ["ipv4", "ipv6"].contains(vals[0].symVal): - raiseInvalid("Domain symbol must be 'ipv4' or 'ipv6'") - if not ["stream", "dgram"].contains(vals[1].symVal): - raiseInvalid("Type symbol must be 'stream' or 'dgram'") - if not ["tcp", "udp"].contains(vals[2].symVal): - raiseInvalid("Protocol symbol must be 'tcp' or 'udp'") - var - domain: Domain - sockettype: SockType - protocol: Protocol - # Process domain - if vals[0].symVal == "ipv4": - domain = AF_INET - else: - domain = AF_INET6 - if vals[1].symVal == "stream": - sockettype = SOCK_STREAM - else: - sockettype = SOCK_DGRAM - if vals[2].symVal == "tcp": - protocol = IPPROTO_TCP - else: - protocol = IPPROTO_UDP - var socket = newSocket(domain, sockettype, protocol) - q.objType = "socket" - q.obj = socket[].addr - i.newScope("<socket>", q) - - q.scope - .symbol("domain") do (i: In): - i.push q - i.push q.qVal[0].symVal.newVal + i.define("net") - .symbol("type") do (i: In): - i.push q - i.push q.qVal[1].symVal.newVal - - .symbol("protocol") do (i: In): - i.push q - i.push q.qVal[2].symVal.newVal - - .symbol("close") do (i: In): - q.to(Socket).close() - - .symbol("listen") do (i: In): - var port: MinValue - i.reqInt port - var socket = q.to(Socket) - socket.bindAddr(Port(port.intVal)) - q.qVal.add "0.0.0.0".newSym - q.qVal.add port - q.scope - .symbol("address") do (i:In): - i.push "0.0.0.0".newVal - .symbol("port") do (i:In): - i.push port - .finalize() - socket.listen() - i.push q - - .symbol("accept") do (i: In): - # Open same socket type as server - i.eval "$1 net %^socket" % [$q.qVal[0..2].newVal] - var clientVal: MinValue - i.reqObject "socket", clientVal - var client = clientVal.to(Socket) - var address = "" - q.to(Socket).acceptAddr(client, address) - clientVal.qVal.add address.newSym - i.push clientVal - - .symbol("connect") do (i: In): - var q, address, port: MinValue - i.reqInt port - i.reqString address - q.to(Socket).connect(address.strVal, Port(port.intVal)) - q.qVal.add address.strVal.newSym - q.qVal.add port - q.scope - .symbol("client-address") do (i:In): - i.push address.strVal.newVal - .symbol("client-port") do (i:In): - i.push port - .finalize() - i.push q - - .symbol("send") do (i: In): - var s: MinValue - i.reqString s - q.to(Socket).send s.strVal - i.push q - - .symbol("recv") do (i: In): - var size: MinValue - i.reqInt size - var s = "" - discard q.to(Socket).recv(s, size.intVal.int) - i.push q - i.push s.newVal + .symbol("^socket") do (i: In): + 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): + raiseInvalid("Quotation must contain three symbols for <domain> <type> <protocol>") + let vals = q.qVal + if not ["ipv4", "ipv6"].contains(vals[0].symVal): + raiseInvalid("Domain symbol must be 'ipv4' or 'ipv6'") + if not ["stream", "dgram"].contains(vals[1].symVal): + raiseInvalid("Type symbol must be 'stream' or 'dgram'") + if not ["tcp", "udp"].contains(vals[2].symVal): + raiseInvalid("Protocol symbol must be 'tcp' or 'udp'") + var + domain: Domain + sockettype: SockType + protocol: Protocol + # Process domain + if vals[0].symVal == "ipv4": + domain = AF_INET + else: + domain = AF_INET6 + if vals[1].symVal == "stream": + sockettype = SOCK_STREAM + else: + sockettype = SOCK_DGRAM + if vals[2].symVal == "tcp": + protocol = IPPROTO_TCP + else: + protocol = IPPROTO_UDP + var socket = newSocket(domain, sockettype, protocol) + q.objType = "socket" + q.obj = socket[].addr + i.newScope("<socket>", q) - .symbol("recv-line") do (i: In): - var s = "" - q.to(Socket).readLine(s) - i.push @[q] - i.push s.newVal + q.scope + .symbol("domain") do (i: In): + i.push q + i.push q.qVal[0].symVal.newVal + + .symbol("type") do (i: In): + i.push q + i.push q.qVal[1].symVal.newVal + + .symbol("protocol") do (i: In): + i.push q + i.push q.qVal[2].symVal.newVal + + .symbol("close") do (i: In): + q.to(Socket).close() + + .symbol("listen") do (i: In): + var port: MinValue + i.reqInt port + var socket = q.to(Socket) + socket.bindAddr(Port(port.intVal)) + q.qVal.add "0.0.0.0".newSym + q.qVal.add port + q.scope + .symbol("address") do (i:In): + i.push "0.0.0.0".newVal + .symbol("port") do (i:In): + i.push port + .finalize() + socket.listen() + i.push q + + .symbol("accept") do (i: In): + # Open same socket type as server + i.eval "$1 net %^socket" % [$q.qVal[0..2].newVal] + var clientVal: MinValue + i.reqObject "socket", clientVal + var client = clientVal.to(Socket) + var address = "" + q.to(Socket).acceptAddr(client, address) + clientVal.qVal.add address.newSym + i.push clientVal + + .symbol("connect") do (i: In): + var q, address, port: MinValue + i.reqInt port + i.reqString address + q.to(Socket).connect(address.strVal, Port(port.intVal)) + q.qVal.add address.strVal.newSym + q.qVal.add port + q.scope + .symbol("client-address") do (i:In): + i.push address.strVal.newVal + .symbol("client-port") do (i:In): + i.push port + .finalize() + i.push q + + .symbol("send") do (i: In): + var s: MinValue + i.reqString s + q.to(Socket).send s.strVal + i.push q + + .symbol("recv") do (i: In): + var size: MinValue + i.reqInt size + var s = "" + discard q.to(Socket).recv(s, size.intVal.int) + i.push q + i.push s.newVal + + .symbol("recv-line") do (i: In): + var s = "" + q.to(Socket).readLine(s) + i.push @[q] + i.push s.newVal + + .finalize() + + i.push q - .finalize() - - i.push q - - .finalize() + .finalize()
M lib/min_num.nimlib/min_num.nim

@@ -7,72 +7,74 @@ ../core/utils

# Arithmetic -define("num") +proc num_module*(i: In)= - .symbol("+") do (i: In): - var a, b: MinValue - i.reqTwoNumbers a, b - if a.isInt: - if b.isInt: - i.push newVal(a.intVal + b.intVal) - else: - i.push newVal(a.intVal.float + b.floatVal) - else: - if b.isFloat: - i.push newVal(a.floatVal + b.floatVal) - else: - i.push newVal(a.floatVal + b.intVal.float) - - .symbol("-") do (i: In): - var a, b: MinValue - i.reqTwoNumbers a, b - if a.isInt: - if b.isInt: - i.push newVal(b.intVal - a.intVal) - else: - i.push newVal(b.floatVal - a.intVal.float) - else: - if b.isFloat: - i.push newVal(b.floatVal - a.floatVal) - else: - i.push newVal(b.intVal.float - a.floatVal) + i.define("num") - .symbol("*") do (i: In): - var a, b: MinValue - i.reqTwoNumbers a, b - if a.isInt: - if b.isInt: - i.push newVal(a.intVal * b.intVal) + .symbol("+") do (i: In): + var a, b: MinValue + i.reqTwoNumbers a, b + if a.isInt: + if b.isInt: + i.push newVal(a.intVal + b.intVal) + else: + i.push newVal(a.intVal.float + b.floatVal) else: - i.push newVal(a.intVal.float * b.floatVal) - else: - if b.isFloat: - i.push newVal(a.floatVal * b.floatVal) + if b.isFloat: + i.push newVal(a.floatVal + b.floatVal) + else: + i.push newVal(a.floatVal + b.intVal.float) + + .symbol("-") do (i: In): + var a, b: MinValue + i.reqTwoNumbers a, b + if a.isInt: + if b.isInt: + i.push newVal(b.intVal - a.intVal) + else: + i.push newVal(b.floatVal - a.intVal.float) else: - i.push newVal(a.floatVal * b.intVal.float) - - .symbol("/") do (i: In): - var a, b: MinValue - i.reqTwoNumbers a, b - if a.isInt: - if b.isInt: - i.push newVal(b.intVal.int / a.intVal.int) + if b.isFloat: + i.push newVal(b.floatVal - a.floatVal) + else: + i.push newVal(b.intVal.float - a.floatVal) + + .symbol("*") do (i: In): + var a, b: MinValue + i.reqTwoNumbers a, b + if a.isInt: + if b.isInt: + i.push newVal(a.intVal * b.intVal) + else: + i.push newVal(a.intVal.float * b.floatVal) else: - i.push newVal(b.floatVal / a.intVal.float) - else: - if b.isFloat: - i.push newVal(b.floatVal / a.floatVal) + if b.isFloat: + i.push newVal(a.floatVal * b.floatVal) + else: + i.push newVal(a.floatVal * b.intVal.float) + + .symbol("/") do (i: In): + var a, b: MinValue + i.reqTwoNumbers a, b + if a.isInt: + if b.isInt: + i.push newVal(b.intVal.int / a.intVal.int) + else: + i.push newVal(b.floatVal / a.intVal.float) else: - i.push newVal(b.intVal.float / a.floatVal) - - .symbol("div") do (i: In): - var a, b: MinValue - i.reqTwoInts b, a - i.push(newVal(a.intVal div b.intVal)) - - .symbol("mod") do (i: In): - var a, b: MinValue - i.reqTwoInts b, a - i.push(newVal(a.intVal mod b.intVal)) - - .finalize() + if b.isFloat: + i.push newVal(b.floatVal / a.floatVal) + else: + i.push newVal(b.intVal.float / a.floatVal) + + .symbol("div") do (i: In): + var a, b: MinValue + i.reqTwoInts b, a + i.push(newVal(a.intVal div b.intVal)) + + .symbol("mod") do (i: In): + var a, b: MinValue + i.reqTwoInts b, a + i.push(newVal(a.intVal mod b.intVal)) + + .finalize()
M lib/min_stack.nimlib/min_stack.nim

@@ -7,40 +7,42 @@ ../core/utils

# Common stack operations -define("stack") - .symbol("id") do (i: In): - discard - - .symbol("pop") do (i: In): - if i.stack.len < 1: - raiseEmptyStack() - discard i.pop - - .symbol("dup") do (i: In): - i.push i.peek +proc stack_module*(i: In) = + i.define("stack") - .symbol("dip") do (i: In): - var q: MinValue - i.reqQuotation q - let v = i.pop - i.unquote("<dip>", q) - i.push v - - .symbol("swap") do (i: In): - if i.stack.len < 2: - raiseEmptyStack() - let a = i.pop - let b = i.pop - i.push a - i.push b - - .symbol("sip") do (i: In): - var a, b: MinValue - i.reqTwoQuotations a, b - i.push b - i.unquote("<sip>", a) - i.push b - - .finalize() + .symbol("id") do (i: In): + discard + + .symbol("pop") do (i: In): + if i.stack.len < 1: + raiseEmptyStack() + discard i.pop + + .symbol("dup") do (i: In): + i.push i.peek + + .symbol("dip") do (i: In): + var q: MinValue + i.reqQuotation q + let v = i.pop + i.unquote("<dip>", q) + i.push v + + .symbol("swap") do (i: In): + if i.stack.len < 2: + raiseEmptyStack() + let a = i.pop + let b = i.pop + i.push a + i.push b + + .symbol("sip") do (i: In): + var a, b: MinValue + i.reqTwoQuotations a, b + i.push b + i.unquote("<sip>", a) + i.push b + .finalize() +
M lib/min_str.nimlib/min_str.nim

@@ -6,7 +6,10 @@ ../core/interpreter,

../core/utils, ../core/regex -define("str") + + +proc str_module*(i: In) = + i.define("str") .symbol("split") do (i: In): var sep, s: MinValue
M lib/min_sys.nimlib/min_sys.nim

@@ -7,101 +7,103 @@ ../core/utils

# OS -define("sys") - .symbol("pwd") do (i: In): - i.push newVal(getCurrentDir()) - - .symbol("cd") do (i: In): - var f: MinValue - i.reqString f - f.strVal.setCurrentDir - - .symbol("ls") do (i: In): - var a: MinValue - i.reqString a - var list = newSeq[MinValue](0) - for i in walkDir(a.strVal): - list.add newVal(i.path) - i.push list.newVal - - .symbol("ls-r") do (i: In): - var a: MinValue - i.reqString a - var list = newSeq[MinValue](0) - for i in walkDirRec(a.strVal): - list.add newVal(i) - i.push list.newVal - - .symbol("system") do (i: In): - var a: MinValue - i.reqString a - i.push execShellCmd(a.strVal).newVal - - .symbol("run") do (i: In): - var a: MinValue - i.reqString a - let words = a.strVal.split(" ") - let cmd = words[0] - var args = newSeq[string](0) - if words.len > 1: - args = words[1..words.len-1] - i.push execProcess(cmd, args, nil, {poUsePath}).newVal - - .symbol("getenv") do (i: In): - var a: MinValue - i.reqString a - i.push a.strVal.getEnv.newVal - - .symbol("putenv") do (i: In): - var key, value: MinValue - i.reqTwoStrings key, value - key.strVal.putEnv value.strVal +proc sys_module*(i: In)= + i.define("sys") - .symbol("os") do (i: In): - i.push hostOS.newVal - - .symbol("cpu") do (i: In): - i.push hostCPU.newVal - - .symbol("file?") do (i: In): - var f: MinValue - i.reqString f - i.push f.strVal.fileExists.newVal - - .symbol("dir?") do (i: In): - var f: MinValue - i.reqString f - i.push f.strVal.dirExists.newVal - - .symbol("rm") do (i: In): - var f: MinValue - i.reqString f - f.strVal.removeFile - - .symbol("cp") do (i: In): - var a, b: MinValue - i.reqTwoStrings a, b - copyFile b.strVal, a.strVal + .symbol("pwd") do (i: In): + i.push newVal(getCurrentDir()) + + .symbol("cd") do (i: In): + var f: MinValue + i.reqString f + f.strVal.setCurrentDir + + .symbol("ls") do (i: In): + var a: MinValue + i.reqString a + var list = newSeq[MinValue](0) + for i in walkDir(a.strVal): + list.add newVal(i.path) + i.push list.newVal + + .symbol("ls-r") do (i: In): + var a: MinValue + i.reqString a + var list = newSeq[MinValue](0) + for i in walkDirRec(a.strVal): + list.add newVal(i) + i.push list.newVal - .symbol("mv") do (i: In): - var a, b: MinValue - i.reqTwoStrings a, b - moveFile b.strVal, a.strVal + .symbol("system") do (i: In): + var a: MinValue + i.reqString a + i.push execShellCmd(a.strVal).newVal + + .symbol("run") do (i: In): + var a: MinValue + i.reqString a + let words = a.strVal.split(" ") + let cmd = words[0] + var args = newSeq[string](0) + if words.len > 1: + args = words[1..words.len-1] + i.push execProcess(cmd, args, nil, {poUsePath}).newVal + + .symbol("getenv") do (i: In): + var a: MinValue + i.reqString a + i.push a.strVal.getEnv.newVal + + .symbol("putenv") do (i: In): + var key, value: MinValue + i.reqTwoStrings key, value + key.strVal.putEnv value.strVal + + .symbol("os") do (i: In): + i.push hostOS.newVal + + .symbol("cpu") do (i: In): + i.push hostCPU.newVal + + .symbol("file?") do (i: In): + var f: MinValue + i.reqString f + i.push f.strVal.fileExists.newVal + + .symbol("dir?") do (i: In): + var f: MinValue + i.reqString f + i.push f.strVal.dirExists.newVal + + .symbol("rm") do (i: In): + var f: MinValue + i.reqString f + f.strVal.removeFile + + .symbol("cp") do (i: In): + var a, b: MinValue + i.reqTwoStrings a, b + copyFile b.strVal, a.strVal + + .symbol("mv") do (i: In): + var a, b: MinValue + i.reqTwoStrings a, b + moveFile b.strVal, a.strVal + + .symbol("rmdir") do (i: In): + var f: MinValue + i.reqString f + f.strVal.removeDir + + .symbol("mkdir") do (i: In): + var f: MinValue + i.reqString f + f.strVal.createDir - .symbol("rmdir") do (i: In): - var f: MinValue - i.reqString f - f.strVal.removeDir + .symbol("sleep") do (i: In): + var ms: MinValue + i.reqInt ms + sleep ms.intVal.int - .symbol("mkdir") do (i: In): - var f: MinValue - i.reqString f - f.strVal.createDir - - .symbol("sleep") do (i: In): - var ms: MinValue - i.reqInt ms - sleep ms.intVal.int - - .finalize() + .finalize()
M lib/min_time.nimlib/min_time.nim

@@ -7,13 +7,15 @@ ../core/utils

# Time -define("time") - .symbol("timestamp") do (i: In): - i.push getTime().int.newVal +proc time_module*(i: In)= + i.define("time") - .symbol("now") do (i: In): - i.push epochTime().newVal - - .finalize() - + .symbol("timestamp") do (i: In): + i.push getTime().int.newVal + + .symbol("now") do (i: In): + i.push epochTime().newVal + + .finalize() +
M lib/prelude.minlib/prelude.min

@@ -43,8 +43,8 @@ 'quote :'

'unquote :i 'unquote :apply 'unquote :-> -'multiunquote :multiapply -'multiunquote :->> +;'multiunquote :multiapply +;'multiunquote :->> 'scope :=> 'filter :select 'clear :empty
M minim.nimminim.nim

@@ -1,8 +1,10 @@

import streams, critbits, parseopt2, strutils, os import + core/types, core/parser, core/interpreter, core/utils, + core/server, vendor/linenoise import lib/min_lang,

@@ -17,11 +19,12 @@ lib/min_net

const version* = "1.0.0-dev" var REPL = false -const prelude = "lib/prelude.min".slurp.strip +var DEBUGGING = false const USE_LINENOISE = true +const PRELUDE* = "lib/prelude.min".slurp.strip let usage* = " MiNiM v" & version & " - a tiny concatenative programming language" & """

@@ -44,46 +47,61 @@ var w = if words.len > 0: words.pop else: ""

var sep = "" if words.len > 0: sep = " " - for s in ROOT.symbols.keys: - if startsWith(s, w): - linenoiseAddCompletion completions, words.join(" ") & sep & s + # TODO REDO + #for s in ROOT.symbols.keys: + # if startsWith(s, w): + # linenoiseAddCompletion completions, words.join(" ") & sep & s proc prompt(s: string): string = var res = linenoise(s) discard $linenoiseHistoryAdd(res) return $res -proc minimStream(s: Stream, filename: string) = - var i = INTERPRETER + +proc stdLib(i: In) = + i.lang_module + i.io_module + i.logic_module + i.net_module + i.num_module + i.stack_module + i.str_module + i.sys_module + i.time_module + i.eval PRELUDE + + +proc minimStream(s: Stream, filename: string, debugging = false) = + var i = newMinInterpreter(debugging) i.pwd = filename.parentDir - i.eval prelude + i.stdLib() i.open(s, filename) discard i.parser.getToken() i.interpret() i.close() -proc minimString*(buffer: string) = - minimStream(newStringStream(buffer), "input") +proc minimString*(buffer: string, debugging = false) = + minimStream(newStringStream(buffer), "input", debugging) -proc minimFile*(filename: string) = +proc minimFile*(filename: string, debugging = false) = var stream = newFileStream(filename, fmRead) if stream == nil: stderr.writeLine("Error - Cannot read from file: "& filename) stderr.flushFile() - minimStream(stream, filename) + minimStream(stream, filename, debugging) -proc minimFile*(file: File, filename="stdin") = +proc minimFile*(file: File, filename="stdin", debugging = false) = var stream = newFileStream(stdin) if stream == nil: stderr.writeLine("Error - Cannot read from "& filename) stderr.flushFile() - minimStream(stream, filename) + minimStream(stream, filename, debugging) -proc minimRepl*() = - var i = INTERPRETER +proc minimRepl*(debugging = false) = + var i = newMinInterpreter(debugging) + i.stdLib() var s = newStringStream("") i.open(s, "") echo "MiNiM v"&version&" - REPL initialized." - i.eval prelude echo "-> Type 'exit' or 'quit' to exit." when USE_LINENOISE: linenoiseSetCompletionCallback completionCallback

@@ -112,7 +130,7 @@ file = key

of cmdLongOption, cmdShortOption: case key: of "debug", "d": - INTERPRETER.debugging = true + DEBUGGING = true of "evaluate", "e": s = val of "help", "h":

@@ -129,12 +147,12 @@ else:

discard if s != "": - minimString(s) + minimString(s, DEBUGGING) elif file != "": - minimFile file + minimFile file, DEBUGGING elif REPL: - minimRepl() + minimRepl DEBUGGING quit(0) else: - minimFile stdin + minimFile stdin, "stdin", DEBUGGING
M tests/lang.mintests/lang.min

@@ -7,7 +7,7 @@

"Total Symbols: " print! symbols size put! " Total Sigils: " print! sigils size put! - (symbols size 180 ==) assert + (symbols size 177 ==) assert (sigils size 12 ==) assert

@@ -70,7 +70,6 @@

(1 (2 3) cons (1 2 3) ==) assert ((1 2 3 4) 2 at 3 ==) assert - ((1 2 3) size 3 ==) assert ((1 2 3 4) 5 contains false ==) assert

@@ -113,13 +112,13 @@ (() 1 at)

(1) ) try 1 ==) assert - ( - ((dup 0 ==) (1 +) (dup 1 -) ( * ) linrec) :factorial - ( - (8 factorial) - (12 factorial) - (9 factorial) - ) ->> (40320 479001600 362880) ==) assert + ;( + ; ((dup 0 ==) (1 +) (dup 1 -) ( * ) linrec) :factorial + ; ( + ; (8 factorial) + ; (12 factorial) + ; (9 factorial) + ; ) ->> (40320 479001600 362880) ==) assert ((a b +) (4 :a 5 :b) with 9 ==) assert