From 62d5eed79db59f6685f775b12c6d480e20a43aac Mon Sep 17 00:00:00 2001 From: sloum Date: Thu, 4 May 2023 14:21:07 -0700 Subject: [PATCH] Adds simple typing implementation --- gui.go | 21 ++++++++++++++++++++- interpreter.go | 37 ++++++++++++++++++++++++++++++++++++- nogui.go | 21 ++++++++++++++++++++- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/gui.go b/gui.go index 1a48daa..182a272 100644 --- a/gui.go +++ b/gui.go @@ -1130,7 +1130,26 @@ func String(v expression, rawString bool) string { case proc: var b strings.Builder b.WriteString("(lambda ") - b.WriteString(String(v.params, true)) + params, ok := v.params.([]expression) + if len(v.predicates) > 0 { + if ok { + newParams := make([]expression, len(params)) + copy(newParams, params) + for i := range newParams { + if pred, ok := v.predicates[newParams[i].(symbol)]; ok { + newParams[i] = symbol(fmt.Sprintf("%s@%s", newParams[i].(symbol), pred)) + } + } + b.WriteString(String(newParams, true)) + } else { + val := String(v.params, false) + b.WriteString(val) + b.WriteString("@") + b.WriteString(string(v.predicates[symbol(val)])) + } + } else { + b.WriteString(String(v.params, true)) + } b.WriteRune(' ') body := String(v.body, true) if strings.HasPrefix(body, "(begin ") { diff --git a/interpreter.go b/interpreter.go index 3818e0b..000fdc2 100644 --- a/interpreter.go +++ b/interpreter.go @@ -17,6 +17,7 @@ type proc struct { params expression body expression en *env + predicates map[symbol]symbol } type macro struct { @@ -280,7 +281,31 @@ func eval(exp expression, en *env) (value expression) { } b := []expression{symbol("begin")} b = append(b, e[2:]...) - value = proc{e[1], stringUnescapeEval(b), en} + predicates := make(map[symbol]symbol) + switch a := e[1].(type) { + case []expression: + for i, v := range a { + s := String(v, false) + ind := strings.LastIndex(s, "@") + if ind < 1 { + continue + } + a[i] = symbol(s[:ind]) + predicates[a[i].(symbol)] = symbol(s[ind+1:]) + } + e[1] = a + default: + s := String(e[1], false) + ind := strings.LastIndex(s, "@") + if ind < 1 { + e[1] = []expression{e[1]} + break + } + e[1] = symbol(s[:ind]) + predicates[e[1].(symbol)] = symbol(s[ind+1:]) + e[1] = []expression{e[1]} + } + value = proc{e[1], stringUnescapeEval(b), en, predicates} case "macro": if len(e) < 3 { value = exception("'macro' expects at least three arguments") @@ -716,9 +741,19 @@ func apply(procedure expression, args []expression, name expression) (value expr } break } + if v, ok := p.predicates[param.(symbol)]; ok { + if !AnythingToBool(eval([]expression{v, args[i]}, p.en)).(bool) { + return exception(fmt.Sprintf("Type Error: Lambda %q expected argument %d to pass predicate %q, it did not", String(name, false), i+1, v)) + } + } en.vars[param.(symbol)] = args[i] } default: + if v, ok := p.predicates[params.(symbol)]; ok { + if !AnythingToBool(eval([]expression{v, args[0]}, p.en)).(bool) { + return exception(fmt.Sprintf("Type Error: Lambda %q expected argument %d to pass predicate %q, it did not", String(name, false), 1, v)) + } + } en.vars[params.(symbol)] = args } value = eval(p.body, en) diff --git a/nogui.go b/nogui.go index b3a0287..ee6e688 100644 --- a/nogui.go +++ b/nogui.go @@ -35,7 +35,26 @@ func String(v expression, rawString bool) string { case proc: var b strings.Builder b.WriteString("(lambda ") - b.WriteString(String(v.params, true)) + params, ok := v.params.([]expression) + if len(v.predicates) > 0 { + if ok { + newParams := make([]expression, len(params)) + copy(newParams, params) + for i := range newParams { + if pred, ok := v.predicates[newParams[i].(symbol)]; ok { + newParams[i] = symbol(fmt.Sprintf("%s@%s", newParams[i].(symbol), pred)) + } + } + b.WriteString(String(newParams, true)) + } else { + val := String(v.params, false) + b.WriteString(val) + b.WriteString("@") + b.WriteString(string(v.predicates[symbol(val)])) + } + } else { + b.WriteString(String(v.params, true)) + } b.WriteRune(' ') body := String(v.body, true) if strings.HasPrefix(body, "(begin ") {