diff --git a/args.go b/args.go index 91bcd17..f6a98a0 100644 --- a/args.go +++ b/args.go @@ -1,7 +1,6 @@ package main import ( - "bufio" "flag" "fmt" "io" @@ -20,12 +19,8 @@ func runfile() { } defer f.Close() - out, errors := make(chan interface{}), make(chan error) - go defaultInterpreter(out, errors) - - c := make(chan token, 32) - go lex(bufio.NewReader(f), c) - evalall(c, out, errors, universe) + i := newInterpreter(f, os.Stdout, os.Stderr) + i.run(universe) } func printErrorMsg(message string) { diff --git a/eval.go b/eval.go new file mode 100644 index 0000000..f9fdcda --- /dev/null +++ b/eval.go @@ -0,0 +1,118 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + "io" + "reflect" +) + +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 t.len() == 0 { + return nil, errors.New("illegal evaluation of empty sexp ()") + } + + if t.quotelvl > 0 { + return t, nil + } + + // eval the first item + v, err := eval(t.items[0], env) + if err != nil { + return nil, err + } + + c, ok := v.(callable) + if !ok { + return nil, fmt.Errorf(`expected special form or builtin procedure, received %v`, reflect.TypeOf(v)) + } + if len(t.items) > 1 { + return c.call(env, t.items[1:]) + } + return c.call(env, nil) + + default: + debugPrint("default eval") + return v, nil + } + + panic("not reached") +} + +type interpreter struct { + in io.Reader // reader of input source code + out1 io.Writer // writer of evaluated values + out2 io.Writer // writer of error info + tokens chan token // tokens returns from the lexer (internal only) + values chan interface{} // values returned from the interpreter (internal only) + errors chan error // errors returned from the interpreter (internal only) +} + +func newInterpreter(in io.Reader, out1, out2 io.Writer) *interpreter { + return &interpreter{ + in: in, + out1: out1, + out2: out2, + tokens: make(chan token), + values: make(chan interface{}), + errors: make(chan error), + } +} + +func (i interpreter) run(env *environment) { + go lex(bufio.NewReader(i.in), i.tokens) + go i.send() + for { + v, err := parse(i.tokens) + switch err { + case io.EOF: + fmt.Println("EOF lol") + return + case nil: + i.eval(v, env) + default: + i.errors <- err + } + } +} + +func (i interpreter) eval(v interface{}, env *environment) { + val, err := eval(v, env) + if err != nil { + i.errors <- err + return + } + i.values <- val +} + +func (i interpreter) send() { + for { + select { + case v := <-i.values: + if _, err := fmt.Fprintln(i.out1, v); err != nil { + fmt.Println("can't write out to client: ", err) + } + case e := <-i.errors: + if _, err := fmt.Fprintln(i.out2, e); err != nil { + fmt.Println("can't write error to client: ", err) + } + } + } +} diff --git a/skeam.go b/skeam.go index 67730bd..aefdf88 100644 --- a/skeam.go +++ b/skeam.go @@ -1,13 +1,11 @@ package main import ( - "bufio" "errors" "flag" "fmt" "io" "os" - "reflect" "strconv" "strings" ) @@ -162,84 +160,6 @@ func parse(c chan token) (interface{}, error) { 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 t.len() == 0 { - return nil, errors.New("illegal evaluation of empty sexp ()") - } - - if t.quotelvl > 0 { - return t, nil - } - - // eval the first item - v, err := eval(t.items[0], env) - if err != nil { - return nil, err - } - - c, ok := v.(callable) - if !ok { - return nil, fmt.Errorf(`expected special form or builtin procedure, received %v`, reflect.TypeOf(v)) - } - if len(t.items) > 1 { - return c.call(env, t.items[1:]) - } - return c.call(env, nil) - - default: - debugPrint("default eval") - return v, nil - } - - panic("not reached") -} - -func evalall(c chan token, out chan interface{}, e chan error, env *environment) { - for { - v, err := parse(c) - switch err { - case io.EOF: - return - case nil: - if v, err := eval(v, env); err != nil { - e <- err - return - } else { - out <- v - } - default: - e <- err - } - } -} - -func defaultInterpreter(out chan interface{}, errors chan error) { - for { - select { - case v := <-out: - fmt.Println(v) - case err := <-errors: - fmt.Printf("error: %v", err) - } - } -} - func main() { flag.BoolVar(&DEBUG, "debug", false, "puts the interpreter in debug mode") flag.Parse() @@ -255,10 +175,6 @@ func main() { return } - out, errors := make(chan interface{}), make(chan error) - go defaultInterpreter(out, errors) - - c := make(chan token, 32) - go lex(bufio.NewReader(os.Stdin), c) - evalall(c, out, errors, universe) + i := newInterpreter(os.Stdin, os.Stdout, os.Stderr) + i.run(universe) } diff --git a/tcp_connections.go b/tcp_connections.go index 3212369..df87de0 100644 --- a/tcp_connections.go +++ b/tcp_connections.go @@ -1,26 +1,13 @@ package main import ( - "bufio" - "errors" - "fmt" "github.com/jordanorelli/skeam/cm" - "io" "net" ) const MAX_SEXP_LINES = 40 var manager = cm.New() -var errSexpTooLong = errors.New("error: sexp is too long") - -type tcpInterpreter struct { - fout io.Writer // buffered file-like output stream - ferr io.Writer // buffered file-like error stream - tokens chan token // tokens returns from the lexer - values chan interface{} // values returned from the interpreter - errors chan error // errors returned from the interpreter -} func runTCPServer() { addr, err := net.ResolveTCPAddr("tcp", *tcpAddr) @@ -41,43 +28,10 @@ func runTCPServer() { } } -func newTcpInterpreter() *tcpInterpreter { - return &tcpInterpreter{ - tokens: make(chan token), - values: make(chan interface{}), - errors: make(chan error), - } -} - -func (t *tcpInterpreter) send() { - for { - select { - case v := <-t.values: - if _, err := fmt.Fprintln(t.fout, v); err != nil { - fmt.Println("can't write out to client: ", err) - } - case e := <-t.errors: - if _, err := fmt.Fprintln(t.ferr, e); err != nil { - fmt.Println("can't write error to client: ", err) - } - } - } -} - -func (t *tcpInterpreter) Run(in io.Reader, out, errors io.Writer) { - go lex(bufio.NewReader(in), t.tokens) - - t.fout = out - t.ferr = errors - go t.send() - - evalall(t.tokens, t.values, t.errors, universe) -} - func startConnection(conn net.Conn, m *cm.Manager) { m.Add(conn) defer m.Remove(conn) - i := newTcpInterpreter() - i.Run(conn, conn, conn) + i := newInterpreter(conn, conn, conn) + i.run(universe) }