|
|
|
@ -7,7 +7,6 @@ import (
|
|
|
|
|
"github.com/jordanorelli/skeam/cm"
|
|
|
|
|
"io"
|
|
|
|
|
"net"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const MAX_SEXP_LINES = 40
|
|
|
|
@ -15,127 +14,70 @@ 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
|
|
|
|
|
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 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)
|
|
|
|
|
func runTCPServer() {
|
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", *tcpAddr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
die(err.Error())
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
for v := range out {
|
|
|
|
|
fmt.Fprintln(conn, v)
|
|
|
|
|
ln, err := net.ListenTCP("tcp", addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
die(err.Error())
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case err := <-errors:
|
|
|
|
|
fmt.Fprintf(conn, "error: %v\n", err)
|
|
|
|
|
case line := <-userinput:
|
|
|
|
|
if errorMode {
|
|
|
|
|
skipLine(line)
|
|
|
|
|
break
|
|
|
|
|
conn, err := ln.AcceptTCP()
|
|
|
|
|
if err != nil {
|
|
|
|
|
printErrorMsg(err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
lineDepth := depth(line)
|
|
|
|
|
currentDepth += lineDepth
|
|
|
|
|
|
|
|
|
|
if currentDepth < 0 {
|
|
|
|
|
lines = lines[:0]
|
|
|
|
|
currentDepth = 0
|
|
|
|
|
break
|
|
|
|
|
go startConnection(conn, manager)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(lines) == 0 && lineDepth == 0 {
|
|
|
|
|
s <- line + "\n"
|
|
|
|
|
break
|
|
|
|
|
func newTcpInterpreter() *tcpInterpreter {
|
|
|
|
|
return &tcpInterpreter{
|
|
|
|
|
tokens: make(chan token),
|
|
|
|
|
values: make(chan interface{}),
|
|
|
|
|
errors: make(chan error),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := addLine(line); err != nil {
|
|
|
|
|
errorMode = true
|
|
|
|
|
lines = lines[:0]
|
|
|
|
|
currentDepth = 0
|
|
|
|
|
break
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if currentDepth == 0 {
|
|
|
|
|
program := strings.Join(append(lines, "\n"), " ")
|
|
|
|
|
lines = lines[:0]
|
|
|
|
|
s <- program
|
|
|
|
|
case e := <-t.errors:
|
|
|
|
|
if _, err := fmt.Fprintln(t.ferr, e); err != nil {
|
|
|
|
|
fmt.Println("can't write error to client: ", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runTCPServer() {
|
|
|
|
|
ln, err := net.Listen("tcp", *tcpAddr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
die(err.Error())
|
|
|
|
|
}
|
|
|
|
|
for {
|
|
|
|
|
conn, err := ln.Accept()
|
|
|
|
|
if err != nil {
|
|
|
|
|
printErrorMsg(err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
go startConnection(conn, manager)
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
out, errors := make(chan interface{}), make(chan error)
|
|
|
|
|
userinput := make(chan string)
|
|
|
|
|
|
|
|
|
|
go tcpInterpreter(conn, userinput, out, errors)
|
|
|
|
|
|
|
|
|
|
r := bufio.NewReader(conn)
|
|
|
|
|
for {
|
|
|
|
|
line, _, err := r.ReadLine()
|
|
|
|
|
switch err {
|
|
|
|
|
case nil:
|
|
|
|
|
break
|
|
|
|
|
case io.EOF:
|
|
|
|
|
io.WriteString(conn, "<eof>")
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
printErrorMsg(err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
userinput <- string(line)
|
|
|
|
|
}
|
|
|
|
|
i := newTcpInterpreter()
|
|
|
|
|
i.Run(conn, conn, conn)
|
|
|
|
|
}
|
|
|
|
|