package main import ( "bufio" "errors" "fmt" "io" "os" "reflect" "strconv" "strings" ) var DEBUG = false type sexp []interface{} func (s sexp) String() string { parts := make([]string, len(s)) for i, _ := range s { parts[i] = fmt.Sprint(s[i]) } return "(" + strings.Join(parts, " ") + ")" } type list struct { sexp quotelevel int } func (l list) String() string { return l.sexp.String() } type symbol string var universe = &environment{map[symbol]interface{}{ // predefined values "#t": true, "#f": false, "null": nil, // builtin functions "+": builtin(addition), "-": builtin(subtraction), "*": builtin(multiplication), "/": builtin(division), ">": builtin(gt), ">=": builtin(gte), "<": builtin(lt), "<=": builtin(lte), "cons": builtin(cons), "car": builtin(car), "cdr": builtin(cdr), "length": builtin(length), "list": builtin(lst), "list?": builtin(islist), "not": builtin(not), "null?": builtin(isnull), "symbol?": builtin(issymbol), // "=" // "equal?" // "eq?" // "append" // special forms "begin": special(begin), "define": special(define), "if": special(_if), "lambda": special(mklambda), "quote": special(quote), "set!": special(set), }, nil} // parses the string lexeme into a value that can be eval'd func atom(t token) (interface{}, error) { switch t.t { case integerToken: val, err := strconv.ParseInt(t.lexeme, 10, 64) if err != nil { return nil, err } return val, nil case floatToken: val, err := strconv.ParseFloat(t.lexeme, 64) if err != nil { return nil, err } return val, nil case stringToken: return t.lexeme, nil case symbolToken: return symbol(t.lexeme), nil } return nil, fmt.Errorf("unable to atomize token: %v", t) } // reads in tokens on the channel until a matching close paren is found. func (s *sexp) readIn(c chan token) error { for t := range c { switch t.t { case closeParenToken: return nil case openParenToken: child := make(sexp, 0) if err := child.readIn(c); err != nil { return err } *s = append(*s, child) default: v, err := atom(t) if err != nil { return err } *s = append(*s, v) } } return errors.New("unexpected EOF in sexp.readIn") } // parses one value that can be evaled from the channel func parse(c chan token) (interface{}, error) { for t := range c { switch t.t { case closeParenToken: return nil, errors.New("unexpected EOF in read") case openParenToken: s := make(sexp, 0) if err := s.readIn(c); err != nil { return nil, err } return s, nil default: return atom(t) } } return nil, io.EOF } func eval(v interface{}, env *environment) (interface{}, error) { if v == nil { return sexp{}, nil } switch t := v.(type) { case symbol: debugPrint("eval symbol") s, err := env.get(t) if err != nil { return nil, err } return eval(s, env) case sexp: debugPrint("eval sexp") if len(t) == 0 { return nil, errors.New("illegal evaluation of empty sexp ()") } // eval the first item v, err := eval(t[0], env) if err != nil { return nil, err } // check to see if this is a special form if spec, ok := v.(special); ok { if len(t) > 1 { return spec(env, t[1:]...) } else { return spec(env) } } // exec builtin func if one exists if b, ok := v.(builtin); ok { if len(t) > 1 { return b.call(env, t[1:]) } else { return b.call(env, nil) } } // exec lambda if possible 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: return v, nil } panic("not reached") } func evalall(c chan token, env *environment) { for { v, err := parse(c) switch err { case io.EOF: return case nil: if v, err := eval(v, env); err != nil { fmt.Println("error:", err) return } else { if v != nil { fmt.Println(v) } } default: fmt.Printf("error in eval: %v\n", err) } } } func args() { filename := os.Args[1] f, err := os.Open(filename) if err != nil { fmt.Fprintln(os.Stderr, "unable to read file ", filename) os.Exit(1) } defer f.Close() c := make(chan token, 32) go lex(bufio.NewReader(f), c) evalall(c, universe) } func main() { if DEBUG { fmt.Println(universe) } if len(os.Args) > 1 { args() return } r := bufio.NewReader(os.Stdin) for { fmt.Print("> ") line, prefix, err := r.ReadLine() if prefix { fmt.Println("(prefix)") } switch err { case nil: break case io.EOF: fmt.Print("\n") return default: fmt.Println("error: ", err) continue } c := make(chan token, 32) go lexs(string(line)+"\n", c) evalall(c, universe) } }