diff --git a/vendor/github.com/hashicorp/hil/parser/binary_op.go b/vendor/github.com/hashicorp/hil/parser/binary_op.go new file mode 100644 index 0000000000..c43c5eb02d --- /dev/null +++ b/vendor/github.com/hashicorp/hil/parser/binary_op.go @@ -0,0 +1,29 @@ +package parser + +import ( + "github.com/hashicorp/hil/ast" + "github.com/hashicorp/hil/scanner" +) + +var binaryOps []map[scanner.TokenType]ast.ArithmeticOp + +func init() { + // This operation table maps from the operator's scanner token type + // to the AST arithmetic operation. All expressions produced from + // binary operators are *ast.Arithmetic nodes. + // + // Binary operator groups are listed in order of precedence, with + // the *lowest* precedence first. Operators within the same group + // have left-to-right associativity. + binaryOps = []map[scanner.TokenType]ast.ArithmeticOp{ + { + scanner.PLUS: ast.ArithmeticOpAdd, + scanner.MINUS: ast.ArithmeticOpSub, + }, + { + scanner.STAR: ast.ArithmeticOpMul, + scanner.SLASH: ast.ArithmeticOpDiv, + scanner.PERCENT: ast.ArithmeticOpMod, + }, + } +} diff --git a/vendor/github.com/hashicorp/hil/parser/parser.go b/vendor/github.com/hashicorp/hil/parser/parser.go index 88d60471b1..af1a90e75f 100644 --- a/vendor/github.com/hashicorp/hil/parser/parser.go +++ b/vendor/github.com/hashicorp/hil/parser/parser.go @@ -188,6 +188,21 @@ func (p *parser) ParseInterpolation() (ast.Node, error) { } func (p *parser) ParseExpression() (ast.Node, error) { + return p.parseBinaryOps(binaryOps) +} + +// 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[scanner.TokenType]ast.ArithmeticOp) (ast.Node, error) { + 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:] + startPos := p.peeker.Peek().Pos var lhs, rhs ast.Node @@ -198,37 +213,24 @@ func (p *parser) ParseExpression() (ast.Node, error) { // expression or it might just be a standalone term, but // we won't know until we've parsed it and can look ahead // to see if there's an operator token. - lhs, err = p.ParseExpressionTerm() + lhs, err = p.parseBinaryOps(remaining) if err != nil { return nil, err } // We'll keep eating up arithmetic operators until we run - // out, so that binary expressions will combine in a manner - // that is compatible with the old yacc-based parser: - // a+b*c => (a+b)*c, *not* a+(b*c) + // 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) // - // (perhaps later we'll implement some more intuitive precendence - // rules here, but for now being compatible with the old parser - // is the goal.) + // 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.peeker.Peek() - newOperator := ast.ArithmeticOpInvalid - - switch next.Type { - case scanner.PLUS: - newOperator = ast.ArithmeticOpAdd - case scanner.MINUS: - newOperator = ast.ArithmeticOpSub - case scanner.STAR: - newOperator = ast.ArithmeticOpMul - case scanner.SLASH: - newOperator = ast.ArithmeticOpDiv - case scanner.PERCENT: - newOperator = ast.ArithmeticOpMod - } - - if newOperator == ast.ArithmeticOpInvalid { + var newOperator ast.ArithmeticOp + var ok bool + if newOperator, ok = thisLevel[next.Type]; !ok { break } @@ -244,7 +246,7 @@ func (p *parser) ParseExpression() (ast.Node, error) { operator = newOperator p.peeker.Read() // eat operator token - rhs, err = p.ParseExpressionTerm() + rhs, err = p.parseBinaryOps(remaining) if err != nil { return nil, err } diff --git a/vendor/vendor.json b/vendor/vendor.json index b0bb726d2e..ce47ca9f96 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1426,26 +1426,26 @@ { "checksumSHA1": "/TJCBetWCMVsOpehJzVk3S/xtWM=", "path": "github.com/hashicorp/hil", - "revision": "fceef4715de114b559e042e19fd2cec06003628a", - "revisionTime": "2016-11-15T03:45:18Z" + "revision": "6861984a28be5acf4e47b5769bc841ec653bf3ba", + "revisionTime": "2016-11-15T22:45:22Z" }, { "checksumSHA1": "YPJwewz3dAqEWOGP2qIIWeCufF0=", "path": "github.com/hashicorp/hil/ast", - "revision": "fceef4715de114b559e042e19fd2cec06003628a", - "revisionTime": "2016-11-15T03:45:18Z" + "revision": "6861984a28be5acf4e47b5769bc841ec653bf3ba", + "revisionTime": "2016-11-15T22:45:22Z" }, { - "checksumSHA1": "BeqAygYXJlCHpU0HVWg4mxuROks=", + "checksumSHA1": "FlyG9T7bd3jNK6R9XwXSobecVEQ=", "path": "github.com/hashicorp/hil/parser", - "revision": "fceef4715de114b559e042e19fd2cec06003628a", - "revisionTime": "2016-11-15T03:45:18Z" + "revision": "6861984a28be5acf4e47b5769bc841ec653bf3ba", + "revisionTime": "2016-11-15T22:45:22Z" }, { "checksumSHA1": "2aIAMbqPrE0qyYoJSL6qjVm+Tt0=", "path": "github.com/hashicorp/hil/scanner", - "revision": "fceef4715de114b559e042e19fd2cec06003628a", - "revisionTime": "2016-11-15T03:45:18Z" + "revision": "6861984a28be5acf4e47b5769bc841ec653bf3ba", + "revisionTime": "2016-11-15T22:45:22Z" }, { "path": "github.com/hashicorp/logutils",