environments now have outer environment pointers

master
Jordan Orelli 12 years ago
parent f8ba2415ff
commit e86260e8dc

@ -10,22 +10,30 @@ func (u UnknownSymbolError) Error() string {
return fmt.Sprintf(`unknown symbol "%v"`, u.symbol) return fmt.Sprintf(`unknown symbol "%v"`, u.symbol)
} }
type environment map[symbol]interface{} type environment struct {
items map[symbol]interface{}
outer *environment
}
func (e environment) get(key symbol) (interface{}, error) { func (e environment) get(key symbol) (interface{}, error) {
v, ok := e[key] v, ok := e.items[key]
if ok { if ok {
debugPrint(fmt.Sprintf(`found key "%v": %v`, key, v)) debugPrint(fmt.Sprintf(`found key "%v": %v`, key, v))
return v, nil return v, nil
} }
if e.outer != nil {
return e.outer.get(key)
}
return nil, UnknownSymbolError{key} return nil, UnknownSymbolError{key}
} }
func (e environment) set(key symbol, val interface{}) { func (e environment) set(key symbol, val interface{}) {
e[key] = val e.items[key] = val
} }
func (e environment) defined(key symbol) bool { func (e environment) defined(key symbol) bool {
_, ok := e[key] _, err := e.get(key)
return ok return err == nil
} }

@ -20,7 +20,7 @@ func (s sexp) String() string {
type symbol string type symbol string
var universe = &environment{ var universe = &environment{map[symbol]interface{}{
"#t": true, "#t": true,
"#f": false, "#f": false,
"+": proc(addition), "+": proc(addition),
@ -31,7 +31,7 @@ var universe = &environment{
"quote": special(quote), "quote": special(quote),
"if": special(_if), "if": special(_if),
"set!": special(set), "set!": special(set),
} }, 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
func atom(t token) (interface{}, error) { func atom(t token) (interface{}, error) {

@ -14,61 +14,100 @@ type nargsInvalidError struct {
} }
func (n nargsInvalidError) Error() string { func (n nargsInvalidError) Error() string {
return fmt.Sprintf(`received %d arguments in *%v*, expected %d`, n.received, n.name, n.expected) return fmt.Sprintf(`received %d arguments in *%v*, expected %d`,
n.received, n.name, n.expected)
} }
// defines the built-in "define" construct. e.g.:
//
// (define x 5)
//
// would create the symbol "x" and set its value to 5.
func define(env *environment, args ...interface{}) (interface{}, error) { func define(env *environment, args ...interface{}) (interface{}, error) {
if len(args) != 2 { if len(args) != 2 {
return nil, nargsInvalidError{2, len(args), "define"} return nil, nargsInvalidError{2, len(args), "define"}
} }
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.:
//
// (quote (1 2 3))
//
// 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
// prevents evaluation of the first element of the list, in this case 1.
func quote(_ *environment, args ...interface{}) (interface{}, error) { func quote(_ *environment, args ...interface{}) (interface{}, error) {
if len(args) != 1 { if len(args) != 1 {
return nil, nargsInvalidError{1, len(args), "quote"} return nil, nargsInvalidError{1, len(args), "quote"}
} }
return args[0], nil return args[0], nil
} }
// defines the built-in "if" contruct. e.g.:
//
// (if #t "foo" "bar")
//
// would evaluate to "foo", while the following:
//
// (if #f "foo" "bar")
//
// would evaluate to "bar"
func _if(env *environment, args ...interface{}) (interface{}, error) { func _if(env *environment, args ...interface{}) (interface{}, error) {
if len(args) != 3 { if len(args) != 3 {
return nil, nargsInvalidError{3, len(args), "if"} return nil, nargsInvalidError{3, len(args), "if"}
} }
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 b, ok := v.(bool); ok && !b { if b, ok := v.(bool); ok && !b {
return eval(args[2], env) return eval(args[2], env)
} }
return eval(args[1], env) return eval(args[1], env)
} }
// defines the built-in "set" construct, which is used to set the value of an
// existing symbol in the provided environment. e.g.:
//
// (set! x 5)
//
// would set the symbol x to the value 5, if and only if the symbol x was
// previously defined.
func set(env *environment, args ...interface{}) (interface{}, error) { func set(env *environment, args ...interface{}) (interface{}, error) {
if len(args) != 2 { if len(args) != 2 {
return nil, nargsInvalidError{2, len(args), "set!"} return nil, nargsInvalidError{2, len(args), "set!"}
} }
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
} }

Loading…
Cancel
Save