all repos — min @ 77d6d7fdbe07ff6f7c0b10961599e476d98560b8

A small but practical concatenative programming language.

feat(modules) Implemented native module, symbol and sigil definitions; refactoring.
h3rald h3rald@h3rald.com
Sat, 21 May 2016 22:41:49 +0200
commit

77d6d7fdbe07ff6f7c0b10961599e476d98560b8

parent

f7c8ccacbc3c0a2ff5d0ca7bde239e881157ec5a

M core/interpreter.nimcore/interpreter.nim

@@ -18,6 +18,8 @@ ]

var ROOT*: ref MinScope = new MinScope +ROOT.name = "ROOT" + proc getSymbol*(scope: ref MinScope, key: string): MinOperator = if scope.symbols.hasKey(key): return scope.symbols[key]

@@ -39,7 +41,6 @@ var i:MinInterpreter = MinInterpreter(

filename: "input", parser: pr, stack: st, - #scope: scope, scope: ROOT, debugging: debugging, currSym: MinValue(column: 1, line: 1, kind: minSymbol, symVal: "")

@@ -154,4 +155,4 @@ for i in i.stack:

s.add i return s - +var INTERPRETER* = newMinInterpreter()
M core/types.nimcore/types.nim

@@ -23,6 +23,7 @@ MinScope* = object

symbols*: CritBitTree[MinOperator] sigils*: CritBitTree[MinOperator] parent*: ref MinScope + name*: string stack*: MinStack MinValue* = object line*: int

@@ -76,6 +77,7 @@ currSym*: MinValue

filename*: string debugging*: bool evaluating*: bool + In* = var MinInterpreter MinOperator* = proc (i: var MinInterpreter) MinSigil* = proc (i: var MinInterpreter, sym: string) MinError* = enum
M core/utils.nimcore/utils.nim

@@ -1,15 +1,6 @@

import tables, strutils, macros, critbits import types, parser, interpreter - -template minsym*(name: string, i: expr, body: stmt): stmt {.immediate.} = - ROOT.symbols[name] = proc (i: var MinInterpreter) {.closure.} = - body - -template minsigil*(name: char, i: expr, body: stmt): stmt {.immediate.} = - ROOT.sigils[name] = proc (i: var MinInterpreter) = - body - proc isSymbol*(s: MinValue): bool = return s.kind == minSymbol

@@ -58,3 +49,41 @@ else:

i.push r1.qVal i.linrec(p, t, r1, r2) i.push r2.qVal + +proc previous*(scope: ref MinScope): ref MinScope = + if scope.parent.isNil: + return ROOT + else: + return scope.parent + +proc define*(name: string): ref MinScope = + var scope = new MinScope + scope.name = name + scope.parent = INTERPRETER.scope + return scope + +proc symbol*(scope: ref MinScope, sym: string, p: MinOperator): ref MinScope = + scope.symbols[sym] = p + scope.previous.symbols[scope.name & ":" & sym] = p + return scope + +proc sigil*(scope: ref MinScope, sym: string, p: MinOperator): ref MinScope = + scope.previous.sigils[sym] = p + return scope + +proc finalize*(scope: ref MinScope) = + var mdl = newSeq[MinValue](0).newVal + mdl.scope = scope + mdl.scope.previous.symbols[scope.name] = proc(i: var MinInterpreter) = + i.evaluating = true + i.push mdl + i.evaluating = false + +template minsym*(name: string, i: expr, body: stmt): stmt {.immediate.} = + ROOT.symbols[name] = proc (i: var MinInterpreter) {.closure.} = + body + +template minsigil*(name: char, i: expr, body: stmt): stmt {.immediate.} = + ROOT.sigils[name] = proc (i: var MinInterpreter) = + body +
M lib/lang.nimlib/lang.nim

@@ -5,176 +5,179 @@ ../core/parser,

../core/interpreter, ../core/utils -minsym "exit", i: - quit(0) +ROOT -minsym "symbols", i: - var q = newSeq[MinValue](0) - for s in i.scope.symbols.keys: - q.add s.newVal - i.push q.newVal + .symbol("exit") do (i: In): + quit(0) -minsym "sigils", i: - var q = newSeq[MinValue](0) - for s in i.scope.sigils.keys: - q.add s.newVal - i.push q.newVal + .symbol("symbols") do (i: In): + var q = newSeq[MinValue](0) + for s in i.scope.symbols.keys: + q.add s.newVal + i.push q.newVal -minsym "debug?", i: - i.push i.debugging.newVal + .symbol("sigils") do (i: In): + var q = newSeq[MinValue](0) + for s in i.scope.sigils.keys: + q.add s.newVal + i.push q.newVal -minsym "debug", i: - i.debugging = not i.debugging - echo "Debugging: $1" % [$i.debugging] + .symbol("debug?") do (i: In): + i.push i.debugging.newVal -# Language constructs + .symbol("debug") do (i: In): + i.debugging = not i.debugging + echo "Debugging: $1" % [$i.debugging] -minsym "bind", i: - var q2 = i.pop # new (can be a quoted symbol or a string) - var q1 = i.pop # existing (auto-quoted) - var symbol: string - if not q1.isQuotation: - q1 = @[q1].newVal - if q2.isString: - symbol = q2.strVal - 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" - if not i.scope.getSymbol(symbol).isNil: - i.error errSystem, "Symbol '$1' already exists" % [symbol] - i.scope.symbols[symbol] = proc(i: var MinInterpreter) = - i.evaluating = true - i.push q1.qVal - i.evaluating = false + # Language constructs -minsym "unbind", i: - var q1 = i.pop - if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: - var symbol = q1.qVal[0].symVal - i.scope.symbols.excl symbol - else: - i.error errIncorrect, "The top quotation must contain only one symbol value" + .symbol("set") do (i: In): + var q2 = i.pop # new (can be a quoted symbol or a string) + var q1 = i.pop # existing (auto-quoted) + var symbol: string + if not q1.isQuotation: + q1 = @[q1].newVal + if q2.isString: + symbol = q2.strVal + 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" + if not i.scope.getSymbol(symbol).isNil: + i.error errSystem, "Symbol '$1' already exists" % [symbol] + i.scope.symbols[symbol] = proc(i: var MinInterpreter) = + i.evaluating = true + i.push q1.qVal + i.evaluating = false + + .symbol("unset") do (i: In): + var q1 = i.pop + if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: + var symbol = q1.qVal[0].symVal + i.scope.symbols.excl symbol + else: + i.error errIncorrect, "The top quotation must contain only one symbol value" -minsym "define", i: - let name = i.pop - var code = i.pop - if not name.isString or not code.isQuotation: - 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.scope = new MinScope - code.scope = i.scope - i.scope.parent = scope - for item in code.qVal: - i.push item - let p = proc(i: var MinInterpreter) = - i.evaluating = true - i.push code - i.evaluating = false - let symbols = i.scope.symbols - i.scope = scope - i.scope.symbols[id] = p - # Define symbols in parent scope as well - for sym, val in symbols.pairs: - i.scope.symbols[id & ":" & sym] = val - i.stack = stack + .symbol("define") do (i: In): + let name = i.pop + var code = i.pop + if not name.isString or not code.isQuotation: + 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.scope = new MinScope + code.scope = i.scope + i.scope.parent = scope + for item in code.qVal: + i.push item + let p = proc(i: var MinInterpreter) = + i.evaluating = true + i.push code + i.evaluating = false + let symbols = i.scope.symbols + i.scope = scope + i.scope.symbols[id] = p + # Define symbols in parent scope as well + for sym, val in symbols.pairs: + i.scope.symbols[id & ":" & sym] = val + i.stack = stack -minsym "import", i: - var mdl: MinValue - try: - i.scope.getSymbol(i.pop.strVal)(i) - mdl = i.pop - except: - discard - if not mdl.isQuotation: - i.error errNoQuotation - if not mdl.scope.isNil: - for sym, val in mdl.scope.symbols.pairs: - i.scope.symbols[sym] = val + .symbol("import") do (i: In): + var mdl: MinValue + try: + i.scope.getSymbol(i.pop.strVal)(i) + mdl = i.pop + except: + discard + if not mdl.isQuotation: + i.error errNoQuotation + if not mdl.scope.isNil: + for sym, val in mdl.scope.symbols.pairs: + i.scope.symbols[sym] = val -minsigil "'", i: - i.push(@[MinValue(kind: minSymbol, symVal: i.pop.strVal)].newVal) + .sigil("'") do (i: In): + i.push(@[MinValue(kind: minSymbol, symVal: i.pop.strVal)].newVal) -minsym "sigil", i: - var q1 = i.pop - let q2 = i.pop - if q1.isString: - q1 = @[q1].newVal - if q1.isQuotation and q2.isQuotation: - if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: - var symbol = q1.qVal[0].symVal - if symbol.len == 1: - if not i.scope.getSigil(symbol).isNil: - i.error errSystem, "Sigil '$1' already exists" % [symbol] - i.scope.sigils[symbol] = proc(i: var MinInterpreter) = - i.evaluating = true - i.push q2.qVal - i.evaluating = false + .symbol("sigil") do (i: In): + var q1 = i.pop + let q2 = i.pop + if q1.isString: + q1 = @[q1].newVal + if q1.isQuotation and q2.isQuotation: + if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: + var symbol = q1.qVal[0].symVal + if symbol.len == 1: + if not i.scope.getSigil(symbol).isNil: + i.error errSystem, "Sigil '$1' already exists" % [symbol] + i.scope.sigils[symbol] = proc(i: var MinInterpreter) = + i.evaluating = true + i.push q2.qVal + i.evaluating = false + else: + i.error errIncorrect, "A sigil can only have one character" else: - i.error errIncorrect, "A sigil can only have one character" + i.error errIncorrect, "The top quotation must contain only one symbol value" else: - i.error errIncorrect, "The top quotation must contain only one symbol value" - else: - i.error errIncorrect, "Two quotations are required on the stack" + i.error errIncorrect, "Two quotations are required on the stack" -minsym "eval", i: - let s = i.pop - if s.isString: - i.eval s.strVal - else: - i.error(errIncorrect, "A string is required on the stack") + .symbol("eval") do (i: In): + let s = i.pop + if s.isString: + i.eval s.strVal + else: + i.error(errIncorrect, "A string is required on the stack") -minsym "load", i: - let s = i.pop - if s.isString: - i.load s.strVal - else: - i.error(errIncorrect, "A string is required on the stack") + .symbol("load") do (i: In): + let s = i.pop + if s.isString: + i.load s.strVal + else: + i.error(errIncorrect, "A string is required on the stack") + # Operations on the whole stack -# Operations on the whole stack + .symbol("clear") do (i: In): + while i.stack.len > 0: + discard i.pop -minsym "clear", i: - while i.stack.len > 0: - discard i.pop + .symbol("dump") do (i: In): + echo i.dump -minsym "dump", i: - echo i.dump + .symbol("stack") do (i: In): + var s = i.stack + i.push s -minsym "stack", i: - var s = i.stack - i.push s + # Operations on quotations or strings -# Operations on quotations or strings + .symbol("concat") do (i: In): + var q1 = i.pop + var q2 = i.pop + if q1.isString and q2.isString: + let s = q2.strVal & q1.strVal + i.push newVal(s) + elif q1.isQuotation and q2.isQuotation: + let q = q2.qVal & q1.qVal + i.push newVal(q) + else: + i.error(errIncorrect, "Two quotations or two strings are required on the stack") -minsym "concat", i: - var q1 = i.pop - var q2 = i.pop - if q1.isString and q2.isString: - let s = q2.strVal & q1.strVal - i.push newVal(s) - elif q1.isQuotation and q2.isQuotation: - let q = q2.qVal & q1.qVal - i.push newVal(q) - else: - i.error(errIncorrect, "Two quotations or two strings are required on the stack") + .symbol("first") do (i: In): + var q = i.pop + if q.isQuotation: + i.push q.qVal[0] + elif q.isString: + i.push newVal($q.strVal[0]) + else: + i.error(errIncorrect, "A quotation or a string is required on the stack") -minsym "first", i: - var q = i.pop - if q.isQuotation: - i.push q.qVal[0] - elif q.isString: - i.push newVal($q.strVal[0]) - else: - i.error(errIncorrect, "A quotation or a string is required on the stack") + .symbol("rest") do (i: In): + var q = i.pop + if q.isQuotation: + i.push newVal(q.qVal[1..q.qVal.len-1]) + elif q.isString: + i.push newVal(q.strVal[1..q.strVal.len-1]) + else: + i.error(errIncorrect, "A quotation or a string is required on the stack") -minsym "rest", i: - var q = i.pop - if q.isQuotation: - i.push newVal(q.qVal[1..q.qVal.len-1]) - elif q.isString: - i.push newVal(q.strVal[1..q.strVal.len-1]) - else: - i.error(errIncorrect, "A quotation or a string is required on the stack") + .finalize()
M lib/prelude.minlib/prelude.min

@@ -1,14 +1,18 @@

+(import) (#) sigil + +// Imports +#str + // Common sigils -(bind) (:) sigil +(set) (:) sigil (getenv) ($) sigil (system) (!) sigil (run) (&) sigil -(import) (#) sigil (define) (=) sigil (load) (@) sigil // Aliases -'bind :: +'set :: 'define := 'import :# 'exit :quit

@@ -60,4 +64,4 @@ ((() cons cons) dip swap i) :bury2

((() cons cons cons) dip swap i) :bury3 // Other -(dup unbind bind) :rebind +(dup unset set) :reset
A lib/str.nim

@@ -0,0 +1,53 @@

+import tables, strutils +import ../vendor/slre +import + ../core/types, + ../core/parser, + ../core/interpreter, + ../core/utils + +define("str") + + .symbol("split") do (i: In): + let sep = i.pop + let s = i.pop + if s.isString and sep.isString: + for e in s.strVal.split(sep.strVal): + i.push e.newVal + else: + i.error errIncorrect, "Two strings are required on the stack" + + .symbol("match") do (i: In): + let reg = i.pop + let str = i.pop + if str.isString and reg.isString: + var matches = str.strVal.match(reg.strVal) + var res = newSeq[MinValue](0) + for s in matches: + res.add s.newVal + i.push res.newVal + else: + i.error(errIncorrect, "Two strings are required on the stack") + + .symbol("match?") do (i: In): + let reg = i.pop + let str = i.pop + if str.isString and reg.isString: + var matches = str.strVal.match(reg.strVal) + if matches.len > 0: + i.push true.newVal + else: + i.push false.newVal + else: + i.error(errIncorrect, "Two strings are required on the stack") + + .symbol("replace") do (i: In): + let s_replace = i.pop + let reg = i.pop + let s_find = i.pop + if reg.isString and s_replace.isString and s_find.isString: + i.push s_find.strVal.gsub(reg.strVal, s_replace.strVal).newVal + else: + i.error(errIncorrect, "Three strings are required on the stack") + + .finalize()
D lib/strings.nim

@@ -1,49 +0,0 @@

-import tables, strutils -import ../vendor/slre -import - ../core/types, - ../core/parser, - ../core/interpreter, - ../core/utils - -minsym "split", i: - let sep = i.pop - let s = i.pop - if s.isString and sep.isString: - for e in s.strVal.split(sep.strVal): - i.push e.newVal - else: - i.error errIncorrect, "Two strings are required on the stack" - -minsym "match", i: - let reg = i.pop - let str = i.pop - if str.isString and reg.isString: - var matches = str.strVal.match(reg.strVal) - var res = newSeq[MinValue](0) - for s in matches: - res.add s.newVal - i.push res.newVal - else: - i.error(errIncorrect, "Two strings are required on the stack") - -minsym "match?", i: - let reg = i.pop - let str = i.pop - if str.isString and reg.isString: - var matches = str.strVal.match(reg.strVal) - if matches.len > 0: - i.push true.newVal - else: - i.push false.newVal - else: - i.error(errIncorrect, "Two strings are required on the stack") - -minsym "replace", i: - let s_replace = i.pop - let reg = i.pop - let s_find = i.pop - if reg.isString and s_replace.isString and s_find.isString: - i.push s_find.strVal.gsub(reg.strVal, s_replace.strVal).newVal - else: - i.error(errIncorrect, "Three strings are required on the stack")
M minim.nimminim.nim

@@ -6,16 +6,15 @@ core/utils

import lib/lang, lib/stack, - lib/quotations, - lib/numbers, - lib/strings, + lib/q, + lib/num, + lib/str, lib/logic, lib/time, lib/io, lib/sys const version* = "0.1.0" -var debugging = false var repl = false const prelude = "lib/prelude.min".slurp.strip

@@ -59,7 +58,7 @@ stdout.write(s)

return stdin.readLine proc minimStream(s: Stream, filename: string) = - var i = newMinInterpreter(debugging) + var i = INTERPRETER i.eval prelude i.open(s, filename) discard i.parser.getToken()

@@ -84,7 +83,7 @@ stderr.flushFile()

minimStream(stream, filename) proc minimRepl*() = - var i = newMinInterpreter(debugging) + var i = INTERPRETER var s = newStringStream("") i.open(s, "") echo "MiNiM v"&version&" - REPL initialized."

@@ -109,7 +108,7 @@ echo i.dump

### -var file, str: string = "" +var file, s: string = "" for kind, key, val in getopt(): case kind:

@@ -118,9 +117,9 @@ file = key

of cmdLongOption, cmdShortOption: case key: of "debug", "d": - debugging = true + INTERPRETER.debugging = true of "evaluate", "e": - str = val + s = val of "help", "h": echo usage quit(0)

@@ -133,8 +132,8 @@ discard

else: discard -if str != "": - minimString(str) +if s != "": + minimString(s) elif file != "": minimFile file elif repl: