Starts splitting out special forms into functions
This commit is contained in:
parent
3b385c4314
commit
bc375214af
276
interpreter.go
276
interpreter.go
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -62,278 +61,31 @@ func eval(exp expression, en *env) (value expression) {
|
|||
case []expression:
|
||||
switch car, _ := e[0].(symbol); car {
|
||||
case "quote":
|
||||
if len(e) < 2 {
|
||||
value = exception("Invalid 'quote' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
value = stringUnescapeEval(e[1])
|
||||
value = specialQuote(e)
|
||||
case "if":
|
||||
if len(e) < 3 {
|
||||
value = exception("Invalid 'if' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
if AnythingToBool(eval(e[1], en)).(bool) {
|
||||
value = eval(e[2], en)
|
||||
} else if len(e) > 3 {
|
||||
value = eval(e[3], en)
|
||||
} else {
|
||||
value = make([]expression, 0)
|
||||
}
|
||||
value = specialIf(e, en)
|
||||
case "and":
|
||||
if len(e) < 2 {
|
||||
value = exception("Invalid 'and' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
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 = eval(item, en)
|
||||
if !AnythingToBool(value).(bool) {
|
||||
break OuterAnd
|
||||
}
|
||||
}
|
||||
}
|
||||
value = specialAnd(e, en)
|
||||
case "or":
|
||||
if len(e) < 2 {
|
||||
value = exception("Invalid 'or' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
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 = eval(item, en)
|
||||
if AnythingToBool(value).(bool) {
|
||||
break OuterOr
|
||||
}
|
||||
}
|
||||
}
|
||||
value = specialOr(e, en)
|
||||
case "case":
|
||||
if len(e) < 3 {
|
||||
value = exception("Invalid 'case' syntax - too few arguments")
|
||||
}
|
||||
target := eval(e[1], en)
|
||||
CaseLoop:
|
||||
for _, exp := range e[2:] {
|
||||
switch i := exp.(type) {
|
||||
case []expression:
|
||||
if len(i) < 2 {
|
||||
value = exception("Invalid 'case' case, cases must take the form: `(<test> <expression>)")
|
||||
break CaseLoop
|
||||
}
|
||||
if i[0] == "else" || i[0] == symbol("else") {
|
||||
value = eval(i[1], en)
|
||||
break CaseLoop
|
||||
}
|
||||
if reflect.DeepEqual(eval(i[0], en), target) {
|
||||
value = eval(i[1], en)
|
||||
break CaseLoop
|
||||
}
|
||||
default:
|
||||
value = exception("Invalid 'case' case, cases must take the form: `(<test> <expression>)")
|
||||
break CaseLoop
|
||||
}
|
||||
}
|
||||
if value == nil {
|
||||
value = make([]expression, 0)
|
||||
}
|
||||
|
||||
value = specialCase(e, en)
|
||||
case "cond":
|
||||
if len(e) < 2 {
|
||||
value = exception("Invalid 'cond' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
CondLoop:
|
||||
for _, exp := range e[1:] {
|
||||
switch i := exp.(type) {
|
||||
case []expression:
|
||||
if len(i) < 2 {
|
||||
value = exception("Invalid 'cond' case, cases must take the form: `(<test> <expression>)")
|
||||
break CondLoop
|
||||
}
|
||||
if i[0] == "else" || i[0] == symbol("else") {
|
||||
value = eval(i[1], en)
|
||||
break CondLoop
|
||||
}
|
||||
if AnythingToBool(eval(i[0], en)).(bool) {
|
||||
value = eval(i[1], en)
|
||||
break CondLoop
|
||||
}
|
||||
default:
|
||||
value = exception("Invalid 'cond' case, cases must take the form: `(<test> <expression>)")
|
||||
break CondLoop
|
||||
}
|
||||
}
|
||||
if value == nil {
|
||||
value = make([]expression, 0)
|
||||
}
|
||||
value = specialCond(e, en)
|
||||
case "for":
|
||||
if len(e) < 3 {
|
||||
value = exception("'for' requires at least two arguments: a list of initializers and a test")
|
||||
break
|
||||
}
|
||||
// TODO follow the lisp do style: https://www.tutorialspoint.com/lisp/lisp_do.htm
|
||||
|
||||
// Set up args/iterators
|
||||
args, ok := e[1].([]expression)
|
||||
if !ok {
|
||||
value = exception("'for' expected a list of arguments and values as its first argument, a non-list value was received")
|
||||
break
|
||||
}
|
||||
newEnv := &env{make(map[symbol]expression), en}
|
||||
updates := []expression{symbol("begin")}
|
||||
for _, v := range args {
|
||||
arg, ok := v.([]expression)
|
||||
if varname, varok := arg[0].(symbol); varok && ok && len(arg) >= 2 {
|
||||
newEnv.vars[varname] = eval(arg[1], en)
|
||||
} else {
|
||||
value = exception("'for' expected its first argument to be a list of lists, each with a symbol, a value, and an optional update expression. A list was given, but it contained a non-list value")
|
||||
break
|
||||
}
|
||||
if len(arg) >= 3 {
|
||||
updates = append(updates, []expression{symbol("set!"), arg[0], arg[2]})
|
||||
}
|
||||
}
|
||||
|
||||
// Get test and return expression
|
||||
testReturn, ok := e[2].([]expression)
|
||||
if !ok || len(testReturn) == 0 {
|
||||
value = exception("'for' expected a list containing a logic test and an optional return value expression for its second argument, a non-list was given")
|
||||
break
|
||||
}
|
||||
test := testReturn[0]
|
||||
var returnExpression expression = []expression{symbol("list")}
|
||||
if len(testReturn) > 1 {
|
||||
returnExpression = testReturn[1]
|
||||
}
|
||||
|
||||
body := []expression{symbol("begin")}
|
||||
body = append(body, e[3:]...)
|
||||
|
||||
for {
|
||||
// Check the condition by evaluating the test
|
||||
if !AnythingToBool(eval(test, newEnv)).(bool) {
|
||||
break
|
||||
}
|
||||
|
||||
// Run the body code
|
||||
eval(body, newEnv)
|
||||
|
||||
// Run the iteration updates
|
||||
eval(updates, newEnv)
|
||||
}
|
||||
|
||||
value = eval(returnExpression, newEnv)
|
||||
value = specialFor(e, en)
|
||||
case "set!":
|
||||
if len(e) < 3 {
|
||||
value = exception("Invalid 'set!' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
v, ok := e[1].(symbol)
|
||||
if !ok {
|
||||
e[1] = eval(e[1], en)
|
||||
v, ok = e[1].(symbol)
|
||||
if !ok {
|
||||
value = exception("'set!' expected a symbol as its first argument, a non-symbol was provided")
|
||||
break
|
||||
}
|
||||
}
|
||||
if strings.Contains(string(v), "::") {
|
||||
value = exception("'set!' cannot modify a symbol reference within a module, designated by '::' within a symbol name")
|
||||
break
|
||||
}
|
||||
val := eval(e[2], en)
|
||||
ex, err := en.Find(v)
|
||||
if err != nil {
|
||||
value = exception(err.Error())
|
||||
} else {
|
||||
ex.vars[v] = val
|
||||
value = val
|
||||
}
|
||||
value = specialSet(e, en)
|
||||
case "define":
|
||||
if len(e) < 3 {
|
||||
value = exception("Invalid 'define' syntax - too few arguments")
|
||||
break
|
||||
}
|
||||
sym, ok := e[1].(symbol)
|
||||
if !ok {
|
||||
e[1] = eval(e[1], en)
|
||||
sym, ok = e[1].(symbol)
|
||||
if !ok {
|
||||
value = exception("'define' expects a symbol as its first argument")
|
||||
break
|
||||
}
|
||||
}
|
||||
if strings.Contains(string(sym), "::") {
|
||||
value = exception("'define' cannot create a symbol reference within a module, designated by '::' within a symbol name")
|
||||
break
|
||||
}
|
||||
val := eval(e[2], en)
|
||||
en.vars[sym] = val
|
||||
value = val
|
||||
value = specialDefine(e, en)
|
||||
case "lambda", "λ":
|
||||
if len(e) < 3 {
|
||||
value = exception("'lambda' expects at least three arguments")
|
||||
break
|
||||
}
|
||||
b := []expression{symbol("begin")}
|
||||
b = append(b, e[2:]...)
|
||||
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}
|
||||
value = specialLambda(e, en)
|
||||
case "macro":
|
||||
if len(e) < 3 {
|
||||
value = exception("'macro' expects at least three arguments")
|
||||
break
|
||||
}
|
||||
b := []expression{symbol("begin")}
|
||||
b = append(b, e[2:]...)
|
||||
value = macro{e[1], b, en}
|
||||
value = specialMacro(e, en)
|
||||
case "begin":
|
||||
for _, i := range e[1:] {
|
||||
value = eval(i, en)
|
||||
}
|
||||
value = specialBegin(e, en)
|
||||
case "begin0":
|
||||
for ii, i := range e[1:] {
|
||||
if ii == 0 {
|
||||
value = eval(i, en)
|
||||
} else {
|
||||
eval(i, en)
|
||||
}
|
||||
}
|
||||
value = specialBegin0(e, en)
|
||||
case "usage":
|
||||
var procSigRE = regexp.MustCompile(`(?s)(\()([^() ]+\b)([^)]*)(\))(?:(\s*=>)([^\n]*))?(.*)`)
|
||||
var replacer = "\033[40;33;1m$1\033[95m$2\033[92m$3\033[33m$4\033[94m$5\033[36m$6\033[0m$7"
|
||||
|
@ -734,6 +486,8 @@ func apply(procedure expression, args []expression, name expression) (value expr
|
|||
}
|
||||
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:
|
||||
|
|
4
lib.go
4
lib.go
|
@ -1255,7 +1255,9 @@ var stdLibrary = vars{
|
|||
}
|
||||
|
||||
},
|
||||
"list": eval(Parse("(lambda ... ...)").([]expression)[0], &globalenv),
|
||||
"list": func(a ...expression) expression {
|
||||
return a
|
||||
},
|
||||
"list-ref": func(a ...expression) expression {
|
||||
// TODO remove this proc entirely someday?
|
||||
return globalenv.vars["ref"].(func(...expression) expression)(a...)
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,297 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func specialAnd(e []expression, en *env) expression {
|
||||
if len(e) < 2 {
|
||||
return exception("Invalid 'and' syntax - too few arguments")
|
||||
}
|
||||
var value expression
|
||||
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 = eval(item, en)
|
||||
if !AnythingToBool(value).(bool) {
|
||||
break OuterAnd
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func specialBegin0(e []expression, en *env) expression {
|
||||
var v expression
|
||||
for ii, i := range e[1:] {
|
||||
if ii == 0 {
|
||||
v = eval(i, en)
|
||||
} else {
|
||||
eval(i, en)
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func specialBegin(e []expression, en *env) expression {
|
||||
var v expression
|
||||
for _, i := range e[1:] {
|
||||
v = eval(i, en)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func specialCase(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("Invalid 'case' syntax - too few arguments")
|
||||
}
|
||||
target := eval(e[1], en)
|
||||
var value expression
|
||||
CaseLoop:
|
||||
for _, exp := range e[2:] {
|
||||
switch i := exp.(type) {
|
||||
case []expression:
|
||||
if len(i) < 2 {
|
||||
return exception("Invalid 'case' case, cases must take the form: `(<test> <expression>)")
|
||||
}
|
||||
if i[0] == "else" || i[0] == symbol("else") {
|
||||
value = eval(i[1], en)
|
||||
break CaseLoop
|
||||
}
|
||||
if reflect.DeepEqual(eval(i[0], en), target) {
|
||||
value = eval(i[1], en)
|
||||
break CaseLoop
|
||||
}
|
||||
default:
|
||||
return exception("Invalid 'case' case, cases must take the form: `(<test> <expression>)")
|
||||
}
|
||||
}
|
||||
// TODO this may not work
|
||||
if value == nil {
|
||||
value = make([]expression, 0)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func specialCond(e []expression, en *env) expression {
|
||||
if len(e) < 2 {
|
||||
return exception("Invalid 'cond' syntax - too few arguments")
|
||||
}
|
||||
|
||||
var value expression
|
||||
CondLoop:
|
||||
for _, exp := range e[1:] {
|
||||
switch i := exp.(type) {
|
||||
case []expression:
|
||||
if len(i) < 2 {
|
||||
return exception("Invalid 'cond' case, cases must take the form: `(<test> <expression>)")
|
||||
}
|
||||
if i[0] == "else" || i[0] == symbol("else") {
|
||||
value = eval(i[1], en)
|
||||
break CondLoop
|
||||
}
|
||||
if AnythingToBool(eval(i[0], en)).(bool) {
|
||||
value = eval(i[1], en)
|
||||
break CondLoop
|
||||
}
|
||||
default:
|
||||
return exception("Invalid 'cond' case, cases must take the form: `(<test> <expression>)")
|
||||
}
|
||||
}
|
||||
if value == nil {
|
||||
value = make([]expression, 0)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func specialDefine(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("Invalid 'define' syntax - too few arguments")
|
||||
}
|
||||
sym, ok := e[1].(symbol)
|
||||
if !ok {
|
||||
e[1] = eval(e[1], en)
|
||||
sym, ok = e[1].(symbol)
|
||||
if !ok {
|
||||
return exception("'define' expects a symbol as its first argument")
|
||||
}
|
||||
}
|
||||
if strings.Contains(string(sym), "::") {
|
||||
return exception("'define' cannot create a symbol reference within a module, designated by '::' within a symbol name")
|
||||
}
|
||||
val := eval(e[2], en)
|
||||
en.vars[sym] = val
|
||||
return val
|
||||
}
|
||||
|
||||
func specialFor(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'for' requires at least two arguments: a list of initializers and a test")
|
||||
}
|
||||
|
||||
// Set up args/iterators
|
||||
args, ok := e[1].([]expression)
|
||||
if !ok {
|
||||
return exception("'for' expected a list of arguments and values as its first argument, a non-list value was received")
|
||||
}
|
||||
newEnv := &env{make(map[symbol]expression), en}
|
||||
updates := []expression{symbol("begin")}
|
||||
for _, v := range args {
|
||||
arg, ok := v.([]expression)
|
||||
if varname, varok := arg[0].(symbol); varok && ok && len(arg) >= 2 {
|
||||
newEnv.vars[varname] = eval(arg[1], en)
|
||||
} else {
|
||||
return exception("'for' expected its first argument to be a list of lists, each with a symbol, a value, and an optional update expression. A list was given, but it contained a non-list value")
|
||||
}
|
||||
if len(arg) >= 3 {
|
||||
updates = append(updates, []expression{symbol("set!"), arg[0], arg[2]})
|
||||
}
|
||||
}
|
||||
|
||||
// Get test and return expression
|
||||
testReturn, ok := e[2].([]expression)
|
||||
if !ok || len(testReturn) == 0 {
|
||||
return exception("'for' expected a list containing a logic test and an optional return value expression for its second argument, a non-list was given")
|
||||
}
|
||||
test := testReturn[0]
|
||||
var returnExpression expression = []expression{symbol("list")}
|
||||
if len(testReturn) > 1 {
|
||||
returnExpression = testReturn[1]
|
||||
}
|
||||
|
||||
body := []expression{symbol("begin")}
|
||||
body = append(body, e[3:]...)
|
||||
|
||||
for {
|
||||
// Check the condition by evaluating the test
|
||||
if !AnythingToBool(eval(test, newEnv)).(bool) {
|
||||
break
|
||||
}
|
||||
|
||||
// Run the body code
|
||||
eval(body, newEnv)
|
||||
|
||||
// Run the iteration updates
|
||||
eval(updates, newEnv)
|
||||
}
|
||||
|
||||
return eval(returnExpression, newEnv)
|
||||
}
|
||||
|
||||
func specialIf(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("Invalid 'if' syntax - too few arguments")
|
||||
}
|
||||
if AnythingToBool(eval(e[1], en)).(bool) {
|
||||
return eval(e[2], en)
|
||||
}
|
||||
|
||||
if len(e) > 3 {
|
||||
return eval(e[3], en)
|
||||
}
|
||||
return make([]expression, 0)
|
||||
}
|
||||
|
||||
func specialLambda(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'lambda' expects at least three arguments")
|
||||
}
|
||||
b := []expression{symbol("begin")}
|
||||
b = append(b, e[2:]...)
|
||||
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]}
|
||||
}
|
||||
return proc{e[1], stringUnescapeEval(b), en, predicates}
|
||||
}
|
||||
|
||||
func specialMacro(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("'macro' expects at least three arguments")
|
||||
}
|
||||
b := []expression{symbol("begin")}
|
||||
b = append(b, e[2:]...)
|
||||
return macro{e[1], b, en}
|
||||
}
|
||||
|
||||
func specialOr(e []expression, en *env) expression {
|
||||
if len(e) < 2 {
|
||||
return exception("Invalid 'or' syntax - too few arguments")
|
||||
}
|
||||
var value expression
|
||||
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 = eval(item, en)
|
||||
if AnythingToBool(value).(bool) {
|
||||
break OuterOr
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func specialQuote(e []expression) expression {
|
||||
if len(e) < 2 {
|
||||
return exception("Invalid 'quote' syntax - too few arguments")
|
||||
}
|
||||
return stringUnescapeEval(e[1])
|
||||
}
|
||||
|
||||
func specialSet(e []expression, en *env) expression {
|
||||
if len(e) < 3 {
|
||||
return exception("Invalid 'set!' syntax - too few arguments")
|
||||
}
|
||||
v, ok := e[1].(symbol)
|
||||
if !ok {
|
||||
e[1] = eval(e[1], en)
|
||||
v, ok = e[1].(symbol)
|
||||
if !ok {
|
||||
return exception("'set!' expected a symbol as its first argument, a non-symbol was provided")
|
||||
}
|
||||
}
|
||||
if strings.Contains(string(v), "::") {
|
||||
return exception("'set!' cannot modify a symbol reference within a module, designated by '::' within a symbol name")
|
||||
}
|
||||
val := eval(e[2], en)
|
||||
ex, err := en.Find(v)
|
||||
if err != nil {
|
||||
return exception(err.Error())
|
||||
}
|
||||
ex.vars[v] = val
|
||||
return val
|
||||
}
|
||||
|
Loading…
Reference in New Issue