Starts splitting out special forms into functions

This commit is contained in:
sloum 2023-05-09 09:25:56 -07:00
parent 3b385c4314
commit bc375214af
4 changed files with 315 additions and 262 deletions

View File

@ -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
View File

@ -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...)

BIN
slope.1.gz Normal file

Binary file not shown.

297
specialForms.go Normal file
View File

@ -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
}