220 lines
5.5 KiB
Go
220 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type proc struct {
|
|
params expression
|
|
body expression
|
|
en *env
|
|
predicates map[symbol]symbol
|
|
}
|
|
|
|
type macro struct {
|
|
params expression
|
|
body expression
|
|
en *env
|
|
}
|
|
|
|
func eval(exp expression, en *env) (value expression) {
|
|
switch e := exp.(type) {
|
|
case symbol:
|
|
environ := en
|
|
if strings.Contains(string(e), "::") {
|
|
parts := strings.SplitN(string(e), "::", 2)
|
|
if len(parts) == 2 {
|
|
ans, ok1 := altnamespaces[parts[0]]
|
|
ns, ok2 := namespaces[parts[0]]
|
|
if ok1 {
|
|
ns, ok2 = namespaces[ans]
|
|
}
|
|
if ok2 {
|
|
environ = &ns
|
|
e = symbol(parts[1])
|
|
}
|
|
}
|
|
}
|
|
ex, err := environ.Find(e)
|
|
if err != nil {
|
|
value = exception(err.Error())
|
|
} else {
|
|
value = ex.vars[e]
|
|
}
|
|
case string:
|
|
value = e
|
|
case bool, number:
|
|
value = e
|
|
case exception:
|
|
if panicOnException {
|
|
panic(string(e))
|
|
}
|
|
value = e
|
|
case []expression:
|
|
switch car, _ := e[0].(symbol); car {
|
|
case "quote":
|
|
value = specialQuote(e)
|
|
case "if":
|
|
value = specialIf(e, en)
|
|
case "and":
|
|
value = specialAnd(e, en)
|
|
case "or":
|
|
value = specialOr(e, en)
|
|
case "case":
|
|
value = specialCase(e, en)
|
|
case "cond":
|
|
value = specialCond(e, en)
|
|
case "for":
|
|
value = specialFor(e, en)
|
|
case "set!":
|
|
value = specialSet(e, en)
|
|
case "define":
|
|
value = specialDefine(e, en)
|
|
case "lambda", "λ":
|
|
value = specialLambda(e, en)
|
|
case "macro":
|
|
value = specialMacro(e, en)
|
|
case "begin":
|
|
value = specialBegin(e, en)
|
|
case "begin0":
|
|
value = specialBegin0(e, en)
|
|
case "usage":
|
|
value = specialUsage(e)
|
|
case "load":
|
|
value = specialLoad(e, en)
|
|
case "load-mod":
|
|
value = specialLoadMod(e, en)
|
|
case "load-mod-file":
|
|
value = specialLoadModFile(e, en)
|
|
case "exists?":
|
|
value = specialExists(e, en)
|
|
case "inspect":
|
|
value = specialInspect(e, en)
|
|
case "apply":
|
|
value = specialApply(e, en)
|
|
case "eval":
|
|
value = specialEval(e, en)
|
|
default:
|
|
operands := e[1:]
|
|
values := make([]expression, len(operands))
|
|
first := eval(e[0], en)
|
|
_, isMacro := first.(macro)
|
|
for i, x := range operands {
|
|
if isMacro {
|
|
values[i] = x
|
|
} else {
|
|
values[i] = eval(x, en)
|
|
}
|
|
}
|
|
value = apply(first, values, e[0])
|
|
}
|
|
default:
|
|
panic("Unknown expression type encountered during EVAL")
|
|
}
|
|
|
|
if e, ok := value.(exception); panicOnException && ok {
|
|
panic(string(e))
|
|
}
|
|
return
|
|
}
|
|
|
|
func apply(procedure expression, args []expression, name expression) (value expression) {
|
|
switch p := procedure.(type) {
|
|
case func(...expression) expression:
|
|
value = p(args...)
|
|
case macro:
|
|
en := &env{make(vars), p.en}
|
|
switch params := p.params.(type) {
|
|
case []expression:
|
|
if variadic(params) > len(args) {
|
|
return exception(fmt.Sprintf("Macro %q expected %d argument(s) but received %d", String(name, false), len(params), len(args)))
|
|
}
|
|
for i, param := range params {
|
|
if param.(symbol) == symbol("args-list") || param.(symbol) == symbol("...") {
|
|
if len(args) >= len(params) {
|
|
en.vars[param.(symbol)] = args[i:]
|
|
} else {
|
|
en.vars[param.(symbol)] = make([]expression, 0)
|
|
}
|
|
break
|
|
}
|
|
en.vars[param.(symbol)] = args[i]
|
|
}
|
|
default:
|
|
en.vars[params.(symbol)] = args
|
|
}
|
|
value = eval(p.body, en)
|
|
case proc: // Mostly used by lambda
|
|
// FIXME the parsing or the application of lists is donked in certain contexts...
|
|
// not sure what is happening: (apply display-lines [1 2 [3 4]]) does not work
|
|
en := &env{make(vars), p.en}
|
|
switch params := p.params.(type) {
|
|
case []expression:
|
|
if variadic(params) > len(args) {
|
|
return exception(fmt.Sprintf("Lambda %q expected %d argument(s) but received %d", String(name, false), len(params), len(args)))
|
|
}
|
|
for i, param := range params {
|
|
variadic := false
|
|
if param.(symbol) == symbol("args-list") || param.(symbol) == symbol("...") {
|
|
variadic = true
|
|
}
|
|
|
|
result := true
|
|
if v, ok := p.predicates[param.(symbol)]; ok {
|
|
if variadic {
|
|
r := apply(eval(v, p.en), []expression{args[i:]}, v)
|
|
result = AnythingToBool(r).(bool)
|
|
} else {
|
|
r := apply(eval(v, p.en), []expression{args[i]}, v)
|
|
result = AnythingToBool(r).(bool)
|
|
}
|
|
if !result {
|
|
return exception(fmt.Sprintf("Type Error: In call to %q argument %d failed to fulfill predicate %q", String(name, false), i+1, v))
|
|
}
|
|
}
|
|
|
|
if variadic {
|
|
if len(args) >= len(params) {
|
|
en.vars[param.(symbol)] = args[i:]
|
|
} else {
|
|
en.vars[param.(symbol)] = make([]expression, 0)
|
|
}
|
|
break
|
|
}
|
|
|
|
en.vars[param.(symbol)] = args[i]
|
|
}
|
|
default:
|
|
// This wont come up with changes to how lambdas without a () structure work
|
|
// hint: they are given a ()
|
|
en.vars[params.(symbol)] = args
|
|
}
|
|
value = eval(p.body, en)
|
|
default:
|
|
panic(fmt.Sprintf("Unexpected procedure symbol encountered during APPLY. Expected \"procedure\", got %q: %s", globalenv.vars["type"].(func(...expression) expression)(procedure), String(procedure, true)))
|
|
}
|
|
return
|
|
}
|
|
|
|
// Because lambda and quote expressions do not have their
|
|
// content evaluated at creation strings were not getting
|
|
// unescaped. As such, they were not usable in the same way
|
|
// as strings used elsewhere. This recursively unescapes all
|
|
// of the strings as need be so that string behavior is
|
|
// consistent accross the system.
|
|
func stringUnescapeEval(exp expression) expression {
|
|
switch e := exp.(type) {
|
|
case string:
|
|
return e
|
|
case []expression:
|
|
values := make([]expression, len(e))
|
|
for i, x := range e {
|
|
values[i] = stringUnescapeEval(x)
|
|
}
|
|
return values
|
|
default:
|
|
return e
|
|
}
|
|
}
|