all repos — min @ 38f9676ebfb1e81a574ec7fbabfe106e276a59e0

A small but practical concatenative programming language.

Implemented sigils.
h3rald h3rald@h3rald.com
Sun, 14 Dec 2014 16:58:04 +0100
commit

38f9676ebfb1e81a574ec7fbabfe106e276a59e0

parent

1de1f3d51a6537dc7d391d86a12a67c16bd4fec7

6 files changed, 170 insertions(+), 107 deletions(-)

jump to
M core/interpreter.nimcore/interpreter.nim

@@ -10,6 +10,7 @@ filename*: string

debugging*: bool evaluating*: bool TMinOperator* = proc (i: var TMinInterpreter) + TMinSigil* = proc (i: var TMinInterpreter, sym: string) TMinError* = enum errSystem, errParser,

@@ -35,7 +36,7 @@ "Division by zero"

] var SYMBOLS* = initOrderedTable[string, TMinOperator]() -var ALIASES* = newSeq[string](0) +var SIGILS* = initOrderedTable[string, TMinOperator]() proc newMinInterpreter*(debugging = false): TMinInterpreter = var s:TMinStack = newSeq[TMinValue](0)

@@ -73,13 +74,23 @@ i.debug val

if val.kind == minSymbol: if not i.evaluating: i.currSym = val + let symbol = val.symVal + let sigil = "" & symbol[0] if SYMBOLS.hasKey(val.symVal): try: SYMBOLS[val.symVal](i) except: i.error(errSystem, getCurrentExceptionMsg()) else: - i.error(errUndefined, "Undefined symbol: '"&val.symVal&"'") + if SIGILS.hasKey(sigil) and symbol.len > 1: + let sym = symbol[1..symbol.len-1] + try: + i.stack.add(TMinValue(kind: minString, strVal: sym)) + SIGILS[sigil](i) + except: + i.error(errSystem, getCurrentExceptionMsg()) + else: + i.error(errUndefined, "Undefined symbol: '"&val.symVal&"'") else: i.stack.add(val)
M core/utils.nimcore/utils.nim

@@ -4,15 +4,18 @@

proc sortSymbols() = SYMBOLS.sort(proc (a, b):int = return a.key.cmpIgnoreCase(b.key)) +proc sortSigils() = + SIGILS.sort(proc (a, b):int = return a.key.cmpIgnoreCase(b.key)) + template minsym*(name: string, body: stmt): stmt {.immediate.} = SYMBOLS[name] = proc (i: var TMinInterpreter) = body sortSymbols() -proc minalias*(newname: string, oldname: string) = - SYMBOLS[newname] = SYMBOLS[oldname] - ALIASES.add newname - sortSymbols() +template minsigil*(name: char, body: stmt): stmt {.immediate.} = + SIGILS[name] = proc (i: var TMinInterpreter) = + body + sortSigils() proc isSymbol*(s: TMinValue): bool = return s.kind == minSymbol
M lib/lang.nimlib/lang.nim

@@ -10,12 +10,6 @@ for s in SYMBOLS.keys:

q.add s.newVal i.push q.newVal -minsym "aliases": - var q = newSeq[TMinValue](0) - for s in ALIASES: - q.add s.newVal - i.push q.newVal - minsym "debug?": i.push i.debugging.newVal

@@ -25,15 +19,41 @@ echo "Debugging: $1" % [$i.debugging]

# Language constructs -minsym "def": - let q1 = i.pop +minsym "symbol": + var q1 = i.pop + var q2 = i.pop + if not q1.isQuotation: + q1 = @[q1].newVal + if not q2.isQuotation: + q2 = @[q2].newVal + if q1.qVal.len == 1 and q1.qVal[0].kind == minSymbol: + var symbol = q1.qVal[0].symVal + if SYMBOLS.hasKey(symbol): + i.error errSystem, "Symbol '$1' already exists" % [symbol] + minsym symbol: + i.evaluating = true + i.push q2.qVal + i.evaluating = false + else: + i.error errIncorrect, "The top quotation must contain only one symbol value" + +minsym "sigil": + 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: - minsym q1.qVal[0].symVal: - i.evaluating = true - i.push q2.qVal - i.evaluating = false + var symbol = q1.qVal[0].symVal + if symbol.len == 1: + if SIGILS.hasKey(symbol): + i.error errSystem, "Sigil '$1' already exists" % [symbol] + minsigil symbol: + 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, "The top quotation must contain only one symbol value" else:

@@ -66,93 +86,6 @@

minsym "stack": var s = i.stack i.push s - -# Operations on quotations - -minsym "quote": - let a = i.pop - i.push TMinValue(kind: minQuotation, qVal: @[a]) - -minsym "unquote": - let q = i.pop - if not q.isQuotation: - i.error errNoQuotation - for item in q.qVal: - i.push item - -minsym "cons": - var q = i.pop - let v = i.pop - if not q.isQuotation: - i.error errNoQuotation - q.qVal.add v - i.push q - -minsym "map": - let prog = i.pop - let list = i.pop - if prog.isQuotation and list.isQuotation: - i.push newVal(newSeq[TMinValue](0)) - for litem in list.qVal: - i.push litem - for pitem in prog.qVal: - i.push pitem - i.apply("swap") - i.apply("cons") - else: - i.error(errIncorrect, "Two quotations are required on the stack") - -minsym "ifte": - let fpath = i.pop - let tpath = i.pop - let check = i.pop - if check.isQuotation and tpath.isQuotation and fpath.isQuotation: - i.push check.qVal - let res = i.pop - if res.isBool and res.boolVal == true: - i.push tpath.qVal - else: - i.push fpath.qVal - else: - i.error(errIncorrect, "Three quotations are required on the stack") - -minsym "while": - let d = i.pop - let b = i.pop - if b.isQuotation and d.isQuotation: - i.push b.qVal - var check = i.pop - while check.isBool and check.boolVal == true: - i.push d.qVal - i.push b.qVal - check = i.pop - else: - i.error(errIncorrect, "Two quotations are required on the stack") - -minsym "filter": - let filter = i.pop - let list = i.pop - var res = newSeq[TMinValue](0) - if filter.isQuotation and list.isQuotation: - for e in list.qVal: - i.push e - i.push filter.qVal - var check = i.pop - if check.isBool and check.boolVal == true: - res.add e - i.push res.newVal - else: - i.error(errIncorrect, "Two quotations are required on the stack") - -minsym "linrec": - var r2 = i.pop - var r1 = i.pop - var t = i.pop - var p = i.pop - if p.isQuotation and t.isQuotation and r1.isQuotation and r2.isQuotation: - i.linrec(p, t, r1, r2) - else: - i.error(errIncorrect, "Four quotations are required on the stack") # Operations on quotations or strings
M lib/prelude.minlib/prelude.min

@@ -1,5 +1,5 @@

// Aliases -[def] [:] def +[symbol] [:] symbol [exit] [quit] : [concat] [,] : [print] [%] :

@@ -13,7 +13,6 @@ [puts] [echo] :

[system] [!] : [run] [&] : [getenv] [$] : -[setenv] [$=] : [pop] [zap] : [quote] [unit] : [unquote] [i] :

@@ -21,6 +20,8 @@ [unquote] [apply] :

[filter] [select] : [clear] [empty] : [match] [~] : +["."] [.] : +[".."] [..] : // Common Environment Variables [os "windows" ==]

@@ -38,6 +39,12 @@ ["PATH" $] [$PATH] :

[$HOME] [$HOMEPATH] : [$USER] [$USERNAME] : +// Common sigils +[quote [eval] concat] ['] sigil // 'test -> [test] +[getenv] [$] sigil +[:] [:] sigil +[!] [!] sigil +[&] [&] sigil // Mathematical Operators [1 +] [succ] :
A lib/quotations.nim

@@ -0,0 +1,108 @@

+import tables +import ../core/parser, ../core/interpreter, ../core/utils + +# Operations on quotations + +minsym "quote": + let a = i.pop + i.push TMinValue(kind: minQuotation, qVal: @[a]) + +minsym "unquote": + let q = i.pop + if not q.isQuotation: + i.error errNoQuotation + for item in q.qVal: + i.push item + +minsym "cons": + var q = i.pop + let v = i.pop + if not q.isQuotation: + i.error errNoQuotation + q.qVal.add v + i.push q + +minsym "at": + var index = i.pop + var q = i.pop + if index.isInt and q.isQuotation: + i.push q.qVal[index.intVal] + else: + i.error errIncorrect, "An integer and a quotation are required on the stack" + +minsym "map": + let prog = i.pop + let list = i.pop + if prog.isQuotation and list.isQuotation: + i.push newVal(newSeq[TMinValue](0)) + for litem in list.qVal: + i.push litem + for pitem in prog.qVal: + i.push pitem + i.apply("swap") + i.apply("cons") + else: + i.error(errIncorrect, "Two quotations are required on the stack") + +minsym "times": + let t = i.pop + let prog = i.pop + if t.isInt and prog.isQuotation: + for c in 1..t.intVal: + for pitem in prog.qVal: + i.push pitem + else: + i.error errIncorrect, "An integer and a quotation are required on the stack" + +minsym "ifte": + let fpath = i.pop + let tpath = i.pop + let check = i.pop + if check.isQuotation and tpath.isQuotation and fpath.isQuotation: + i.push check.qVal + let res = i.pop + if res.isBool and res.boolVal == true: + i.push tpath.qVal + else: + i.push fpath.qVal + else: + i.error(errIncorrect, "Three quotations are required on the stack") + +minsym "while": + let d = i.pop + let b = i.pop + if b.isQuotation and d.isQuotation: + i.push b.qVal + var check = i.pop + while check.isBool and check.boolVal == true: + i.push d.qVal + i.push b.qVal + check = i.pop + else: + i.error(errIncorrect, "Two quotations are required on the stack") + +minsym "filter": + let filter = i.pop + let list = i.pop + var res = newSeq[TMinValue](0) + if filter.isQuotation and list.isQuotation: + for e in list.qVal: + i.push e + i.push filter.qVal + var check = i.pop + if check.isBool and check.boolVal == true: + res.add e + i.push res.newVal + else: + i.error(errIncorrect, "Two quotations are required on the stack") + +minsym "linrec": + var r2 = i.pop + var r1 = i.pop + var t = i.pop + var p = i.pop + if p.isQuotation and t.isQuotation and r1.isQuotation and r2.isQuotation: + i.linrec(p, t, r1, r2) + else: + i.error(errIncorrect, "Four quotations are required on the stack") +
M minim.nimminim.nim

@@ -6,6 +6,7 @@ core/utils

import lib/lang, lib/stack, + lib/quotations, lib/numbers, lib/logic, lib/time,