2021-07-26 05:31:22 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
type proc struct {
|
2021-07-31 20:25:20 +00:00
|
|
|
params expression
|
|
|
|
body expression
|
|
|
|
en *env
|
2021-07-26 05:31:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func eval(exp expression, en *env) (value expression) {
|
|
|
|
switch e := exp.(type) {
|
|
|
|
case symbol:
|
|
|
|
value = en.Find(e).vars[e]
|
2021-07-29 03:47:05 +00:00
|
|
|
case string:
|
|
|
|
value = unescapeString(e)
|
|
|
|
case bool, number:
|
2021-07-26 05:31:22 +00:00
|
|
|
value = e
|
|
|
|
case []expression:
|
|
|
|
switch car, _ := e[0].(symbol); car {
|
|
|
|
case "quote":
|
2021-07-27 03:19:59 +00:00
|
|
|
if len(e) < 2 {
|
|
|
|
panic("Invalid 'quote' syntax - too few arguments")
|
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
value = e[1]
|
|
|
|
case "if":
|
2021-07-30 23:03:25 +00:00
|
|
|
if len(e) < 3 {
|
2021-07-27 03:19:59 +00:00
|
|
|
panic("Invalid 'if' syntax - too few arguments")
|
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
if AnythingToBool(eval(e[1], en)).(bool) {
|
|
|
|
value = eval(e[2], en)
|
2021-07-30 23:03:25 +00:00
|
|
|
} else if len(e) > 3 {
|
2021-07-26 05:31:22 +00:00
|
|
|
value = eval(e[3], en)
|
2021-07-30 23:03:25 +00:00
|
|
|
} else {
|
|
|
|
value = make([]expression, 0)
|
2021-07-26 05:31:22 +00:00
|
|
|
}
|
2021-08-01 22:13:25 +00:00
|
|
|
case "and":
|
|
|
|
if len(e) < 2 {
|
|
|
|
panic("Invalid 'and' syntax - too few arguments")
|
|
|
|
}
|
|
|
|
OuterAnd:
|
|
|
|
for i := range e[1:] {
|
|
|
|
switch item := e[i+1].(type) {
|
|
|
|
case []expression:
|
|
|
|
value = eval(item, en)
|
|
|
|
if !AnythingToBool(value).(bool) {
|
|
|
|
break OuterAnd
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
value = item
|
|
|
|
if !AnythingToBool(value).(bool) {
|
|
|
|
break OuterAnd
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case "or":
|
|
|
|
if len(e) < 2 {
|
|
|
|
panic("Invalid 'or' syntax - too few arguments")
|
|
|
|
}
|
|
|
|
OuterOr:
|
|
|
|
for i := range e[1:] {
|
|
|
|
switch item := e[i+1].(type) {
|
|
|
|
case []expression:
|
|
|
|
value = eval(item, en)
|
|
|
|
if AnythingToBool(value).(bool) {
|
|
|
|
break OuterOr
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
value = item
|
|
|
|
if AnythingToBool(value).(bool) {
|
|
|
|
break OuterOr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-27 03:19:59 +00:00
|
|
|
case "cond":
|
|
|
|
if len(e) < 2 {
|
|
|
|
panic("Invalid 'cond' syntax - too few arguments")
|
|
|
|
}
|
2021-08-01 06:31:38 +00:00
|
|
|
// CondLoop:
|
2021-07-27 03:19:59 +00:00
|
|
|
for _, exp := range e[1:] {
|
|
|
|
switch i := exp.(type) {
|
|
|
|
case []expression:
|
|
|
|
if len(i) < 2 {
|
|
|
|
panic("Invalid 'cond' case, cases must take the form: `(<test> <expression>)")
|
|
|
|
}
|
2021-08-02 06:06:52 +00:00
|
|
|
if i[0] == "else" || i[0] == symbol("else") {
|
2021-07-27 03:19:59 +00:00
|
|
|
return eval(i[1], en)
|
2021-08-02 06:06:52 +00:00
|
|
|
}
|
|
|
|
if AnythingToBool(eval(i[0], en)).(bool) {
|
2021-07-27 03:19:59 +00:00
|
|
|
return eval(i[1], en)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic("Invalid 'cond' case, cases must take the form: `(<test> <expression>)")
|
|
|
|
}
|
|
|
|
value = make([]expression, 0)
|
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
case "set!":
|
2021-07-27 03:19:59 +00:00
|
|
|
if len(e) < 3 {
|
|
|
|
panic("Invalid 'set!' syntax - too few arguments")
|
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
v := e[1].(symbol)
|
|
|
|
en.Find(v).vars[v] = eval(e[2], en)
|
|
|
|
value = symbol("ok")
|
|
|
|
case "define":
|
2021-07-27 03:19:59 +00:00
|
|
|
if len(e) < 3 {
|
|
|
|
panic("Invalid 'define' syntax - too few arguments")
|
|
|
|
}
|
|
|
|
if _, ok := e[1].(symbol); !ok {
|
2021-08-02 21:37:57 +00:00
|
|
|
panic("'define' expects a symbol as its first argument")
|
2021-07-27 03:19:59 +00:00
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
en.vars[e[1].(symbol)] = eval(e[2], en)
|
|
|
|
value = symbol("ok")
|
2021-08-02 21:37:57 +00:00
|
|
|
case "lambda", "λ":
|
|
|
|
if len(e) < 3 {
|
|
|
|
panic("'lambda' expects at least three arguments")
|
|
|
|
}
|
2021-08-01 21:17:02 +00:00
|
|
|
b := []expression{symbol("begin")}
|
|
|
|
b = append(b, e[2:]...)
|
|
|
|
value = proc{e[1], b, en}
|
2021-07-26 05:31:22 +00:00
|
|
|
case "begin":
|
|
|
|
for _, i := range e[1:] {
|
|
|
|
value = eval(i, en)
|
|
|
|
}
|
2021-07-31 06:31:08 +00:00
|
|
|
case "begin0":
|
|
|
|
for ii, i := range e[1:] {
|
|
|
|
if ii == 0 {
|
|
|
|
value = eval(i, en)
|
|
|
|
} else {
|
|
|
|
eval(i, en)
|
|
|
|
}
|
|
|
|
}
|
2021-08-02 21:37:57 +00:00
|
|
|
case "load":
|
|
|
|
if en.outer != nil {
|
|
|
|
panic("'load' is only callable from the global/top-level")
|
|
|
|
}
|
|
|
|
for _, fp := range e[1:] {
|
|
|
|
if _, ok := fp.(string); !ok {
|
|
|
|
panic("'load' expects filepaths as a string, a non-string value was encountered")
|
|
|
|
}
|
|
|
|
loadFiles(e[1:])
|
|
|
|
}
|
|
|
|
value = symbol("ok")
|
2021-08-03 21:08:39 +00:00
|
|
|
case "apply":
|
|
|
|
if len(e) < 3 {
|
|
|
|
panic("'apply' expects two arguments: a procedure and an argument list, too few arguments were given")
|
|
|
|
}
|
|
|
|
args := eval(e[2], en)
|
|
|
|
switch item := args.(type) {
|
|
|
|
case []expression:
|
|
|
|
return apply(eval(e[1], en), item)
|
|
|
|
default:
|
|
|
|
return apply(eval(e[1], en), []expression{item})
|
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
default:
|
|
|
|
operands := e[1:]
|
|
|
|
values := make([]expression, len(operands))
|
|
|
|
for i, x := range operands {
|
|
|
|
values[i] = eval(x, en)
|
|
|
|
}
|
|
|
|
value = apply(eval(e[0], en), values)
|
2021-07-31 20:25:20 +00:00
|
|
|
}
|
|
|
|
default:
|
2021-07-26 05:31:22 +00:00
|
|
|
panic("Unknown expression type encountered during EVAL")
|
2021-07-31 20:25:20 +00:00
|
|
|
}
|
|
|
|
return
|
2021-07-26 05:31:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func apply(procedure expression, args []expression) (value expression) {
|
|
|
|
switch p := procedure.(type) {
|
|
|
|
case func(...expression) expression:
|
|
|
|
value = p(args...)
|
|
|
|
case proc: // Mostly used by lambda
|
|
|
|
en := &env{make(vars), p.en}
|
|
|
|
switch params := p.params.(type) {
|
|
|
|
case []expression:
|
2021-08-01 03:04:26 +00:00
|
|
|
if len(params) > len(args) {
|
2021-07-26 05:31:22 +00:00
|
|
|
panic(fmt.Sprintf("Lambda expected %d arguments but received %d", len(params), len(args)))
|
|
|
|
}
|
|
|
|
for i, param := range params {
|
2021-08-01 03:04:26 +00:00
|
|
|
if param.(symbol) == symbol("args-list") {
|
|
|
|
en.vars[param.(symbol)] = args[i:]
|
|
|
|
break
|
|
|
|
}
|
2021-07-26 05:31:22 +00:00
|
|
|
en.vars[param.(symbol)] = args[i]
|
|
|
|
}
|
|
|
|
default:
|
2021-08-01 06:31:38 +00:00
|
|
|
en.vars[params.(symbol)] = args
|
2021-07-26 05:31:22 +00:00
|
|
|
}
|
|
|
|
value = eval(p.body, en)
|
|
|
|
default:
|
|
|
|
panic("Unknown procedure type encountered during APPLY")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|