defined the callable interface

master
Jordan Orelli 12 years ago
parent b387d909b5
commit 1ee8bff82c

@ -18,6 +18,10 @@ type sexp struct {
quotelvl int quotelvl int
} }
type callable interface {
call(*environment, []interface{}) (interface{}, error)
}
func newSexp() *sexp { func newSexp() *sexp {
return &sexp{ return &sexp{
items: make([]interface{}, 0, 8), items: make([]interface{}, 0, 8),
@ -73,12 +77,12 @@ var universe = &environment{map[symbol]interface{}{
// "append" // "append"
// special forms // special forms
"begin": special(begin), symbol(begin.name): begin,
"define": special(define), symbol(define.name): define,
"if": special(_if), symbol(_if.name): _if,
"lambda": special(mklambda), symbol(mklambda.name): mklambda,
"quote": special(quote), symbol(quote.name): quote,
"set!": special(set), symbol(set.name): set,
}, nil} }, nil}
// parses the string lexeme into a value that can be eval'd // parses the string lexeme into a value that can be eval'd
@ -181,35 +185,14 @@ func eval(v interface{}, env *environment) (interface{}, error) {
return nil, err return nil, err
} }
// check to see if this is a special form c, ok := v.(callable)
if spec, ok := v.(special); ok { if !ok {
debugPrint("special!") return nil, fmt.Errorf(`expected special form or builtin procedure, received %v`, reflect.TypeOf(v))
if len(t.items) > 1 {
return spec(env, t.items[1:]...)
} else {
return spec(env)
}
}
// exec builtin func if one exists
if b, ok := v.(builtin); ok {
if len(t.items) > 1 {
return b.call(env, t.items[1:])
} else {
return b.call(env, nil)
}
} }
if len(t.items) > 1 {
// exec lambda if possible return c.call(env, t.items[1:])
if l, ok := v.(lambda); ok {
if len(t.items) > 1 {
return l.call(env, t.items[1:])
} else {
return l.call(env, nil)
}
} }
return c.call(env, nil)
return nil, fmt.Errorf(`expected special form or builtin procedure, received %v`, reflect.TypeOf(v))
default: default:
debugPrint("default eval") debugPrint("default eval")

@ -9,7 +9,37 @@ import (
// type special is a callable outside of the normal execution workflow. That // type special is a callable outside of the normal execution workflow. That
// is, a special receives its arguments unevaluated, unlike lambdas or builtin, // is, a special receives its arguments unevaluated, unlike lambdas or builtin,
// both of whose arguments are evaluated upon invocation. // both of whose arguments are evaluated upon invocation.
type special func(*environment, ...interface{}) (interface{}, error) // type special func(*environment, ...interface{}) (interface{}, error)
type special struct {
name string
arity int
variadic bool
fn func(*environment, []interface{}) (interface{}, error)
}
func (s special) checkArity(n int) error {
if n == s.arity {
return nil
}
if s.variadic && n > s.arity {
return nil
}
return arityError{
expected: s.arity,
received: n,
name: s.name,
variadic: s.variadic,
}
}
func (s special) call(env *environment, rawArgs []interface{}) (interface{}, error) {
if err := s.checkArity(len(rawArgs)); err != nil {
return nil, err
}
return s.fn(env, rawArgs)
}
// type arityError is used to store information related to arity errors. That // type arityError is used to store information related to arity errors. That
// is, the invocation of a callable with the wrong number of arguments. // is, the invocation of a callable with the wrong number of arguments.
@ -50,23 +80,23 @@ func checkArity(arity int, args []interface{}, name string) error {
// (define x 5) // (define x 5)
// //
// would create the symbol "x" and set its value to 5. // would create the symbol "x" and set its value to 5.
func define(env *environment, args ...interface{}) (interface{}, error) { var define = special{
if err := checkArity(2, args, "define"); err != nil { name: "define",
return nil, err arity: 2,
} fn: func(env *environment, args []interface{}) (interface{}, error) {
s, ok := args[0].(symbol)
s, ok := args[0].(symbol) if !ok {
if !ok { return nil, fmt.Errorf(`first argument to *define* must be symbol, received %v`, reflect.TypeOf(args[0]))
return nil, fmt.Errorf(`first argument to *define* must be symbol, received %v`, reflect.TypeOf(args[0])) }
}
v, err := eval(args[1], env) v, err := eval(args[1], env)
if err != nil { if err != nil {
return nil, err return nil, err
} }
env.set(s, v) env.set(s, v)
return nil, nil return nil, nil
},
} }
// defines the built-in "quote" construct. e.g.: // defines the built-in "quote" construct. e.g.:
@ -76,19 +106,19 @@ func define(env *environment, args ...interface{}) (interface{}, error) {
// would evaluate to the list (1 2 3). That is, quote is a function of arity 1 // would evaluate to the list (1 2 3). That is, quote is a function of arity 1
// that is effectively a no-op; the input value is not evaluated, which // that is effectively a no-op; the input value is not evaluated, which
// prevents evaluation of the first element of the list, in this case 1. // prevents evaluation of the first element of the list, in this case 1.
func quote(_ *environment, args ...interface{}) (interface{}, error) { var quote = special{
if err := checkArity(1, args, "quote"); err != nil { name: "quote",
return nil, err arity: 1,
} fn: func(_ *environment, args []interface{}) (interface{}, error) {
switch t := args[0].(type) {
switch t := args[0].(type) { case *sexp:
case *sexp: t.quotelvl++
t.quotelvl++ return t, nil
return t, nil default:
default: return &sexp{items: []interface{}{t}, quotelvl: 1}, nil
return &sexp{items: []interface{}{t}, quotelvl: 1}, nil }
} panic("not reached")
panic("not reached") },
} }
// turns an arbitrary lisp value into a boolean. Apparently the sematics of // turns an arbitrary lisp value into a boolean. Apparently the sematics of
@ -110,20 +140,20 @@ func booleanize(v interface{}) bool {
// (if #f "foo" "bar") // (if #f "foo" "bar")
// //
// would evaluate to "bar" // would evaluate to "bar"
func _if(env *environment, args ...interface{}) (interface{}, error) { var _if = special{
if err := checkArity(3, args, "if"); err != nil { name: "if",
return nil, err arity: 3,
} fn: func(env *environment, args []interface{}) (interface{}, error) {
v, err := eval(args[0], env)
v, err := eval(args[0], env) if err != nil {
if err != nil { return nil, err
return nil, err }
}
if booleanize(v) { if booleanize(v) {
return eval(args[1], env) return eval(args[1], env)
} }
return eval(args[2], env) return eval(args[2], env)
},
} }
// defines the built-in "set!" construct, which is used to set the value of an // defines the built-in "set!" construct, which is used to set the value of an
@ -133,27 +163,27 @@ func _if(env *environment, args ...interface{}) (interface{}, error) {
// //
// would set the symbol x to the value 5, if and only if the symbol x was // would set the symbol x to the value 5, if and only if the symbol x was
// previously defined. // previously defined.
func set(env *environment, args ...interface{}) (interface{}, error) { var set = special{
if err := checkArity(2, args, "set!"); err != nil { name: "set!",
return nil, err arity: 2,
} fn: func(env *environment, args []interface{}) (interface{}, error) {
s, ok := args[0].(symbol)
s, ok := args[0].(symbol) if !ok {
if !ok { return nil, fmt.Errorf(`first argument to *set!* must be symbol, received %v`, reflect.TypeOf(args[0]))
return nil, fmt.Errorf(`first argument to *set!* must be symbol, received %v`, reflect.TypeOf(args[0])) }
}
if !env.defined(s) { if !env.defined(s) {
return nil, fmt.Errorf(`cannot *set!* undefined symbol %v`, s) return nil, fmt.Errorf(`cannot *set!* undefined symbol %v`, s)
} }
v, err := eval(args[1], env) v, err := eval(args[1], env)
if err != nil { if err != nil {
return nil, err return nil, err
} }
env.set(s, v) env.set(s, v)
return nil, nil return nil, nil
},
} }
type lambda struct { type lambda struct {
@ -190,32 +220,33 @@ func (l lambda) call(env *environment, rawArgs []interface{}) (interface{}, erro
// (lambda (x) (* x x)) // (lambda (x) (* x x))
// //
// would evaluate to a lambda that, when executed, squares its input. // would evaluate to a lambda that, when executed, squares its input.
func mklambda(env *environment, args ...interface{}) (interface{}, error) { var mklambda = special{
debugPrint("mklambda") name: "lambda",
if err := checkArity(2, args, "lambda"); err != nil { arity: 2,
return nil, err fn: func(env *environment, args []interface{}) (interface{}, error) {
} debugPrint("mklambda")
params, ok := args[0].(*sexp) params, ok := args[0].(*sexp)
if !ok {
return nil, fmt.Errorf(`first argument to *lambda* must be sexp, received %v`, reflect.TypeOf(args[0]))
}
arglabels := make([]symbol, 0, len(params.items))
for _, v := range params.items {
s, ok := v.(symbol)
if !ok { if !ok {
return nil, fmt.Errorf(`lambda args must all be symbols; received invalid %v`, reflect.TypeOf(v)) return nil, fmt.Errorf(`first argument to *lambda* must be sexp, received %v`, reflect.TypeOf(args[0]))
} }
arglabels = append(arglabels, s)
}
body, ok := args[1].(*sexp) arglabels := make([]symbol, 0, len(params.items))
if !ok { for _, v := range params.items {
return nil, fmt.Errorf(`second argument to *lambda* must be sexp, received %v`, reflect.TypeOf(args[1])) s, ok := v.(symbol)
} if !ok {
return nil, fmt.Errorf(`lambda args must all be symbols; received invalid %v`, reflect.TypeOf(v))
}
arglabels = append(arglabels, s)
}
return lambda{env, arglabels, body}, nil body, ok := args[1].(*sexp)
if !ok {
return nil, fmt.Errorf(`second argument to *lambda* must be sexp, received %v`, reflect.TypeOf(args[1]))
}
return lambda{env, arglabels, body}, nil
},
} }
// defines the built-in "begin" construct. A "begin" statement evaluates each // defines the built-in "begin" construct. A "begin" statement evaluates each
@ -225,17 +256,19 @@ func mklambda(env *environment, args ...interface{}) (interface{}, error) {
// (begin (+ 1 1) (* 2 2) (+ 3 3)) // (begin (+ 1 1) (* 2 2) (+ 3 3))
// //
// would evaluate to 6. // would evaluate to 6.
func begin(env *environment, args ...interface{}) (interface{}, error) { var begin = special{
debugPrint("begin") name: "begin",
variadic: true,
var err error fn: func(env *environment, args []interface{}) (interface{}, error) {
var v interface{} debugPrint("begin")
for _, arg := range args { var err error
v, err = eval(arg, env) var v interface{}
if err != nil { for _, arg := range args {
return nil, err v, err = eval(arg, env)
if err != nil {
return nil, err
}
} }
} return v, nil
},
return v, nil
} }

Loading…
Cancel
Save