all repos — min @ bd0baeacd28a9f117b0113fef215831cdd8f403e

A small but practical concatenative programming language.

Implemented type classes
h3rald h3rald@h3rald.com
Wed, 13 Jan 2021 20:32:22 +0100
commit

bd0baeacd28a9f117b0113fef215831cdd8f403e

parent

ec35b136d7ca66f2014c304ca3fa9619b37916ce

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

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

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

@@ -201,16 +201,31 @@ return value.isString

of "a": return true else: - var split = t.split(":") - # Typed dictionaries - if split[0] == "dict": - if value.isTypedDictionary(split[1]): - return true - return false + if t.contains(":"): + var split = t.split(":") + # Typed dictionaries + if split[0] == "dict": + if value.isTypedDictionary(split[1]): + return true + return false + elif i.scope.hasSymbol("type:$#" % t): + # Custom type class + var i2 = i.copy(i.filename) + i2.withScope(): + i2.push value + i2.pushSym("type:$#" % t) + let res = i2.pop + 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) -proc validType*(s: string): bool = +proc validType*(i: In, s: string): bool = const ts = ["bool", "null", "int", "num", "float", "quot", "dict", "'sym", "sym", "string", "a"] if ts.contains(s): + return true + if i.scope.hasSymbol("type:$#" % s): return true for tt in s.split("|"): if not ts.contains(tt) or tt.startsWith("dict:"):

@@ -236,12 +251,12 @@ var split = element.split("|")

if split.len > 1: var res = false for t in split: - if validate(value, t): + if i.validate(value, t): res = true break if not res: raiseInvalid(message(value.typeName)) - elif not validate(value, element): + elif not i.validate(value, element): raiseInvalid(message(value.typeName)) valid.add element
M minpkg/lib/min_lang.nimminpkg/lib/min_lang.nim

@@ -202,7 +202,7 @@ check = c mod 2 != 0

if check: if v == "==>": o = true - elif not validType(v): + elif not i.validType(v): raiseInvalid("Invalid type specified in signature at position $#" % $(c+1)) else: if o:

@@ -258,11 +258,11 @@ var r = false;

if o.contains("|"): let types = o.split("|") for ut in types: - if validate(x, ut): + if i.validate(x, ut): r = true break else: - r = validate(x, o) + r = i.validate(x, o) if not r: discard i.pop raiseInvalid("Invalid value for output symbol '$#'. Expected $#, found $#" % [outVars[k], o, $x])

@@ -493,6 +493,16 @@

def.symbol("type") do (i: In): let vals = i.expect("a") i.push vals[0].typeName.newVal + + def.symbol("typeclass") do (i: In): + let vals = i.expect("'sym", "quot") + let name = vals[0].getString + let symbol = "type:$#" % name + let code = vals[1] + info "[typeclass] $1 = $2" % [symbol, $code] + if i.scope.symbols.hasKey(symbol) and i.scope.symbols[symbol].sealed: + raiseUndefined("Attempting to redefine sealed typeclass '$1'" % [name]) + i.scope.symbols[symbol] = MinOperator(kind: minValOp, val: code, sealed: false, quotation: true) def.symbol("import") do (i: In): var vals = i.expect("'sym")
M minpkg/lib/min_logic.nimminpkg/lib/min_logic.nim

@@ -212,6 +212,12 @@ if i.pop.kind == minQuotation:

i.push true.newVal else: i.push false.newVal + + def.symbol("stringlike?") do (i: In): + if i.pop.isStringLike: + i.push true.newVal + else: + i.push false.newVal def.symbol("dictionary?") do (i: In): if i.pop.isDictionary:
M next-release.mdnext-release.md

@@ -3,3 +3,5 @@ * Added **sealed?** and **sealed-sigil?** symbols.

+ **floor** and **ceil** now correctly return an integer again. * Added **abs** symbol. * Now executing min shell if no file is specified (unless input is piped in), without the need of specifying **-i**. +* Implemented the possibility to define type classes. +* Added **stringlike?** symbol.
M tests/lang.mintests/lang.min

@@ -264,6 +264,22 @@ ) ::

nt"float" ) assert + (:n ((n integer?) (n 0 >)) &&) 'natural typeclass + ("type:natural" defined?) assert + ( + symbol natural-sum + (natural :n natural :m ==> natural :result) + (n m + @result) + ) :: + null :err + ( + (3 -3 natural-sum) + (@err) + ) 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 + report ; Tidy up clear-stack
M tests/logic.mintests/logic.min

@@ -132,6 +132,10 @@ (true quotation? false ==) assert

(false quotation? false ==) assert (("a" 2 c) quotation?) assert + ("a" stringlike?) assert + (1 stringlike? false ==) assert + ('test stringlike?) assert + ({} 'module type? false ==) assert ((1 2 3) 'module type? false ==) assert (4 'module type? false ==) assert