From d672d51f7dd06dd87da5a5eb37de0e309e7e9694 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 15 Jun 2013 13:59:53 -0400 Subject: [PATCH] hmm, tcp should work more like local... --- args.go | 10 +++----- cm/manager.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++ skeam.go | 50 +++++++++++++++---------------------- tcp_connections.go | 8 ++---- 4 files changed, 85 insertions(+), 44 deletions(-) diff --git a/args.go b/args.go index 8ef0e6e..91bcd17 100644 --- a/args.go +++ b/args.go @@ -10,13 +10,9 @@ import ( var tcpAddr = flag.String("tcp", "", "foo") -func args() { - flag.Parse() - if *tcpAddr != "" { - runTCPServer() - return - } - filename := flag.Args()[1] +func runfile() { + filename := flag.Args()[0] + fmt.Println(filename) f, err := os.Open(filename) if err != nil { fmt.Fprintln(os.Stderr, "unable to read file ", filename) diff --git a/cm/manager.go b/cm/manager.go index 02acf54..5540a22 100644 --- a/cm/manager.go +++ b/cm/manager.go @@ -2,12 +2,14 @@ package cm import ( "net" + "strings" ) type Manager struct { active map[net.Conn]bool connect chan net.Conn disconnect chan net.Conn + write chan writeOp } func New() *Manager { @@ -15,6 +17,7 @@ func New() *Manager { active: make(map[net.Conn]bool, 10), connect: make(chan net.Conn), disconnect: make(chan net.Conn), + write: make(chan writeOp), } go m.run() return m @@ -27,10 +30,20 @@ func (m *Manager) run() { m.active[conn] = true case conn := <-m.disconnect: delete(m.active, conn) + case op := <-m.write: + m.broadcast(op) } } } +func (m *Manager) broadcast(op writeOp) { + var res writeResponse + for conn, _ := range m.active { + res.add(conn.Write(op.data)) + } + op.reply <- res +} + func (m *Manager) Add(conn net.Conn) { m.connect <- conn } @@ -38,3 +51,51 @@ func (m *Manager) Add(conn net.Conn) { func (m *Manager) Remove(conn net.Conn) { m.disconnect <- conn } + +func (m *Manager) Write(b []byte) (int, error) { + op := *newWriteOp(b) + m.write <- op + res := <-op.reply + return res.i, res.e +} + +type multiError []error + +func (e multiError) add(err error) { + if e == nil { + e = make([]error, 0, 4) + } + e = append(e, err) +} + +func (e multiError) Error() string { + messages := make([]string, len(e)) + for i, _ := range e { + messages[i] = e[i].Error() + } + return strings.Join(messages, " | ") +} + +type writeResponse struct { + i int + e multiError +} + +func (res writeResponse) add(n int, e error) { + if e != nil { + res.e.add(e) + } + res.i += n +} + +type writeOp struct { + data []byte + reply chan writeResponse +} + +func newWriteOp(b []byte) *writeOp { + return &writeOp{ + data: b, + reply: make(chan writeResponse), + } +} diff --git a/skeam.go b/skeam.go index 6a1adab..650302a 100644 --- a/skeam.go +++ b/skeam.go @@ -3,6 +3,7 @@ package main import ( "bufio" "errors" + "flag" "fmt" "io" "os" @@ -140,22 +141,23 @@ func (s *sexp) readIn(c chan token) error { } // parses one value that can be evaled from the channel -func parse(c chan token) (interface{}, error) { +func parse(c chan token) (interface{}, bool, error) { for t := range c { switch t.t { case closeParenToken: - return nil, errors.New("unexpected EOF in read") + return nil, false, errors.New("unexpected EOF in read") case openParenToken: s := newSexp() if err := s.readIn(c); err != nil { - return nil, err + return nil, true, err } - return s, nil + return s, false, nil default: - return atom(t) + v, err := atom(t) + return v, false, err } } - return nil, io.EOF + return nil, false, io.EOF } func eval(v interface{}, env *environment) (interface{}, error) { @@ -208,7 +210,7 @@ func eval(v interface{}, env *environment) (interface{}, error) { func evalall(c chan token, out chan interface{}, e chan error, env *environment) { for { - v, err := parse(c) + v, _, err := parse(c) switch err { case io.EOF: return @@ -237,37 +239,23 @@ func defaultInterpreter(out chan interface{}, errors chan error) { } func main() { + flag.Parse() if DEBUG { fmt.Println(universe) } - if len(os.Args) > 1 { - args() + if *tcpAddr != "" { + runTCPServer() + return + } + if len(flag.Args()) > 0 { + runfile() return } out, errors := make(chan interface{}), make(chan error) go defaultInterpreter(out, errors) - 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, out, errors, universe) - } + c := make(chan token, 32) + go lex(bufio.NewReader(os.Stdin), c) + evalall(c, out, errors, universe) } diff --git a/tcp_connections.go b/tcp_connections.go index 0d3276f..98e7c50 100644 --- a/tcp_connections.go +++ b/tcp_connections.go @@ -11,19 +11,15 @@ import ( var manager = cm.New() func tcpInterpreter(conn net.Conn, userinput chan string, out chan interface{}, errors chan error) { - prompt := func() { - io.WriteString(conn, "> ") - } - prompt() for { select { case v := <-out: - fmt.Fprintln(conn, v) - prompt() + fmt.Fprintln(manager, v) case err := <-errors: fmt.Fprintf(conn, "error: %v", err) case line := <-userinput: tokens := make(chan token, 32) + // this is probably dumb go lexs(line+"\n", tokens) go evalall(tokens, out, errors, universe) }