diff --git a/env.go b/env.go index 566822d..ab44e02 100644 --- a/env.go +++ b/env.go @@ -15,6 +15,13 @@ type environment struct { outer *environment } +func newEnvironment(outer *environment) *environment { + return &environment{ + items: make(map[symbol]interface{}), + outer: outer, + } +} + func (e environment) get(key symbol) (interface{}, error) { v, ok := e.items[key] if ok { diff --git a/input.scm b/input.scm index e12785a..ff5de7d 100644 --- a/input.scm +++ b/input.scm @@ -37,3 +37,6 @@ x (if #f (quote "true-value") (quote "false-value")) (if #t (quote "true-value") (quote "false-value")) + +(define plusone (lambda (x) (+ x 1))) +(plusone 1) diff --git a/skeam.go b/skeam.go index 518dc3b..3017e0e 100644 --- a/skeam.go +++ b/skeam.go @@ -31,6 +31,7 @@ var universe = &environment{map[symbol]interface{}{ "quote": special(quote), "if": special(_if), "set!": special(set), + "lambda": special(mklambda), }, nil} // 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)) default: diff --git a/special.go b/special.go index 423d473..b889ec8 100644 --- a/special.go +++ b/special.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "reflect" ) @@ -111,3 +112,65 @@ func set(env *environment, args ...interface{}) (interface{}, error) { 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 +}