diff --git a/types.go b/types.go index cfa02ea..d14c264 100644 --- a/types.go +++ b/types.go @@ -166,6 +166,57 @@ func (p Pipe) Equal(e Expr) bool { //////////////////////////////////////////////////////////////////////////////// +type Statement struct { + in Expr + pipe Pipe +} + +func (s Statement) Token() lexer.Token { + return s.in.Token() +} + +func (s Statement) String() string { + return fmt.Sprintf("(%s > %s)", s.in.String(), s.pipe.String()) +} + +func (s Statement) Equal(e Expr) bool { + ss, ok := e.(Statement) + return ok && s.in.Equal(ss.in) && s.pipe.Equal(ss.pipe) +} + +//////////////////////////////////////////////////////////////////////////////// + +type Block struct { + stmts []Statement +} + +func (b Block) Token() lexer.Token { + return b.stmts[0].Token() +} + +func (b Block) String() string { + strs := make([]string, len(b.stmts)) + for i := range b.stmts { + strs[i] = b.stmts[i].String() + } + return fmt.Sprintf("{ %s }", strings.Join(strs, " ")) +} + +func (b Block) Equal(e Expr) bool { + bb, ok := e.(Block) + if !ok { + return false + } + for i := range b.stmts { + if !b.stmts[i].Equal(bb.stmts[i]) { + return false + } + } + return true +} + +//////////////////////////////////////////////////////////////////////////////// + // toks[0] must be start func sliceEnclosedToks(toks []lexer.Token, start, end lexer.Token) ([]lexer.Token, []lexer.Token, error) { c := 1 @@ -203,8 +254,11 @@ func readAllToks(r io.Reader) []lexer.Token { var ( openParen = lexer.Token{TokenType: lexer.Punctuation, Val: "("} closeParen = lexer.Token{TokenType: lexer.Punctuation, Val: ")"} + openCurly = lexer.Token{TokenType: lexer.Punctuation, Val: "{"} + closeCurly = lexer.Token{TokenType: lexer.Punctuation, Val: "}"} comma = lexer.Token{TokenType: lexer.Punctuation, Val: ","} pipe = lexer.Token{TokenType: lexer.Punctuation, Val: "|"} + arrow = lexer.Token{TokenType: lexer.Punctuation, Val: ">"} ) func parse(toks []lexer.Token) (Expr, []lexer.Token, error) { @@ -242,6 +296,18 @@ func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) { return nil, nil, fmt.Errorf("multiple expressions inside parenthesis; %v", starter) } return expr, toks, nil + + } else if toks[0].Equal(openCurly) { + var btoks []lexer.Token + btoks, toks, err = sliceEnclosedToks(toks, openCurly, closeCurly) + if err != nil { + return nil, nil, err + } + + if expr, err = parseBlock(btoks); err != nil { + return nil, nil, err + } + return expr, toks, nil } if expr, err = parseNonPunct(toks[0]); err != nil { @@ -283,11 +349,23 @@ func parseString(t lexer.Token) (Expr, error) { func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) { if toks[0].Equal(comma) { return parseTuple(toks, root) + } else if toks[0].Equal(pipe) { return parsePipe(toks, root) + + } else if toks[0].Equal(arrow) { + expr, toks, err := parse(toks[1:]) + if err != nil { + return nil, nil, err + } + pipe, ok := expr.(Pipe) + if !ok { + pipe = Pipe{exprs: []Expr{expr}} + } + return Statement{in: root, pipe: pipe}, toks, nil } - return nil, nil, fmt.Errorf("invalid connecting punctuation: %v", toks[0]) + return root, toks, nil } func parseTuple(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) { @@ -333,3 +411,29 @@ func parsePipe(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) { rootTup.exprs = append(rootTup.exprs, expr) return parsePipe(toks, rootTup) } + +// parseBlock assumes that the given token list is the entire block, already +// pulled from outer curly braces by sliceEnclosedToks, or determined to be the +// entire block in some other way. +func parseBlock(toks []lexer.Token) (Expr, error) { + b := Block{} + + // TODO figure out what we want to do about empty blocks + + var expr Expr + var err error + for { + if len(toks) == 0 { + return b, nil + } + + if expr, toks, err = parse(toks); err != nil { + return nil, err + } + stmt, ok := expr.(Statement) + if !ok { + return nil, fmt.Errorf("blocks may only contain full statements: %v", expr) + } + b.stmts = append(b.stmts, stmt) + } +} diff --git a/types_test.go b/types_test.go index 14997a0..02cdf6d 100644 --- a/types_test.go +++ b/types_test.go @@ -112,3 +112,64 @@ func TestParsePipe(t *T) { toks = []lexer.Token{foo, pipe, openParen, foo, pipe, foo, closeParen, pipe, foo, foo} assertParse(t, toks, mkPipe(fooExpr, mkPipe(fooExpr, fooExpr), fooExpr), []lexer.Token{foo}) } + +func TestParseStatement(t *T) { + stmt := func(in Expr, ee ...Expr) Expr { + return Statement{in: in, pipe: Pipe{exprs: ee}} + } + + foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} + fooExpr := Identifier{tok: tok(foo), ident: "foo"} + + toks := []lexer.Token{foo, arrow, foo} + assertParse(t, toks, stmt(fooExpr, fooExpr), []lexer.Token{}) + + toks = []lexer.Token{openParen, foo, arrow, foo, closeParen} + assertParse(t, toks, stmt(fooExpr, fooExpr), []lexer.Token{}) + + toks = []lexer.Token{foo, arrow, openParen, foo, closeParen} + assertParse(t, toks, stmt(fooExpr, fooExpr), []lexer.Token{}) + + toks = []lexer.Token{foo, arrow, foo, pipe, foo} + assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{}) + + toks = []lexer.Token{foo, arrow, foo, pipe, foo, foo} + assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{foo}) + + toks = []lexer.Token{foo, arrow, openParen, foo, pipe, foo, closeParen, foo} + assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{foo}) + + toks = []lexer.Token{openParen, foo, closeParen, arrow, openParen, foo, pipe, foo, closeParen, foo} + assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{foo}) + + toks = []lexer.Token{openParen, foo, closeParen, arrow, openParen, foo, pipe, foo, closeParen, foo} + assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{foo}) + + fooTupExpr := Tuple{exprs: []Expr{fooExpr, fooExpr}} + toks = []lexer.Token{foo, arrow, openParen, foo, comma, foo, closeParen, pipe, foo, foo} + assertParse(t, toks, stmt(fooExpr, fooTupExpr, fooExpr), []lexer.Token{foo}) +} + +func TestParseBlock(t *T) { + stmt := func(in Expr, ee ...Expr) Statement { + return Statement{in: in, pipe: Pipe{exprs: ee}} + } + block := func(stmts ...Statement) Block { + return Block{stmts: stmts} + } + + foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} + fooExpr := Identifier{tok: tok(foo), ident: "foo"} + + toks := []lexer.Token{openCurly, foo, arrow, foo, closeCurly} + assertParse(t, toks, block(stmt(fooExpr, fooExpr)), []lexer.Token{}) + + toks = []lexer.Token{openCurly, foo, arrow, foo, closeCurly, foo} + assertParse(t, toks, block(stmt(fooExpr, fooExpr)), []lexer.Token{foo}) + + toks = []lexer.Token{openCurly, foo, arrow, foo, openParen, foo, arrow, foo, closeParen, closeCurly, foo} + assertParse(t, toks, block(stmt(fooExpr, fooExpr), stmt(fooExpr, fooExpr)), []lexer.Token{foo}) + + toks = []lexer.Token{openCurly, foo, arrow, foo, openParen, foo, arrow, foo, closeParen, closeCurly, foo} + assertParse(t, toks, block(stmt(fooExpr, fooExpr), stmt(fooExpr, fooExpr)), []lexer.Token{foo}) +}