Testd and documented type expressions.
h3rald h3rald@h3rald.com
Thu, 04 Feb 2021 21:35:53 +0000
6 files changed,
137 insertions(+),
46 deletions(-)
M
minpkg/core/utils.nim
→
minpkg/core/utils.nim
@@ -246,6 +246,7 @@ if not ts.contains(tt) and not tt.startsWith("dict:") and not i.scope.hasSymbol("typeclass:$#" % tt):
return false return true +# The following is used in operator signatures proc expect*(i: var MinInterpreter, elements: varargs[string], generics: var CritBitTree[string]): seq[MinValue] = let sym = i.currSym.getString var valid = newSeq[string](0)@@ -288,7 +289,7 @@ if not andr:
if neg: vTypes[c] = t else: - vTypes[c] = "!"&t + vTypes[c] = value.typeName break if andr: res = true@@ -300,49 +301,12 @@ valid.add(generics[el])
else: raiseInvalid(message(vTypes[c], elements, generics)) c = c+1 - -# TODO: review -proc expect*(i: var MinInterpreter, elements: varargs[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 = "Incorrect values found on the stack:\n" - 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 - let ands = element.split("&") - for a in ands: - let ors = a.split("|") - var res = false - for to in ors: - var t = to - var neg = false - if t.len > 1 and t[0] == '!': - t = t[1..element.len-1] - neg = true - if i.validate(value, t) or neg: - res = true - break - if not res: - raiseInvalid(message(value.typeName)) - else: - var el = element - var neg = false - if element.len > 1 and element[0] == '!': - el = element[1..element.len-1] - neg = true - if i.validate(value, element) or neg: - valid.add element - else: - raiseInvalid(message(value.typeName)) +# The following is used in expect symbol and native symbol expectations. +proc expect*(i: var MinInterpreter, elements: varargs[string]): seq[MinValue] = + var c: CritBitTree[string] + return i.expect(elements, c) + proc reqQuotationOfQuotations*(i: var MinInterpreter, a: var MinValue) = a = i.pop if not a.isQuotation:
M
minpkg/lib/min_lang.nim
→
minpkg/lib/min_lang.nim
@@ -971,7 +971,7 @@ opts = i.dset(opts, key.newVal, val.newVal)
else: discard i.push opts - + def.symbol("expect") do (i: In): var q: MinValue i.reqQuotationOfSymbols q
M
next-release.md
→
next-release.md
@@ -28,3 +28,50 @@ Note that:
* Lambdas must be captured using the `^` sigil in signatures and bound using `lambda-bind` in the operator body. * Lambdas cannot be captured in input values (they have already been pushed on the stack). * Requiring a lambda as an output value effectively bypasses stack pollution checks. While this can be useful at times, use with caution! + +#### Type Expressions + +When specifying types in operator signatures or through the {#link-operator||lang||expect#} operator, you can specify a logical expression containing types and type classes joined with one kf the following operators: + +* `|` (or) +* `&` (and) +* `!` (not) + +Suppose for example you defined the following type classes: + +``` +(typeclass fiveplus + (int :n ==> bool :o) + ( + n 5 > @o + ) +) :: + +(typeclass tenminus + (int :n ==> bool :o) + ( + n 10 < @o + ) +) :: + +(typeclass even + (int :n ==> bool :o) + ( + n 2 mod 0 == @o + ) +) :: +``` + +You can combine them in a type expression as following: + +``` +(symbol test + (!even|tenminus&fiveplus :n ==> bool :o) + ( + true @o + ) +) :: +4 test ; error +6 test ; true +11 test ; true +```
M
site/contents/learn-operators.md
→
site/contents/learn-operators.md
@@ -142,7 +142,7 @@
* One of the following shorthand symbols identifying a well-known {{m}} base type (see the {#link-page||reference||reference#} section for more information): `a`, `bool`, `null`, `str`, `int`, `num`, `flt`, `'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`. +* a type expression like `str|int`. > %note% > Note@@ -196,6 +196,53 @@ Note that:
* Lambdas must be captured using the `^` sigil in signatures and bound using {#link-operator||lang||lambda-bind#} in the operator body. * Lambdas cannot be captured in input values (they have already been pushed on the stack). * Requiring a lambda as an output value effectively bypasses stack pollution checks. While this can be useful at times, use with caution! + +### Type Expressions + +When specifying types in operator signatures or through the {#link-operator||lang||expect#} operator, you can specify a logical expression containing types and type classes joined with one kf the following operators: + +* `|` (or) +* `&` (and) +* `!` (not) + +Suppose for example you defined the following type classes: + +``` +(typeclass fiveplus + (int :n ==> bool :o) + ( + n 5 > @o + ) +) :: + +(typeclass tenminus + (int :n ==> bool :o) + ( + n 10 < @o + ) +) :: + +(typeclass even + (int :n ==> bool :o) + ( + n 2 mod 0 == @o + ) +) :: +``` + +You can combine them in a type expression as following: + +``` +(symbol test + (!even|tenminus&fiveplus :n ==> bool :o) + ( + true @o + ) +) :: +4 test ; error +6 test ; true +11 test ; true +``` ### Generics
M
site/contents/reference-lang.md
→
site/contents/reference-lang.md
@@ -121,7 +121,7 @@ > > %tip%
> > Tips > > > > * You can specify a typed dictionary by prepending the type name with `dict:`. Example: `dict:socket` -> > * You can specify two or more matching types by separating the type names with a pipe: `string|quot` +> > * You can specify two or more matching types by separating combined together in a logical type expression, e.g.: `string|quot` > > %sidebar% > > Example
M
tests/lang.min
→
tests/lang.min
@@ -310,6 +310,39 @@ )
) :: ("a" "b" add "ab" ==) *test/assert ((1 2 3) (4 5) add (1 2 3 4 5) ==) *test/assert + + ( + (typeclass fiveplus + (int :n ==> bool :o) + ( + n 5 > @o + ) + ) :: + + (typeclass tenminus + (int :n ==> bool :o) + ( + n 10 < @o + ) + ) :: + + (typeclass even + (int :n ==> bool :o) + ( + n 2 mod 0 == @o + ) + ) :: + + (symbol test + (!even|tenminus&fiveplus :n ==> bool :o) + ( + true @o + ) + ) :: + 6 test + 11 test + and + ) *test/assert *test/report ; Tidy up