diff --git a/tcp_connections.go b/tcp_connections.go index 0cb1c51..3212369 100644 --- a/tcp_connections.go +++ b/tcp_connections.go @@ -7,7 +7,6 @@ import ( "github.com/jordanorelli/skeam/cm" "io" "net" - "strings" ) const MAX_SEXP_LINES = 40 @@ -15,97 +14,25 @@ const MAX_SEXP_LINES = 40 var manager = cm.New() var errSexpTooLong = errors.New("error: sexp is too long") -func depth(s string) int { - n := 0 - for _, r := range s { - switch r { - case '(': - n += 1 - case ')': - n -= 1 - case ';': - return n - } - } - return n -} - -func tcpInterpreter(conn net.Conn, userinput chan string, out chan interface{}, errors chan error) { - lines := make([]string, 0, MAX_SEXP_LINES) - currentDepth := 0 - addLine := func(line string) error { - if len(lines) >= MAX_SEXP_LINES { - return errSexpTooLong - } - lines = append(lines, line+"\n") - return nil - } - errorMode := false - skipLine := func(line string) { - currentDepth += depth(line) - if currentDepth == 0 { - errorMode = false - } - } - s := make(chan string) - go func() { - for program := range s { - tokens := make(chan token, 32) - go lexs(program, tokens) - evalall(tokens, out, errors, universe) - } - }() - go func() { - for v := range out { - fmt.Fprintln(conn, v) - } - }() - for { - select { - case err := <-errors: - fmt.Fprintf(conn, "error: %v\n", err) - case line := <-userinput: - if errorMode { - skipLine(line) - break - } - lineDepth := depth(line) - currentDepth += lineDepth - - if currentDepth < 0 { - lines = lines[:0] - currentDepth = 0 - break - } - - if len(lines) == 0 && lineDepth == 0 { - s <- line + "\n" - break - } - - if err := addLine(line); err != nil { - errorMode = true - lines = lines[:0] - currentDepth = 0 - break - } - - if currentDepth == 0 { - program := strings.Join(append(lines, "\n"), " ") - lines = lines[:0] - s <- program - } - } - } +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() { - ln, err := net.Listen("tcp", *tcpAddr) + addr, err := net.ResolveTCPAddr("tcp", *tcpAddr) + if err != nil { + die(err.Error()) + } + ln, err := net.ListenTCP("tcp", addr) if err != nil { die(err.Error()) } for { - conn, err := ln.Accept() + conn, err := ln.AcceptTCP() if err != nil { printErrorMsg(err.Error()) continue @@ -114,28 +41,43 @@ func runTCPServer() { } } -func startConnection(conn net.Conn, m *cm.Manager) { - m.Add(conn) - defer m.Remove(conn) - - out, errors := make(chan interface{}), make(chan error) - userinput := make(chan string) - - go tcpInterpreter(conn, userinput, out, errors) +func newTcpInterpreter() *tcpInterpreter { + return &tcpInterpreter{ + tokens: make(chan token), + values: make(chan interface{}), + errors: make(chan error), + } +} - r := bufio.NewReader(conn) +func (t *tcpInterpreter) send() { for { - line, _, err := r.ReadLine() - switch err { - case nil: - break - case io.EOF: - io.WriteString(conn, "") - return - default: - printErrorMsg(err.Error()) - return + 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) + } } - userinput <- string(line) } } + +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) +}