refactor expr to remove complexity in individual types

This commit is contained in:
Brian Picciano 2016-07-24 14:52:15 -06:00
parent b988e22416
commit 9f53060b0c
2 changed files with 155 additions and 161 deletions

View File

@ -9,126 +9,107 @@ import (
"github.com/mediocregopher/ginger/lexer" "github.com/mediocregopher/ginger/lexer"
) )
// TODO doc strings
// TODO empty blocks // TODO empty blocks
// TODO empty parenthesis // TODO empty parenthesis
type tok lexer.Token // Actual represents the actual expression in question, and has certain
// properties. It is wrapped by Expr which also holds onto contextual
func (t tok) Token() lexer.Token { // information, like the token to which Actual was originally parsed from
return lexer.Token(t) type Actual interface {
// Equal should return true if the type and value of the other expression
// are equal.
Equal(Actual) bool
} }
type Expr interface { // Expr contains the actual expression as well as some contextual information
Token() lexer.Token // wrapping it. Most interactions will be with this and not with the Actual
String() string // directly.
type Expr struct {
Actual Actual
// Equal should return true if the type and value of the other expression // Token is a nice-to-have, nothing will break if it's not there
// are equal. The tokens shouldn't be taken into account Token lexer.Token
Equal(Expr) bool
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type Bool struct { // Bool represents a true or false value
tok type Bool bool
val bool
}
func (b Bool) String() string { // Equal implements the Actual method
return fmt.Sprint(b.val) func (b Bool) Equal(e Actual) bool {
}
func (b Bool) Equal(e Expr) bool {
bb, ok := e.(Bool) bb, ok := e.(Bool)
if !ok { if !ok {
return false return false
} }
return bb.val == b.val return bb == b
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type Int struct { // Int represents an integer value
tok type Int int64
val int64
}
func (i Int) String() string { // Equal implements the Actual method
return fmt.Sprint(i.val) func (i Int) Equal(e Actual) bool {
}
func (i Int) Equal(e Expr) bool {
ii, ok := e.(Int) ii, ok := e.(Int)
if !ok { if !ok {
return false return false
} }
return ii.val == i.val return ii == i
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type String struct { // String represents a string value
tok type String string
str string
}
func (s String) String() string { // Equal implements the Actual method
return strconv.QuoteToASCII(s.str) func (s String) Equal(e Actual) bool {
}
func (s String) Equal(e Expr) bool {
ss, ok := e.(String) ss, ok := e.(String)
if !ok { if !ok {
return false return false
} }
return ss.str == s.str return ss == s
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type Identifier struct { // Identifier represents a binding to some other value which has been given a
tok // name
ident string type Identifier string
}
func (id Identifier) String() string { // Equal implements the Actual method
return id.ident func (id Identifier) Equal(e Actual) bool {
}
func (id Identifier) Equal(e Expr) bool {
idid, ok := e.(Identifier) idid, ok := e.(Identifier)
if !ok { if !ok {
return false return false
} }
return idid.ident == id.ident return idid == id
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type Tuple struct { // Tuple represents a fixed set of expressions which are interacted with as if
exprs []Expr // they were a single value
} type Tuple []Expr
func (tup Tuple) Token() lexer.Token {
return tup.exprs[0].Token()
}
func (tup Tuple) String() string { func (tup Tuple) String() string {
strs := make([]string, len(tup.exprs)) strs := make([]string, len(tup))
for i := range tup.exprs { for i := range tup {
strs[i] = tup.exprs[i].String() strs[i] = fmt.Sprint(tup[i].Actual)
} }
return "(" + strings.Join(strs, ", ") + ")" return "(" + strings.Join(strs, ", ") + ")"
} }
func (tup Tuple) Equal(e Expr) bool { // Equal implements the Actual method
func (tup Tuple) Equal(e Actual) bool {
tuptup, ok := e.(Tuple) tuptup, ok := e.(Tuple)
if !ok || len(tuptup.exprs) != len(tup.exprs) { if !ok || len(tuptup) != len(tup) {
return false return false
} }
for i := range tup.exprs { for i := range tup {
if !tup.exprs[i].Equal(tuptup.exprs[i]) { if !tup[i].Actual.Equal(tuptup[i].Actual) {
return false return false
} }
} }
@ -137,29 +118,27 @@ func (tup Tuple) Equal(e Expr) bool {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type Pipe struct { // Pipe represents a set of expressions which operate on values and return new
exprs []Expr // values. The inputs of one expression in the pipe is the output of the
} // previous expression
type Pipe []Expr
func (p Pipe) Token() lexer.Token {
return p.exprs[0].Token()
}
func (p Pipe) String() string { func (p Pipe) String() string {
strs := make([]string, len(p.exprs)) strs := make([]string, len(p))
for i := range p.exprs { for i := range p {
strs[i] = p.exprs[i].String() strs[i] = fmt.Sprint(p[i].Actual)
} }
return "(" + strings.Join(strs, "|") + ")" return "(" + strings.Join(strs, "|") + ")"
} }
func (p Pipe) Equal(e Expr) bool { // Equal implements the Actual method
func (p Pipe) Equal(e Actual) bool {
pp, ok := e.(Pipe) pp, ok := e.(Pipe)
if !ok || len(pp.exprs) != len(p.exprs) { if !ok || len(pp) != len(p) {
return false return false
} }
for i := range p.exprs { for i := range p {
if !p.exprs[i].Equal(pp.exprs[i]) { if !p[i].Actual.Equal(pp[i].Actual) {
return false return false
} }
} }
@ -168,49 +147,47 @@ func (p Pipe) Equal(e Expr) bool {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Statement represents an actual action which will be taken. The input value is
// used as the input to the pipe, and the output of the pipe is the output of
// the statement
type Statement struct { type Statement struct {
in Expr in Expr
pipe Pipe pipe Pipe
} }
func (s Statement) Token() lexer.Token {
return s.in.Token()
}
func (s Statement) String() string { func (s Statement) String() string {
return fmt.Sprintf("(%s > %s)", s.in.String(), s.pipe.String()) return fmt.Sprintf("(%v > %s)", s.in.Actual, s.pipe.String())
} }
func (s Statement) Equal(e Expr) bool { // Equal implements the Actual method
func (s Statement) Equal(e Actual) bool {
ss, ok := e.(Statement) ss, ok := e.(Statement)
return ok && s.in.Equal(ss.in) && s.pipe.Equal(ss.pipe) return ok && s.in.Actual.Equal(ss.in.Actual) && s.pipe.Equal(ss.pipe)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type Block struct { // Block represents a set of statements which share a scope, i.e. If one
stmts []Statement // statement binds a variable the rest of the statements in the block can use
} // that variable, including sub-blocks within this one.
type Block []Statement
func (b Block) Token() lexer.Token {
return b.stmts[0].Token()
}
func (b Block) String() string { func (b Block) String() string {
strs := make([]string, len(b.stmts)) strs := make([]string, len(b))
for i := range b.stmts { for i := range b {
strs[i] = b.stmts[i].String() strs[i] = b[i].String()
} }
return fmt.Sprintf("{ %s }", strings.Join(strs, " ")) return fmt.Sprintf("{ %s }", strings.Join(strs, " "))
} }
func (b Block) Equal(e Expr) bool { // Equal implements the Actual method
func (b Block) Equal(e Actual) bool {
bb, ok := e.(Block) bb, ok := e.(Block)
if !ok { if !ok {
return false return false
} }
for i := range b.stmts { for i := range b {
if !b.stmts[i].Equal(bb.stmts[i]) { if !b[i].Equal(bb[i]) {
return false return false
} }
} }
@ -300,8 +277,9 @@ func Parse(r io.Reader) ([]Expr, error) {
// ParseAsBlock reads the given io.Reader as if it was implicitly surrounded by // ParseAsBlock reads the given io.Reader as if it was implicitly surrounded by
// curly braces, making it into a Block. This means all expressions from the // curly braces, making it into a Block. This means all expressions from the
// io.Reader *must* be statements // io.Reader *must* be statements. The returned Expr's Actual will always be a
func ParseAsBlock(r io.Reader) (Block, error) { // Block.
func ParseAsBlock(r io.Reader) (Expr, error) {
return parseBlock(readAllToks(r)) return parseBlock(readAllToks(r))
} }
@ -329,7 +307,7 @@ var (
func parse(toks []lexer.Token) (Expr, []lexer.Token, error) { func parse(toks []lexer.Token) (Expr, []lexer.Token, error) {
expr, toks, err := parseSingle(toks) expr, toks, err := parseSingle(toks)
if err != nil { if err != nil {
return nil, nil, err return Expr{}, nil, err
} }
if len(toks) > 0 && toks[0].TokenType == lexer.Punctuation { if len(toks) > 0 && toks[0].TokenType == lexer.Punctuation {
@ -344,7 +322,7 @@ func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) {
var err error var err error
if toks[0].Err() != nil { if toks[0].Err() != nil {
return nil, nil, exprErr{ return Expr{}, nil, exprErr{
reason: "could not parse token", reason: "could not parse token",
tok: toks[0], tok: toks[0],
} }
@ -355,13 +333,13 @@ func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) {
var ptoks []lexer.Token var ptoks []lexer.Token
ptoks, toks, err = sliceEnclosedToks(toks, openParen, closeParen) ptoks, toks, err = sliceEnclosedToks(toks, openParen, closeParen)
if err != nil { if err != nil {
return nil, nil, err return Expr{}, nil, err
} }
if expr, ptoks, err = parse(ptoks); err != nil { if expr, ptoks, err = parse(ptoks); err != nil {
return nil, nil, err return Expr{}, nil, err
} else if len(ptoks) > 0 { } else if len(ptoks) > 0 {
return nil, nil, exprErr{ return Expr{}, nil, exprErr{
reason: "multiple expressions inside parenthesis", reason: "multiple expressions inside parenthesis",
tok: starter, tok: starter,
tokCtx: "starting at", tokCtx: "starting at",
@ -373,17 +351,17 @@ func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) {
var btoks []lexer.Token var btoks []lexer.Token
btoks, toks, err = sliceEnclosedToks(toks, openCurly, closeCurly) btoks, toks, err = sliceEnclosedToks(toks, openCurly, closeCurly)
if err != nil { if err != nil {
return nil, nil, err return Expr{}, nil, err
} }
if expr, err = parseBlock(btoks); err != nil { if expr, err = parseBlock(btoks); err != nil {
return nil, nil, err return Expr{}, nil, err
} }
return expr, toks, nil return expr, toks, nil
} }
if expr, err = parseNonPunct(toks[0]); err != nil { if expr, err = parseNonPunct(toks[0]); err != nil {
return nil, nil, err return Expr{}, nil, err
} }
return expr, toks[1:], nil return expr, toks[1:], nil
} }
@ -395,42 +373,46 @@ func parseNonPunct(tok lexer.Token) (Expr, error) {
return parseString(tok) return parseString(tok)
} }
return nil, exprErr{ return Expr{}, exprErr{
reason: "unexpected non-punctuation token", reason: "unexpected non-punctuation token",
tok: tok, tok: tok,
} }
} }
func parseIdentifier(t lexer.Token) (Expr, error) { func parseIdentifier(t lexer.Token) (Expr, error) {
e := Expr{Token: t}
if t.Val[0] == '-' || (t.Val[0] >= '0' && t.Val[0] <= '9') { if t.Val[0] == '-' || (t.Val[0] >= '0' && t.Val[0] <= '9') {
n, err := strconv.ParseInt(t.Val, 10, 64) n, err := strconv.ParseInt(t.Val, 10, 64)
if err != nil { if err != nil {
return nil, exprErr{ return Expr{}, exprErr{
err: err, err: err,
tok: t, tok: t,
} }
} }
return Int{tok: tok(t), val: n}, nil e.Actual = Int(n)
}
} else if t.Val == "true" {
e.Actual = Bool(true)
if t.Val == "true" {
return Bool{tok: tok(t), val: true}, nil
} else if t.Val == "false" { } else if t.Val == "false" {
return Bool{tok: tok(t), val: false}, nil e.Actual = Bool(false)
} else {
e.Actual = Identifier(t.Val)
} }
return Identifier{tok: tok(t), ident: t.Val}, nil return e, nil
} }
func parseString(t lexer.Token) (Expr, error) { func parseString(t lexer.Token) (Expr, error) {
str, err := strconv.Unquote(t.Val) str, err := strconv.Unquote(t.Val)
if err != nil { if err != nil {
return nil, exprErr{ return Expr{}, exprErr{
err: err, err: err,
tok: t, tok: t,
} }
} }
return String{tok: tok(t), str: str}, nil return Expr{Token: t, Actual: String(str)}, nil
} }
func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) { func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
@ -443,92 +425,104 @@ func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, e
} else if toks[0].Equal(arrow) { } else if toks[0].Equal(arrow) {
expr, toks, err := parse(toks[1:]) expr, toks, err := parse(toks[1:])
if err != nil { if err != nil {
return nil, nil, err return Expr{}, nil, err
} }
pipe, ok := expr.(Pipe) pipe, ok := expr.Actual.(Pipe)
if !ok { if !ok {
pipe = Pipe{exprs: []Expr{expr}} pipe = Pipe{expr}
} }
return Statement{in: root, pipe: pipe}, toks, nil return Expr{Token: root.Token, Actual: Statement{in: root, pipe: pipe}}, toks, nil
} }
return root, toks, nil return root, toks, nil
} }
func parseTuple(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) { func parseTuple(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
rootTup, ok := root.(Tuple) rootTup, ok := root.Actual.(Tuple)
if !ok { if !ok {
rootTup = Tuple{exprs: []Expr{root}} rootTup = Tuple{root}
}
// rootTup is modified throughout, be we need to make it into an Expr for
// every return, which is annoying. so make a function to do it on the fly
mkRoot := func() Expr {
return Expr{Token: rootTup[0].Token, Actual: rootTup}
} }
if len(toks) < 2 { if len(toks) < 2 {
return rootTup, toks, nil return mkRoot(), toks, nil
} else if !toks[0].Equal(comma) { } else if !toks[0].Equal(comma) {
if toks[0].TokenType == lexer.Punctuation { if toks[0].TokenType == lexer.Punctuation {
return parseConnectingPunct(toks, rootTup) return parseConnectingPunct(toks, mkRoot())
} }
return rootTup, toks, nil return mkRoot(), toks, nil
} }
var expr Expr var expr Expr
var err error var err error
if expr, toks, err = parseSingle(toks[1:]); err != nil { if expr, toks, err = parseSingle(toks[1:]); err != nil {
return nil, nil, err return Expr{}, nil, err
} }
rootTup.exprs = append(rootTup.exprs, expr) rootTup = append(rootTup, expr)
return parseTuple(toks, rootTup) return parseTuple(toks, mkRoot())
} }
func parsePipe(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) { func parsePipe(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
rootTup, ok := root.(Pipe) rootPipe, ok := root.Actual.(Pipe)
if !ok { if !ok {
rootTup = Pipe{exprs: []Expr{root}} rootPipe = Pipe{root}
}
// rootPipe is modified throughout, be we need to make it into an Expr for
// every return, which is annoying. so make a function to do it on the fly
mkRoot := func() Expr {
return Expr{Token: rootPipe[0].Token, Actual: rootPipe}
} }
if len(toks) < 2 { if len(toks) < 2 {
return rootTup, toks, nil return mkRoot(), toks, nil
} else if !toks[0].Equal(pipe) { } else if !toks[0].Equal(pipe) {
if toks[0].TokenType == lexer.Punctuation { if toks[0].TokenType == lexer.Punctuation {
return parseConnectingPunct(toks, rootTup) return parseConnectingPunct(toks, mkRoot())
} }
return rootTup, toks, nil return mkRoot(), toks, nil
} }
var expr Expr var expr Expr
var err error var err error
if expr, toks, err = parseSingle(toks[1:]); err != nil { if expr, toks, err = parseSingle(toks[1:]); err != nil {
return nil, nil, err return Expr{}, nil, err
} }
rootTup.exprs = append(rootTup.exprs, expr) rootPipe = append(rootPipe, expr)
return parsePipe(toks, rootTup) return parsePipe(toks, mkRoot())
} }
// parseBlock assumes that the given token list is the entire block, already // parseBlock assumes that the given token list is the entire block, already
// pulled from outer curly braces by sliceEnclosedToks, or determined to be the // pulled from outer curly braces by sliceEnclosedToks, or determined to be the
// entire block in some other way. // entire block in some other way.
func parseBlock(toks []lexer.Token) (Block, error) { func parseBlock(toks []lexer.Token) (Expr, error) {
b := Block{} b := Block{}
first := toks[0]
var expr Expr var expr Expr
var err error var err error
for { for {
if len(toks) == 0 { if len(toks) == 0 {
return b, nil return Expr{Token: first, Actual: b}, nil
} }
if expr, toks, err = parse(toks); err != nil { if expr, toks, err = parse(toks); err != nil {
return Block{}, err return Expr{}, err
} }
stmt, ok := expr.(Statement) stmt, ok := expr.Actual.(Statement)
if !ok { if !ok {
return Block{}, exprErr{ return Expr{}, exprErr{
reason: "blocks may only contain full statements", reason: "blocks may only contain full statements",
tok: expr.Token(), tok: expr.Token,
tokCtx: "non-statement here", tokCtx: "non-statement here",
} }
} }
b.stmts = append(b.stmts, stmt) b = append(b, stmt)
} }
} }

View File

@ -37,13 +37,13 @@ func TestSliceEnclosedToks(t *T) {
func assertParse(t *T, in []lexer.Token, expExpr Expr, expOut []lexer.Token) { func assertParse(t *T, in []lexer.Token, expExpr Expr, expOut []lexer.Token) {
expr, out, err := parse(in) expr, out, err := parse(in)
require.Nil(t, err) require.Nil(t, err)
assert.True(t, expExpr.Equal(expr), "expr:%v expExpr:%v", expr, expExpr) assert.True(t, expExpr.Actual.Equal(expr.Actual), "expr:%v expExpr:%v", expr, expExpr)
assert.Equal(t, expOut, out, "out:%v expOut:%v", out, expOut) assert.Equal(t, expOut, out, "out:%v expOut:%v", out, expOut)
} }
func TestParseSingle(t *T) { func TestParseSingle(t *T) {
foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"}
fooExpr := Identifier{tok: tok(foo), ident: "foo"} fooExpr := Expr{Actual: Identifier("foo")}
toks := []lexer.Token{foo} toks := []lexer.Token{foo}
assertParse(t, toks, fooExpr, []lexer.Token{}) assertParse(t, toks, fooExpr, []lexer.Token{})
@ -60,11 +60,11 @@ func TestParseSingle(t *T) {
func TestParseTuple(t *T) { func TestParseTuple(t *T) {
tup := func(ee ...Expr) Expr { tup := func(ee ...Expr) Expr {
return Tuple{exprs: ee} return Expr{Actual: Tuple(ee)}
} }
foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"}
fooExpr := Identifier{tok: tok(foo), ident: "foo"} fooExpr := Expr{Actual: Identifier("foo")}
toks := []lexer.Token{foo, comma, foo} toks := []lexer.Token{foo, comma, foo}
assertParse(t, toks, tup(fooExpr, fooExpr), []lexer.Token{}) assertParse(t, toks, tup(fooExpr, fooExpr), []lexer.Token{})
@ -88,11 +88,11 @@ func TestParseTuple(t *T) {
// This is basically the same as tuple // This is basically the same as tuple
func TestParsePipe(t *T) { func TestParsePipe(t *T) {
mkPipe := func(ee ...Expr) Expr { mkPipe := func(ee ...Expr) Expr {
return Pipe{exprs: ee} return Expr{Actual: Pipe(ee)}
} }
foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"}
fooExpr := Identifier{tok: tok(foo), ident: "foo"} fooExpr := Expr{Actual: Identifier("foo")}
toks := []lexer.Token{foo, pipe, foo} toks := []lexer.Token{foo, pipe, foo}
assertParse(t, toks, mkPipe(fooExpr, fooExpr), []lexer.Token{}) assertParse(t, toks, mkPipe(fooExpr, fooExpr), []lexer.Token{})
@ -112,18 +112,18 @@ func TestParsePipe(t *T) {
toks = []lexer.Token{foo, pipe, openParen, foo, pipe, foo, closeParen, pipe, foo, foo} 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}) assertParse(t, toks, mkPipe(fooExpr, mkPipe(fooExpr, fooExpr), fooExpr), []lexer.Token{foo})
fooTupExpr := Tuple{exprs: []Expr{fooExpr, fooExpr}} fooTupExpr := Expr{Actual: Tuple{fooExpr, fooExpr}}
toks = []lexer.Token{foo, comma, foo, pipe, foo} toks = []lexer.Token{foo, comma, foo, pipe, foo}
assertParse(t, toks, mkPipe(fooTupExpr, fooExpr), []lexer.Token{}) assertParse(t, toks, mkPipe(fooTupExpr, fooExpr), []lexer.Token{})
} }
func TestParseStatement(t *T) { func TestParseStatement(t *T) {
stmt := func(in Expr, ee ...Expr) Expr { stmt := func(in Expr, ee ...Expr) Expr {
return Statement{in: in, pipe: Pipe{exprs: ee}} return Expr{Actual: Statement{in: in, pipe: Pipe(ee)}}
} }
foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"}
fooExpr := Identifier{tok: tok(foo), ident: "foo"} fooExpr := Expr{Actual: Identifier("foo")}
toks := []lexer.Token{foo, arrow, foo} toks := []lexer.Token{foo, arrow, foo}
assertParse(t, toks, stmt(fooExpr, fooExpr), []lexer.Token{}) assertParse(t, toks, stmt(fooExpr, fooExpr), []lexer.Token{})
@ -149,7 +149,7 @@ func TestParseStatement(t *T) {
toks = []lexer.Token{openParen, foo, closeParen, arrow, openParen, foo, pipe, foo, closeParen, foo} toks = []lexer.Token{openParen, foo, closeParen, arrow, openParen, foo, pipe, foo, closeParen, foo}
assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{foo}) assertParse(t, toks, stmt(fooExpr, fooExpr, fooExpr), []lexer.Token{foo})
fooTupExpr := Tuple{exprs: []Expr{fooExpr, fooExpr}} fooTupExpr := Expr{Actual: Tuple{fooExpr, fooExpr}}
toks = []lexer.Token{foo, arrow, openParen, foo, comma, foo, closeParen, pipe, foo, foo} toks = []lexer.Token{foo, arrow, openParen, foo, comma, foo, closeParen, pipe, foo, foo}
assertParse(t, toks, stmt(fooExpr, fooTupExpr, fooExpr), []lexer.Token{foo}) assertParse(t, toks, stmt(fooExpr, fooTupExpr, fooExpr), []lexer.Token{foo})
@ -162,14 +162,14 @@ func TestParseStatement(t *T) {
func TestParseBlock(t *T) { func TestParseBlock(t *T) {
stmt := func(in Expr, ee ...Expr) Statement { stmt := func(in Expr, ee ...Expr) Statement {
return Statement{in: in, pipe: Pipe{exprs: ee}} return Statement{in: in, pipe: Pipe(ee)}
} }
block := func(stmts ...Statement) Block { block := func(stmts ...Statement) Expr {
return Block{stmts: stmts} return Expr{Actual: Block(stmts)}
} }
foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"} foo := lexer.Token{TokenType: lexer.Identifier, Val: "foo"}
fooExpr := Identifier{tok: tok(foo), ident: "foo"} fooExpr := Expr{Actual: Identifier("foo")}
toks := []lexer.Token{openCurly, foo, arrow, foo, closeCurly} toks := []lexer.Token{openCurly, foo, arrow, foo, closeCurly}
assertParse(t, toks, block(stmt(fooExpr, fooExpr)), []lexer.Token{}) assertParse(t, toks, block(stmt(fooExpr, fooExpr)), []lexer.Token{})