You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
4.1 KiB
Go

12 years ago
package main
import (
"bufio"
12 years ago
"errors"
12 years ago
"fmt"
"io"
"os"
"reflect"
12 years ago
"strconv"
12 years ago
)
var DEBUG = false
12 years ago
type sexp []interface{}
12 years ago
func (s sexp) String() string {
return "(" + fmt.Sprint(s...) + ")"
}
12 years ago
type symbol string
var universe = &environment{
12 years ago
"true": true,
"false": false,
"+": proc(addition),
"-": proc(subtraction),
"*": proc(multiplication),
"/": proc(division),
"define": special(define),
12 years ago
"quote": special(quote),
12 years ago
"if": special(_if),
}
12 years ago
// parses the string lexeme into a value that can be eval'd
func atom(t token) (interface{}, error) {
12 years ago
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)
12 years ago
}
// reads in tokens on the channel until a matching close paren is found.
func (s *sexp) readIn(c chan token) error {
12 years ago
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")
12 years ago
}
// parses one value that can be evaled from the channel
func parse(c chan token) (interface{}, error) {
12 years ago
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
12 years ago
}
func eval(v interface{}, env *environment) (interface{}, error) {
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 ()")
}
// get first element as symbol
s, ok := t[0].(symbol)
if !ok {
return nil, errors.New("expected a symbol")
}
// resolve symbol
v, err := env.get(s)
if err != nil {
return nil, err
}
if spec, ok := v.(special); ok {
if len(t) > 1 {
return spec(env, t[1:]...)
} else {
return spec(env)
}
}
p, ok := v.(proc)
if !ok {
return nil, fmt.Errorf("expected proc, found %v", reflect.TypeOf(v))
}
if len(t) > 1 {
args := make([]interface{}, 0, len(t)-1)
for _, raw := range t[1:] {
v, err := eval(raw, env)
if err != nil {
return nil, err
}
args = append(args, v)
}
inner, err := p(args...)
if err != nil {
return nil, err
}
return eval(inner, env)
}
inner, err := p()
if err != nil {
return nil, err
}
return eval(inner, env)
default:
return v, nil
}
return nil, nil
12 years ago
}
func evalall(c chan token, env *environment) {
12 years ago
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)
12 years ago
return
} else {
fmt.Println(v)
}
12 years ago
default:
fmt.Println("error in eval: %v", err)
}
}
12 years ago
}
func args() {
filename := os.Args[1]
f, err := os.Open(filename)
12 years ago
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)
12 years ago
}
}