zclsyntax: first pass of parsing binary operations

Not actually tested yet, since it can't be used until parseExpressionTerm
works.
This commit is contained in:
Martin Atkins 2017-05-31 18:54:54 -07:00
parent 2fc14b729a
commit 9a380c6260
3 changed files with 110 additions and 1 deletions

View File

@ -1,5 +1,10 @@
package zclsyntax
import (
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-zcl/zcl"
)
type Operation rune
const (
@ -60,3 +65,28 @@ func init() {
},
}
}
type BinaryOpExpr struct {
LHS Expression
Op Operation
RHS Expression
SrcRange zcl.Range
}
func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) {
e.LHS = w(e.LHS).(Expression)
e.RHS = w(e.LHS).(Expression)
}
func (e *BinaryOpExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
panic("BinaryOpExpr.Value not yet implemented")
}
func (e *BinaryOpExpr) Range() zcl.Range {
return e.SrcRange
}
func (e *BinaryOpExpr) StartRange() zcl.Range {
return e.LHS.StartRange()
}

View File

@ -7,6 +7,10 @@ import (
"github.com/zclconf/go-zcl/zcl"
)
func (e *BinaryOpExpr) Variables() []zcl.Traversal {
return Variables(e)
}
func (e *ConditionalExpr) Variables() []zcl.Traversal {
return Variables(e)
}

View File

@ -315,8 +315,83 @@ func (p *parser) parseTernaryConditional() (Expression, zcl.Diagnostics) {
}, diags
}
// parseBinaryOps calls itself recursively to work through all of the
// operator precedence groups, and then eventually calls parseExpressionTerm
// for each operand.
func (p *parser) parseBinaryOps(ops []map[TokenType]Operation) (Expression, zcl.Diagnostics) {
panic("parseBinaryOps not yet implemented")
if len(ops) == 0 {
// We've run out of operators, so now we'll just try to parse a term.
return p.parseExpressionTerm()
}
thisLevel := ops[0]
remaining := ops[1:]
var lhs, rhs Expression
operation := OpNil
var diags zcl.Diagnostics
// Parse a term that might be the first operand of a binary
// operation or it might just be a standalone term.
// We won't know until we've parsed it and can look ahead
// to see if there's an operator token for this level.
lhs, lhsDiags := p.parseBinaryOps(remaining)
diags = append(diags, lhsDiags...)
if p.recovery && lhsDiags.HasErrors() {
return lhs, diags
}
// We'll keep eating up operators until we run out, so that operators
// with the same precedence will combine in a left-associative manner:
// a+b+c => (a+b)+c, not a+(b+c)
//
// Should we later want to have right-associative operators, a way
// to achieve that would be to call back up to ParseExpression here
// instead of iteratively parsing only the remaining operators.
for {
next := p.Peek()
var newOp Operation
var ok bool
if newOp, ok = thisLevel[next.Type]; !ok {
break
}
// Are we extending an expression started on the previous iteration?
if operation != OpNil {
lhs = &BinaryOpExpr{
LHS: lhs,
Op: operation,
RHS: rhs,
SrcRange: zcl.RangeBetween(lhs.Range(), rhs.Range()),
}
}
operation = newOp
p.Read() // eat operator token
var rhsDiags zcl.Diagnostics
rhs, rhsDiags = p.parseBinaryOps(remaining)
diags = append(diags, rhsDiags...)
if p.recovery && rhsDiags.HasErrors() {
return lhs, diags
}
}
if operation == OpNil {
return lhs, diags
}
return &BinaryOpExpr{
LHS: lhs,
Op: operation,
RHS: rhs,
SrcRange: zcl.RangeBetween(lhs.Range(), rhs.Range()),
}, diags
}
func (p *parser) parseExpressionTerm() (Expression, zcl.Diagnostics) {
panic("parseExpressionTerm not yet implemented")
}
// parseQuotedStringLiteral is a helper for parsing quoted strings that