all repos — min @ 50b274da02e5e784c93463fec91296fac01e2195

A small but practical concatenative programming language.

Started implementing generics (tests failing).
h3rald h3rald@h3rald.com
Sat, 16 Jan 2021 21:34:29 +0000
commit

50b274da02e5e784c93463fec91296fac01e2195

parent

a5f57df7dffd4510d57470630bc97f9dcb2f1e1a

4 files changed, 87 insertions(+), 11 deletions(-)

jump to
M minpkg/core/utils.nimminpkg/core/utils.nim

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

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

@@ -219,8 +219,21 @@ if not res.isBool:

raiseInvalid("Type class '$#' does not evaluate to a boolean value ($# was returned instead)" % [t, $res]) return res.boolVal else: - raiseInvalid("Unknown type class '$#'" % t) + raiseInvalid("Unknown type '$#'" % t) +proc validate*(i: In, value: MinValue, t: string, generics: var CritBitTree[string]): bool = + if generics.hasKey(t): + let ts = generics[t].split("|") + for tp in ts: + if i.validate(value, tp, generics): + generics[t] = tp # lock type for future uses within same signature + return true + return false + return i.basicValidate(value, t) + +proc validate*(i: In, value: MinValue, t: string): bool = + return i.basicValidate(value, t) + proc validType*(i: In, s: string): bool = const ts = ["bool", "null", "int", "num", "float", "quot", "dict", "'sym", "sym", "string", "a"] if ts.contains(s):

@@ -232,6 +245,34 @@ if not ts.contains(tt) or tt.startsWith("dict:"):

return false return true +proc expect*(i: var MinInterpreter, elements: varargs[string], generics: var CritBitTree[string]): seq[MinValue] = + let stack = elements.reverse.join(" ") + let sym = i.currSym.getString + var valid = newSeq[string](0) + result = newSeq[MinValue](0) + let message = proc(invalid: string): string = + result = "Symbol: $1 - Incorrect values found on the stack:\n" % sym + result &= "- expected: " & stack & " $1\n" % sym + var other = "" + if valid.len > 0: + other = valid.reverse.join(" ") & " " + result &= "- got: " & invalid & " " & other & sym + for element in elements: + let value = i.pop + result.add value + var split = element.split("|") + if split.len > 1: + var res = false + for t in split: + if i.validate(value, t, generics): + res = true + break + if not res: + raiseInvalid(message(value.typeName)) + elif not i.validate(value, element, generics): + raiseInvalid(message(value.typeName)) + valid.add element + proc expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] = let stack = elements.reverse.join(" ") let sym = i.currSym.getString
M minpkg/lib/min_lang.nimminpkg/lib/min_lang.nim

@@ -191,18 +191,32 @@ var inExpects= newSeq[string](0)

var inVars = newSeq[string](0) var outExpects= newSeq[string](0) var outVars = newSeq[string](0) + var generics: CritBitTree[string] var o= false for vv in sv.qVal: - if not vv.isSymbol: - raiseInvalid("Signature must be a quotation id symbols") - let v = vv.symVal + if not vv.isSymbol and not vv.isQuotation: + raiseInvalid("Signature must be a quotation of symbols/quotations") + var v: string var check = c mod 2 == 0 if o: check = c mod 2 != 0 + if vv.isQuotation: + if vv.qVal.len != 2 or not vv.qVal[0].isSymbol or not vv.qVal[1].isSymbol: + raiseInvalid("Generic quotation must contain exactly two symbols") + let t = vv.qVal[0].getString + let g = vv.qVal[1].getString + if not i.validType(t): + raiseInvalid("Invalid type '$#' in generic in signature at position $#" % [$t, $(c+1)]) + if g[0] != ':': + raiseInvalid("No mapping symbol specified in generic in signature at position $#" % $(c+1)) + v = g[1..g.len-1] + generics[v] = t + else: + v = vv.symVal if check: if v == "==>": o = true - elif not i.validType(v): + elif not i.validType(v) and not generics.hasKey(v): raiseInvalid("Invalid type specified in signature at position $#" % $(c+1)) else: if o:

@@ -227,7 +241,7 @@ raiseInvalid("Body must be a quotation")

inExpects.reverse inVars.reverse var p: MinOperatorProc = proc (i: In) = - var inVals = i.expect(inExpects) + var inVals = i.expect(inExpects, generics) i.withScope(): # Inject variables for mapped inputs for k in 0..inVars.len-1:

@@ -258,14 +272,17 @@ var r = false;

if o.contains("|"): let types = o.split("|") for ut in types: - if i.validate(x, ut): + if i.validate(x, ut, generics): r = true break else: - r = i.validate(x, o) + r = i.validate(x, o, generics) if not r: discard i.pop - raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], o, $x]) + var tp = t + if generics.hasKey(o): + tp = generics[o] + raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], tp, $x]) # Define symbol/sigil if t == "symbol": if i.scope.symbols.hasKey(n) and i.scope.symbols[n].sealed:
M site/contents/learn-operators.mdsite/contents/learn-operators.md

@@ -114,7 +114,7 @@ In this case for example tbe `square` symbol expects a number on the stack, which will be captured to tbe symbol `n` and it will place a number on the stack which needs to be bound in the operator body to the symbol `result`.

In a signature, a type expression must precede the capturing symbol. Such type expression can be: -* One of the following shorthand symbols identifying a well-known {{m}} base type (see the {#link-page||chapter#} section for more information): `a`, `null`, `str`, `int`, `num`, `float`, `'sym`, `quot`, or `dict`. +* One of the following shorthand symbols identifying a well-known {{m}} base type (see the {#link-page||chapter#} section for more information): `a`, `bool`, `null`, `str`, `int`, `num`, `float`, `'sym`, `quot`, or `dict`. * A typed dictionary like `dict:module` or `dict:datastore`. * A type class (see below). * a union of types/typed dictionaries/type classes, like `str|int`.
M tests/lang.mintests/lang.min

@@ -279,6 +279,24 @@ ) try

pop ;Remove 3 that was left on the stack. (err format-error "expected: natural natural natural-sum" match) assert (2 3 natural-sum 5 ==) assert + + ( + symbol add + ((string|num|quot :t) :a t :b ==> t :result) + ( + (a type "string" ==) + (a b suffix @result return) + when + (a type "num" ==) + (a b + @result return) + when + (a type "quot" ==) + (a b concat #result return) + when + ) + ) :: + ("a" "b" add "ab" ==) assert + ((1 2 3) (4 5) add (1 2 3 4 5) ==) assert report ; Tidy up