refactor expr to remove complexity in individual types
This commit is contained in:
parent
b988e22416
commit
9f53060b0c
288
expr/expr.go
288
expr/expr.go
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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{})
|
||||||
|
Loading…
Reference in New Issue
Block a user