2016-07-23 17:06:26 +00:00
|
|
|
package expr
|
2016-07-06 02:03:04 +00:00
|
|
|
|
2016-07-22 20:38:20 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2016-07-06 02:03:04 +00:00
|
|
|
|
2016-07-22 20:38:20 +00:00
|
|
|
"github.com/mediocregopher/ginger/lexer"
|
|
|
|
)
|
2016-07-06 02:03:04 +00:00
|
|
|
|
2016-07-23 16:59:11 +00:00
|
|
|
// TODO empty blocks
|
|
|
|
// TODO empty parenthesis
|
2016-07-06 02:03:04 +00:00
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Actual represents the actual expression in question, and has certain
|
|
|
|
// properties. It is wrapped by Expr which also holds onto contextual
|
|
|
|
// information, like the token to which Actual was originally parsed from
|
|
|
|
type Actual interface {
|
|
|
|
// Equal should return true if the type and value of the other expression
|
|
|
|
// are equal.
|
|
|
|
Equal(Actual) bool
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Expr contains the actual expression as well as some contextual information
|
|
|
|
// wrapping it. Most interactions will be with this and not with the Actual
|
|
|
|
// directly.
|
|
|
|
type Expr struct {
|
|
|
|
Actual Actual
|
2016-07-22 20:38:20 +00:00
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Token is a nice-to-have, nothing will break if it's not there
|
|
|
|
Token lexer.Token
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Bool represents a true or false value
|
|
|
|
type Bool bool
|
2016-07-22 20:38:20 +00:00
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (b Bool) Equal(e Actual) bool {
|
2016-07-22 20:38:20 +00:00
|
|
|
bb, ok := e.(Bool)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return bb == b
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Int represents an integer value
|
|
|
|
type Int int64
|
2016-07-22 20:38:20 +00:00
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (i Int) Equal(e Actual) bool {
|
2016-07-22 20:38:20 +00:00
|
|
|
ii, ok := e.(Int)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return ii == i
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// String represents a string value
|
|
|
|
type String string
|
2016-07-22 20:38:20 +00:00
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (s String) Equal(e Actual) bool {
|
2016-07-22 20:38:20 +00:00
|
|
|
ss, ok := e.(String)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return ss == s
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Identifier represents a binding to some other value which has been given a
|
|
|
|
// name
|
|
|
|
type Identifier string
|
2016-07-22 20:38:20 +00:00
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (id Identifier) Equal(e Actual) bool {
|
2016-07-22 20:38:20 +00:00
|
|
|
idid, ok := e.(Identifier)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return idid == id
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 21:04:06 +00:00
|
|
|
// Macro is an identifier for a macro which can be used to transform
|
|
|
|
// expressions. The tokens for macros start with a '%', but the Macro identifier
|
|
|
|
// itself has that stripped off
|
|
|
|
type Macro string
|
|
|
|
|
|
|
|
// String returns the Macro with a '%' prepended to it
|
|
|
|
func (m Macro) String() string {
|
|
|
|
return "%" + string(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Equal implements the Actual method
|
|
|
|
func (m Macro) Equal(e Actual) bool {
|
|
|
|
mm, ok := e.(Macro)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return m == mm
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Tuple represents a fixed set of expressions which are interacted with as if
|
|
|
|
// they were a single value
|
|
|
|
type Tuple []Expr
|
2016-07-22 20:38:20 +00:00
|
|
|
|
|
|
|
func (tup Tuple) String() string {
|
2016-07-24 20:52:15 +00:00
|
|
|
strs := make([]string, len(tup))
|
|
|
|
for i := range tup {
|
|
|
|
strs[i] = fmt.Sprint(tup[i].Actual)
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
return "(" + strings.Join(strs, ", ") + ")"
|
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (tup Tuple) Equal(e Actual) bool {
|
2016-07-22 20:38:20 +00:00
|
|
|
tuptup, ok := e.(Tuple)
|
2016-07-24 20:52:15 +00:00
|
|
|
if !ok || len(tuptup) != len(tup) {
|
2016-07-22 20:38:20 +00:00
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
for i := range tup {
|
|
|
|
if !tup[i].Actual.Equal(tuptup[i].Actual) {
|
2016-07-22 20:38:20 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Pipe represents a set of expressions which operate on values and return new
|
|
|
|
// values. The inputs of one expression in the pipe is the output of the
|
|
|
|
// previous expression
|
|
|
|
type Pipe []Expr
|
2016-07-22 20:53:04 +00:00
|
|
|
|
|
|
|
func (p Pipe) String() string {
|
2016-07-24 20:52:15 +00:00
|
|
|
strs := make([]string, len(p))
|
|
|
|
for i := range p {
|
|
|
|
strs[i] = fmt.Sprint(p[i].Actual)
|
2016-07-22 20:53:04 +00:00
|
|
|
}
|
|
|
|
return "(" + strings.Join(strs, "|") + ")"
|
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (p Pipe) Equal(e Actual) bool {
|
2016-07-22 20:53:04 +00:00
|
|
|
pp, ok := e.(Pipe)
|
2016-07-24 20:52:15 +00:00
|
|
|
if !ok || len(pp) != len(p) {
|
2016-07-22 20:53:04 +00:00
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
for i := range p {
|
|
|
|
if !p[i].Actual.Equal(pp[i].Actual) {
|
2016-07-22 20:53:04 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// 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
|
2016-07-22 21:53:27 +00:00
|
|
|
type Statement struct {
|
|
|
|
in Expr
|
|
|
|
pipe Pipe
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s Statement) String() string {
|
2016-07-24 20:52:15 +00:00
|
|
|
return fmt.Sprintf("(%v > %s)", s.in.Actual, s.pipe.String())
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (s Statement) Equal(e Actual) bool {
|
2016-07-22 21:53:27 +00:00
|
|
|
ss, ok := e.(Statement)
|
2016-07-24 20:52:15 +00:00
|
|
|
return ok && s.in.Actual.Equal(ss.in.Actual) && s.pipe.Equal(ss.pipe)
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Block represents a set of statements which share a scope, i.e. If one
|
|
|
|
// 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
|
2016-07-22 21:53:27 +00:00
|
|
|
|
|
|
|
func (b Block) String() string {
|
2016-07-24 20:52:15 +00:00
|
|
|
strs := make([]string, len(b))
|
|
|
|
for i := range b {
|
|
|
|
strs[i] = b[i].String()
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
return fmt.Sprintf("{ %s }", strings.Join(strs, " "))
|
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
// Equal implements the Actual method
|
|
|
|
func (b Block) Equal(e Actual) bool {
|
2016-07-22 21:53:27 +00:00
|
|
|
bb, ok := e.(Block)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
for i := range b {
|
|
|
|
if !b[i].Equal(bb[i]) {
|
2016-07-22 21:53:27 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-23 16:59:11 +00:00
|
|
|
type exprErr struct {
|
|
|
|
reason string
|
2016-07-24 02:26:26 +00:00
|
|
|
err error
|
2016-07-23 16:59:11 +00:00
|
|
|
tok lexer.Token
|
|
|
|
tokCtx string // e.g. "block starting at" or "open paren at"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e exprErr) Error() string {
|
2016-07-24 02:26:26 +00:00
|
|
|
var msg string
|
|
|
|
if e.err != nil {
|
|
|
|
msg = e.err.Error()
|
|
|
|
} else {
|
|
|
|
msg = e.reason
|
|
|
|
}
|
2016-07-23 16:59:11 +00:00
|
|
|
if err := e.tok.Err(); err != nil {
|
|
|
|
msg += " - token error: " + err.Error()
|
|
|
|
} else if (e.tok != lexer.Token{}) {
|
|
|
|
msg += " - "
|
|
|
|
if e.tokCtx != "" {
|
|
|
|
msg += e.tokCtx + ": "
|
|
|
|
}
|
|
|
|
msg = fmt.Sprintf("%s [line:%d col:%d]", msg, e.tok.Row, e.tok.Col)
|
|
|
|
}
|
|
|
|
return msg
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-07-22 20:38:20 +00:00
|
|
|
// toks[0] must be start
|
|
|
|
func sliceEnclosedToks(toks []lexer.Token, start, end lexer.Token) ([]lexer.Token, []lexer.Token, error) {
|
|
|
|
c := 1
|
|
|
|
ret := []lexer.Token{}
|
2016-07-23 16:59:11 +00:00
|
|
|
first := toks[0]
|
2016-07-22 20:38:20 +00:00
|
|
|
for i, tok := range toks[1:] {
|
2016-07-23 16:59:11 +00:00
|
|
|
if tok.Err() != nil {
|
|
|
|
return nil, nil, exprErr{
|
|
|
|
reason: fmt.Sprintf("missing closing %v", end),
|
|
|
|
tok: tok,
|
|
|
|
}
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if tok.Equal(start) {
|
|
|
|
c++
|
|
|
|
} else if tok.Equal(end) {
|
|
|
|
c--
|
|
|
|
}
|
|
|
|
if c == 0 {
|
|
|
|
return ret, toks[2+i:], nil
|
|
|
|
}
|
|
|
|
ret = append(ret, tok)
|
|
|
|
}
|
|
|
|
|
2016-07-23 16:59:11 +00:00
|
|
|
return nil, nil, exprErr{
|
|
|
|
reason: fmt.Sprintf("missing closing %v", end),
|
|
|
|
tok: first,
|
|
|
|
tokCtx: "starting at",
|
|
|
|
}
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 18:53:15 +00:00
|
|
|
// Parse reads in all expressions it can from the given io.Reader and returns
|
|
|
|
// them
|
2016-07-23 17:06:26 +00:00
|
|
|
func Parse(r io.Reader) ([]Expr, error) {
|
|
|
|
toks := readAllToks(r)
|
|
|
|
var ret []Expr
|
|
|
|
var expr Expr
|
|
|
|
var err error
|
|
|
|
for len(toks) > 0 {
|
|
|
|
if toks[0].TokenType == lexer.EOF {
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
expr, toks, err = parse(toks)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ret = append(ret, expr)
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
2016-07-24 18:53:15 +00:00
|
|
|
// 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
|
2016-07-24 20:52:15 +00:00
|
|
|
// io.Reader *must* be statements. The returned Expr's Actual will always be a
|
|
|
|
// Block.
|
|
|
|
func ParseAsBlock(r io.Reader) (Expr, error) {
|
2016-07-24 18:53:15 +00:00
|
|
|
return parseBlock(readAllToks(r))
|
|
|
|
}
|
|
|
|
|
2016-07-22 20:38:20 +00:00
|
|
|
func readAllToks(r io.Reader) []lexer.Token {
|
|
|
|
l := lexer.New(r)
|
|
|
|
var toks []lexer.Token
|
|
|
|
for l.HasNext() {
|
|
|
|
toks = append(toks, l.Next())
|
|
|
|
}
|
|
|
|
return toks
|
|
|
|
}
|
|
|
|
|
|
|
|
// For all parse methods it is assumed that toks is not empty
|
|
|
|
|
|
|
|
var (
|
2016-07-24 02:37:10 +00:00
|
|
|
openParen = lexer.Token{TokenType: lexer.Wrapper, Val: "("}
|
|
|
|
closeParen = lexer.Token{TokenType: lexer.Wrapper, Val: ")"}
|
|
|
|
openCurly = lexer.Token{TokenType: lexer.Wrapper, Val: "{"}
|
|
|
|
closeCurly = lexer.Token{TokenType: lexer.Wrapper, Val: "}"}
|
2016-07-22 20:38:20 +00:00
|
|
|
comma = lexer.Token{TokenType: lexer.Punctuation, Val: ","}
|
2016-07-22 20:53:04 +00:00
|
|
|
pipe = lexer.Token{TokenType: lexer.Punctuation, Val: "|"}
|
2016-07-22 21:53:27 +00:00
|
|
|
arrow = lexer.Token{TokenType: lexer.Punctuation, Val: ">"}
|
2016-07-22 20:38:20 +00:00
|
|
|
)
|
2016-07-06 02:03:04 +00:00
|
|
|
|
2016-07-22 20:38:20 +00:00
|
|
|
func parse(toks []lexer.Token) (Expr, []lexer.Token, error) {
|
|
|
|
expr, toks, err := parseSingle(toks)
|
|
|
|
if err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(toks) > 0 && toks[0].TokenType == lexer.Punctuation {
|
|
|
|
return parseConnectingPunct(toks, expr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return expr, toks, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) {
|
|
|
|
var expr Expr
|
|
|
|
var err error
|
2016-07-06 02:03:04 +00:00
|
|
|
|
2016-07-23 16:59:11 +00:00
|
|
|
if toks[0].Err() != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, exprErr{
|
2016-07-23 16:59:11 +00:00
|
|
|
reason: "could not parse token",
|
|
|
|
tok: toks[0],
|
|
|
|
}
|
2016-07-06 02:03:04 +00:00
|
|
|
}
|
2016-07-22 20:38:20 +00:00
|
|
|
|
|
|
|
if toks[0].Equal(openParen) {
|
|
|
|
starter := toks[0]
|
|
|
|
var ptoks []lexer.Token
|
|
|
|
ptoks, toks, err = sliceEnclosedToks(toks, openParen, closeParen)
|
|
|
|
if err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if expr, ptoks, err = parse(ptoks); err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 20:38:20 +00:00
|
|
|
} else if len(ptoks) > 0 {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, exprErr{
|
2016-07-23 16:59:11 +00:00
|
|
|
reason: "multiple expressions inside parenthesis",
|
|
|
|
tok: starter,
|
|
|
|
tokCtx: "starting at",
|
|
|
|
}
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
return expr, toks, nil
|
2016-07-22 21:53:27 +00:00
|
|
|
|
|
|
|
} else if toks[0].Equal(openCurly) {
|
|
|
|
var btoks []lexer.Token
|
|
|
|
btoks, toks, err = sliceEnclosedToks(toks, openCurly, closeCurly)
|
|
|
|
if err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if expr, err = parseBlock(btoks); err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
return expr, toks, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if expr, err = parseNonPunct(toks[0]); err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
return expr, toks[1:], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseNonPunct(tok lexer.Token) (Expr, error) {
|
|
|
|
if tok.TokenType == lexer.Identifier {
|
|
|
|
return parseIdentifier(tok)
|
|
|
|
} else if tok.TokenType == lexer.String {
|
|
|
|
return parseString(tok)
|
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, exprErr{
|
2016-07-23 16:59:11 +00:00
|
|
|
reason: "unexpected non-punctuation token",
|
|
|
|
tok: tok,
|
|
|
|
}
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseIdentifier(t lexer.Token) (Expr, error) {
|
2016-07-24 20:52:15 +00:00
|
|
|
e := Expr{Token: t}
|
2016-07-22 20:38:20 +00:00
|
|
|
if t.Val[0] == '-' || (t.Val[0] >= '0' && t.Val[0] <= '9') {
|
|
|
|
n, err := strconv.ParseInt(t.Val, 10, 64)
|
2016-07-23 16:59:11 +00:00
|
|
|
if err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, exprErr{
|
2016-07-24 02:26:26 +00:00
|
|
|
err: err,
|
2016-07-23 16:59:11 +00:00
|
|
|
tok: t,
|
|
|
|
}
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
e.Actual = Int(n)
|
|
|
|
|
2016-07-24 21:04:06 +00:00
|
|
|
} else if t.Val == "%true" {
|
2016-07-24 20:52:15 +00:00
|
|
|
e.Actual = Bool(true)
|
2016-07-22 20:38:20 +00:00
|
|
|
|
2016-07-24 21:04:06 +00:00
|
|
|
} else if t.Val == "%false" {
|
2016-07-24 20:52:15 +00:00
|
|
|
e.Actual = Bool(false)
|
|
|
|
|
2016-07-24 21:04:06 +00:00
|
|
|
} else if t.Val[0] == '%' {
|
|
|
|
e.Actual = Macro(t.Val[1:])
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
} else {
|
|
|
|
e.Actual = Identifier(t.Val)
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
return e, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseString(t lexer.Token) (Expr, error) {
|
|
|
|
str, err := strconv.Unquote(t.Val)
|
2016-07-23 16:59:11 +00:00
|
|
|
if err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, exprErr{
|
2016-07-24 02:26:26 +00:00
|
|
|
err: err,
|
2016-07-23 16:59:11 +00:00
|
|
|
tok: t,
|
|
|
|
}
|
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{Token: t, Actual: String(str)}, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
|
|
|
|
if toks[0].Equal(comma) {
|
|
|
|
return parseTuple(toks, root)
|
2016-07-22 21:53:27 +00:00
|
|
|
|
2016-07-22 20:53:04 +00:00
|
|
|
} else if toks[0].Equal(pipe) {
|
|
|
|
return parsePipe(toks, root)
|
2016-07-22 21:53:27 +00:00
|
|
|
|
|
|
|
} else if toks[0].Equal(arrow) {
|
|
|
|
expr, toks, err := parse(toks[1:])
|
|
|
|
if err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
pipe, ok := expr.Actual.(Pipe)
|
2016-07-22 21:53:27 +00:00
|
|
|
if !ok {
|
2016-07-24 20:52:15 +00:00
|
|
|
pipe = Pipe{expr}
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{Token: root.Token, Actual: Statement{in: root, pipe: pipe}}, toks, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
2016-07-22 21:53:27 +00:00
|
|
|
return root, toks, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseTuple(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
|
2016-07-24 20:52:15 +00:00
|
|
|
rootTup, ok := root.Actual.(Tuple)
|
2016-07-22 20:38:20 +00:00
|
|
|
if !ok {
|
2016-07-24 20:52:15 +00:00
|
|
|
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}
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(toks) < 2 {
|
2016-07-24 20:52:15 +00:00
|
|
|
return mkRoot(), toks, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
} else if !toks[0].Equal(comma) {
|
2016-07-24 02:37:10 +00:00
|
|
|
if toks[0].TokenType == lexer.Punctuation {
|
2016-07-24 20:52:15 +00:00
|
|
|
return parseConnectingPunct(toks, mkRoot())
|
2016-07-24 02:37:10 +00:00
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return mkRoot(), toks, nil
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var expr Expr
|
|
|
|
var err error
|
|
|
|
if expr, toks, err = parseSingle(toks[1:]); err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 20:38:20 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
rootTup = append(rootTup, expr)
|
|
|
|
return parseTuple(toks, mkRoot())
|
2016-07-06 02:03:04 +00:00
|
|
|
}
|
2016-07-22 20:53:04 +00:00
|
|
|
|
|
|
|
func parsePipe(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
|
2016-07-24 20:52:15 +00:00
|
|
|
rootPipe, ok := root.Actual.(Pipe)
|
2016-07-22 20:53:04 +00:00
|
|
|
if !ok {
|
2016-07-24 20:52:15 +00:00
|
|
|
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}
|
2016-07-22 20:53:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(toks) < 2 {
|
2016-07-24 20:52:15 +00:00
|
|
|
return mkRoot(), toks, nil
|
2016-07-22 20:53:04 +00:00
|
|
|
} else if !toks[0].Equal(pipe) {
|
2016-07-24 02:37:10 +00:00
|
|
|
if toks[0].TokenType == lexer.Punctuation {
|
2016-07-24 20:52:15 +00:00
|
|
|
return parseConnectingPunct(toks, mkRoot())
|
2016-07-24 02:37:10 +00:00
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
return mkRoot(), toks, nil
|
2016-07-22 20:53:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var expr Expr
|
|
|
|
var err error
|
|
|
|
if expr, toks, err = parseSingle(toks[1:]); err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, nil, err
|
2016-07-22 20:53:04 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 20:52:15 +00:00
|
|
|
rootPipe = append(rootPipe, expr)
|
|
|
|
return parsePipe(toks, mkRoot())
|
2016-07-22 20:53:04 +00:00
|
|
|
}
|
2016-07-22 21:53:27 +00:00
|
|
|
|
|
|
|
// 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.
|
2016-07-24 20:52:15 +00:00
|
|
|
func parseBlock(toks []lexer.Token) (Expr, error) {
|
2016-07-22 21:53:27 +00:00
|
|
|
b := Block{}
|
2016-07-24 20:52:15 +00:00
|
|
|
first := toks[0]
|
2016-07-22 21:53:27 +00:00
|
|
|
var expr Expr
|
|
|
|
var err error
|
|
|
|
for {
|
|
|
|
if len(toks) == 0 {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{Token: first, Actual: b}, nil
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if expr, toks, err = parse(toks); err != nil {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, err
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
stmt, ok := expr.Actual.(Statement)
|
2016-07-22 21:53:27 +00:00
|
|
|
if !ok {
|
2016-07-24 20:52:15 +00:00
|
|
|
return Expr{}, exprErr{
|
2016-07-23 16:59:11 +00:00
|
|
|
reason: "blocks may only contain full statements",
|
2016-07-24 20:52:15 +00:00
|
|
|
tok: expr.Token,
|
2016-07-23 16:59:11 +00:00
|
|
|
tokCtx: "non-statement here",
|
|
|
|
}
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
2016-07-24 20:52:15 +00:00
|
|
|
b = append(b, stmt)
|
2016-07-22 21:53:27 +00:00
|
|
|
}
|
|
|
|
}
|