all repos — min @ b829c87bdd1c6360e7924bbdc9e110112d68697d

A small but practical concatenative programming language.

Implemented operator operator
h3rald h3rald@h3rald.com
Mon, 28 Dec 2020 03:01:33 +0000
commit

b829c87bdd1c6360e7924bbdc9e110112d68697d

parent

a2c33d1ee4bfa82cd425a65548caac5efc24e03f

5 files changed, 124 insertions(+), 8 deletions(-)

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

@@ -20,6 +20,7 @@ parser

type MinTrappedException* = ref object of CatchableError + MinReturnException* = ref object of CatchableError MinRuntimeError* = ref object of CatchableError data*: MinValue

@@ -232,6 +233,8 @@ i.trace.add val

if not i.evaluating: i.currSym = val let symbol = val.symVal + if symbol == "return": + raise MinReturnException(msg: "return symbol found") if i.scope.hasSymbol(symbol): i.apply i.scope.getSymbol(symbol) else:
M core/utils.nimcore/utils.nim

@@ -176,7 +176,7 @@ return res.newVal

# Validators -proc validate(value: MinValue, t: string): bool = +proc validate*(value: MinValue, t: string): bool = case t: of "bool": return value.isBool

@@ -275,4 +275,4 @@ proc reqTwoQuotationsOrStrings*(i: var MinInterpreter, a, b: var MinValue) =

a = i.pop b = i.pop if not (a.isQuotation and b.isQuotation or a.isString and b.isString): - raiseInvalid("Two quotations or two strings are required on the stack")+ raiseInvalid("Two quotations or two strings are required on the stack")
M lib/min_lang.nimlib/min_lang.nim

@@ -177,6 +177,110 @@ i.scope = origscope

qscope.scope.symbols[sym] = MinOperator(kind: minProcOp, prc: op) ### End of symbols not present in minimin + + def.symbol("operator") do (i: In): + let vals = i.expect("quot"); + let q = vals[0] + if q.qVal.len != 4: + raiseInvalid("Invalid operator definition") + let tv = q.qVal[0] + if not tv.isSymbol or (tv.symVal != "symbol" and tv.symVal != "sigil"): + raiseInvalid("Incorrect operator type specified (it must be 'symbol' or 'sigil', found '$#')" % tv.symVal) + let t = tv.symVal + let nv = q.qVal[1] + if not nv.isSymbol: + raiseInvalid("Operator name must be a symbol") + let n = nv.symVal + when not defined(mini): + if not n.match(USER_SYMBOL_REGEX): + raiseInvalid("Operator name must not contain ivalid characters") + # Validate signature + let sv = q.qVal[2] + let tps = ["bool", "null", "int", "num", "float", "quot", "dict", "'sym", "sym", "string", "a"] + if not sv.isQuotation: + raiseInvalid("Signature must be a quotation") + elif sv.qVal.len == 0: + raiseInvalid("No signature specified") + elif sv.qVal.len == 1 and sv.qVal[0] != "==>".newVal: + raiseInvalid("Invalid signature") + elif sv.qVal.len mod 2 == 0: + raiseInvalid("Invalid signature") + var c = 0 + # Process signature + var inExpects= newSeq[string](0) + var inVars = newSeq[string](0) + var outExpects= newSeq[string](0) + var outVars = newSeq[string](0) + var o= false + for vv in sv.qVal: + if not vv.isSymbol: + raiseInvalid("Signature must be a quotation id symbols") + let v = vv.symVal + var check = c mod 2 == 0 + if o: + check = c mod 2 != 0 + if check: + if v == "==>": + o = true + elif not tps.contains(v) and not v.startsWith("dict:"): + raiseInvalid("Invalid type specified in signature at position $#" % $(c+1)) + else: + if o: + outExpects.add v + else: + inExpects.add v + else: + if v[0] != ':': + echo v + raiseInvalid("No mapping symbol specified in signature at position $#" % $(c+1)) + else: + if o: + outVars.add v[1..v.len-1] + else: + inVars.add v[1..v.len-1] + c.inc() + if not o: + raiseInvalid("No output specified in signature") + # Process body + var bv = q.qVal[3] + if not bv.isQuotation: + raiseInvalid("Body must be a quotation") + var p: MinOperatorProc = proc (i: In) = + var inVals = i.expect(inExpects) + inVals.reverse + let snapshot = i.stack + i.withScope(): + # Inject variables for mapped inputs + for k in 0..inVars.len-1: + i.scope.symbols[inVars[k]] = MinOperator(kind: minValOp, sealed: false, val: inVals[k], quotation: inVals[k].isQuotation) + # Inject variables for mapped outputs + for k in 0..outVars.len-1: + i.scope.symbols[outVars[k]] = MinOperator(kind: minValOp, sealed: false, val: newNull(), quotation: false) + # Actually execute the body of the operator + try: + i.dequote bv + except MinReturnException: + discard + finally: + if i.stack != snapshot: + raiseInvalid("Operator '$#' is polluting the stack" % n) + # Validate output + for k in 0..outVars.len-1: + i.apply i.scope.symbols[outVars[k]] + let x = i.pop + let r = validate(x, outExpects[k]) + if not r: + raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], outExpects[k], $x]) + i.push x + # Define symbol/sigil + if t == "symbol": + if i.scope.symbols.hasKey(n) and i.scope.symbols[n].sealed: + raiseUndefined("Attempting to redefine sealed symbol '$1'" % [n]) + i.scope.symbols[n] = MinOperator(kind: minProcOp, prc: p, sealed: false) + else: + if i.scope.sigils.hasKey(n) and i.scope.sigils[n].sealed: + raiseUndefined("Attempting to redefine sealed sigil '$1'" % [n]) + i.scope.sigils[n] = MinOperator(kind: minProcOp, prc: p, sealed: true) def.symbol("expect-empty-stack") do (i: In): let l = i.stack.len

@@ -200,7 +304,7 @@ i.push readLineFromStdin("").newVal

else: var ed = initEditor() i.push ed.readLine().newVal - + def.symbol("apply") do (i: In): let vals = i.expect("quot|dict") var prog = vals[0]
M min.nimmin.nim

@@ -291,8 +291,6 @@ var COMPILE = false

var MODULEPATH = "" var libfile = "" var exeName = "min" - var installOpt = "\n -—install:<lib> Install dynamic library file <lib>\n" - var uninstallOpt = "\n —-uninstall:<lib> Uninstall dynamic library file <lib>\n" var iOpt = "\n -i, --interactive Start $1 shell (with advanced prompt)\n" when defined(lite): exeName = "litemin"

@@ -384,7 +382,7 @@ $exe [options] [filename]

Arguments: filename A $exe file to interpret or compile (default: STDIN). - Options:$installOpt$uninstallOpt + Options: -a, --asset-path Specify a directory containing the asset files to include in the compiled executable (if -c is set) -c, --compile Compile the specified file

@@ -400,8 +398,6 @@ -p, --prelude:<file.min> If specified, it loads <file.min> instead of the default prelude code

-v, —-version Print the program version""" % [ "exe", exeName, "version", pkgVersion, - "installOpt", installOpt, - "uninstallOpt", uninstallOpt, "iOpt", iOpt ]
M tests/lang.mintests/lang.min

@@ -233,6 +233,19 @@

( {{100 :b} :a} :test *test/a/b 100 == ) assert + + ( + ( + symbol pow-mul + (num :base int :exp ==> num :pr num :mr) + ( + exp 1 - :n + base exp * @mr + base (dup) n times (*) n times @pr + ) + ) operator + 2 4 pow-mul - 8 == + ) assert report ; Tidy up