added lambda support

master
Jordan Orelli 12 years ago
parent 77195faff8
commit 51a27aaa9b

@ -15,6 +15,13 @@ type environment struct {
outer *environment outer *environment
} }
func newEnvironment(outer *environment) *environment {
return &environment{
items: make(map[symbol]interface{}),
outer: outer,
}
}
func (e environment) get(key symbol) (interface{}, error) { func (e environment) get(key symbol) (interface{}, error) {
v, ok := e.items[key] v, ok := e.items[key]
if ok { if ok {

@ -37,3 +37,6 @@ x
(if #f (quote "true-value") (quote "false-value")) (if #f (quote "true-value") (quote "false-value"))
(if #t (quote "true-value") (quote "false-value")) (if #t (quote "true-value") (quote "false-value"))
(define plusone (lambda (x) (+ x 1)))
(plusone 1)

@ -31,6 +31,7 @@ var universe = &environment{map[symbol]interface{}{
"quote": special(quote), "quote": special(quote),
"if": special(_if), "if": special(_if),
"set!": special(set), "set!": special(set),
"lambda": special(mklambda),
}, 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
@ -149,6 +150,14 @@ func eval(v interface{}, env *environment) (interface{}, error) {
} }
} }
if l, ok := v.(lambda); ok {
if len(t) > 1 {
return l.call(env, t[1:])
} else {
return l.call(env, nil)
}
}
return nil, fmt.Errorf(`expected special form or builtin procedure, received %v`, reflect.TypeOf(v)) return nil, fmt.Errorf(`expected special form or builtin procedure, received %v`, reflect.TypeOf(v))
default: default:

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
) )
@ -111,3 +112,65 @@ func set(env *environment, args ...interface{}) (interface{}, error) {
return nil, nil return nil, nil
} }
type lambda struct {
env *environment
arglabels []symbol
body sexp
}
func (l lambda) call(env *environment, rawArgs []interface{}) (interface{}, error) {
debugPrint("call lambda")
args := make([]interface{}, 0, len(rawArgs))
for _, raw := range rawArgs {
v, err := eval(raw, env)
if err != nil {
return nil, err
}
args = append(args, v)
}
if len(args) != len(l.arglabels) {
return nil, errors.New("parity error")
}
for i := range args {
l.env.set(l.arglabels[i], args[i])
}
return eval(l.body, l.env)
}
// defines the built-in lambda construct. e.g.:
//
// (lambda (x) (* x x))
//
// would evaluate to a lambda that, when executed, squares its input.
func mklambda(env *environment, args ...interface{}) (interface{}, error) {
debugPrint("mklambda")
if len(args) != 2 {
return nil, nargsInvalidError{2, len(args), "lambda"}
}
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))
for _, v := range params {
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)
}
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
}

Loading…
Cancel
Save