route/vendor/github.com/hashicorp/hil/parser/parser_test.go

1221 lines
23 KiB
Go

package parser
import (
"reflect"
"testing"
"github.com/hashicorp/hil/ast"
"github.com/hashicorp/hil/scanner"
)
func TestParser(t *testing.T) {
cases := []struct {
Input string
Error bool
Result ast.Node
}{
{
"",
false,
&ast.LiteralNode{
Value: "",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
},
{
"$",
false,
&ast.LiteralNode{
Value: "$",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
},
{
"foo",
false,
&ast.LiteralNode{
Value: "foo",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
},
{
"$${var.foo}",
false,
&ast.LiteralNode{
Value: "${var.foo}",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
},
// Identifier starting with a number
{
`foo ${123abcd}`,
true,
nil,
},
// Identifier starting with a *
{
`foo ${*abcd}`,
true,
nil,
},
{
"foo ${var.bar}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.VariableAccess{
Name: "var.bar",
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${var.bar.*.baz}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.VariableAccess{
Name: "var.bar.*.baz",
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${var.bar} baz",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.VariableAccess{
Name: "var.bar",
Posx: ast.Pos{Column: 7, Line: 1},
},
&ast.LiteralNode{
Value: " baz",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 15, Line: 1},
},
},
},
},
{
"foo ${var.bar.0}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.VariableAccess{
Name: "var.bar.0",
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${foo.foo-bar.baz.0.attr}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.VariableAccess{
Name: "foo.foo-bar.baz.0.attr",
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
`foo ${"bar"}`,
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.LiteralNode{
Value: "bar",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 8, Line: 1},
},
},
},
},
{
`foo ${"bar\nbaz"}`,
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.LiteralNode{
Value: "bar\nbaz",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 8, Line: 1},
},
},
},
},
{
`foo ${"bar \"baz\""}`,
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.LiteralNode{
Value: `bar "baz"`,
Typex: ast.TypeString,
Posx: ast.Pos{Column: 8, Line: 1},
},
},
},
},
{
`foo ${func('baz')}`,
true,
nil,
},
{
"foo ${42}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.LiteralNode{
Value: 42,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${3.14159}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.LiteralNode{
Value: 3.14159,
Typex: ast.TypeFloat,
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"fรถo ${true}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "fรถo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.LiteralNode{
Value: true,
Typex: ast.TypeBool,
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${42+1}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.Arithmetic{
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.LiteralNode{
Value: 42,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 7, Line: 1},
},
&ast.LiteralNode{
Value: 1,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 10, Line: 1},
},
},
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${-1}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.Arithmetic{
Op: ast.ArithmeticOpSub,
Exprs: []ast.Node{
&ast.LiteralNode{
Value: 0,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 7, Line: 1},
},
&ast.LiteralNode{
Value: 1,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 8, Line: 1},
},
},
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
{
"foo ${var.bar*1} baz",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.Arithmetic{
Op: ast.ArithmeticOpMul,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "var.bar",
Posx: ast.Pos{Column: 7, Line: 1},
},
&ast.LiteralNode{
Value: 1,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 15, Line: 1},
},
},
Posx: ast.Pos{Column: 7, Line: 1},
},
&ast.LiteralNode{
Value: " baz",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 17, Line: 1},
},
},
},
},
{
"${!a}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
// !a parses as (false == a)
&ast.Arithmetic{
Op: ast.ArithmeticOpEqual,
Exprs: []ast.Node{
&ast.LiteralNode{
Value: false,
Typex: ast.TypeBool,
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 4, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a==b}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpEqual,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.VariableAccess{
Name: "b",
Posx: ast.Pos{Column: 6, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a!=b}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpNotEqual,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.VariableAccess{
Name: "b",
Posx: ast.Pos{Column: 6, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a < 5 ? a + 5 : a + 10}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Conditional{
CondExpr: &ast.Arithmetic{
Op: ast.ArithmeticOpLessThan,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.LiteralNode{
Value: 5,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 7, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
TrueExpr: &ast.Arithmetic{
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 11, Line: 1},
},
&ast.LiteralNode{
Value: 5,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 15, Line: 1},
},
},
Posx: ast.Pos{Column: 11, Line: 1},
},
FalseExpr: &ast.Arithmetic{
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 19, Line: 1},
},
&ast.LiteralNode{
Value: 10,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 23, Line: 1},
},
},
Posx: ast.Pos{Column: 19, Line: 1},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${true&&false}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalAnd,
Exprs: []ast.Node{
&ast.LiteralNode{
Value: true,
Typex: ast.TypeBool,
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.LiteralNode{
Value: false,
Typex: ast.TypeBool,
Posx: ast.Pos{Column: 9, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${true||false}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalOr,
Exprs: []ast.Node{
&ast.LiteralNode{
Value: true,
Typex: ast.TypeBool,
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.LiteralNode{
Value: false,
Typex: ast.TypeBool,
Posx: ast.Pos{Column: 9, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a||b&&c}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalOr,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalAnd,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "b",
Posx: ast.Pos{Column: 6, Line: 1},
},
&ast.VariableAccess{
Name: "c",
Posx: ast.Pos{Column: 9, Line: 1},
},
},
Posx: ast.Pos{Column: 6, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a&&b||c}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalOr,
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalAnd,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.VariableAccess{
Name: "b",
Posx: ast.Pos{Column: 6, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.VariableAccess{
Name: "c",
Posx: ast.Pos{Column: 9, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a<5||b>2}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalOr,
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLessThan,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.LiteralNode{
Value: 5,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 5, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.Arithmetic{
Op: ast.ArithmeticOpGreaterThan,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "b",
Posx: ast.Pos{Column: 8, Line: 1},
},
&ast.LiteralNode{
Value: 2,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 10, Line: 1},
},
},
Posx: ast.Pos{Column: 8, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${a<5&&b>2}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLogicalAnd,
Exprs: []ast.Node{
&ast.Arithmetic{
Op: ast.ArithmeticOpLessThan,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "a",
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.LiteralNode{
Value: 5,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 5, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
&ast.Arithmetic{
Op: ast.ArithmeticOpGreaterThan,
Exprs: []ast.Node{
&ast.VariableAccess{
Name: "b",
Posx: ast.Pos{Column: 8, Line: 1},
},
&ast.LiteralNode{
Value: 2,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 10, Line: 1},
},
},
Posx: ast.Pos{Column: 8, Line: 1},
},
},
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${fรถo()}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Call{
Func: "fรถo",
Args: nil,
Posx: ast.Pos{Column: 3, Line: 1},
},
},
},
},
{
"${foo(bar)}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Call{
Func: "foo",
Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{
&ast.VariableAccess{
Name: "bar",
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
},
},
{
"${foo(bar, baz)}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Call{
Func: "foo",
Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{
&ast.VariableAccess{
Name: "bar",
Posx: ast.Pos{Column: 7, Line: 1},
},
&ast.VariableAccess{
Name: "baz",
Posx: ast.Pos{Column: 12, Line: 1},
},
},
},
},
},
},
{
"${foo(bar(baz))}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Call{
Func: "foo",
Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{
&ast.Call{
Func: "bar",
Posx: ast.Pos{Column: 7, Line: 1},
Args: []ast.Node{
&ast.VariableAccess{
Name: "baz",
Posx: ast.Pos{Column: 11, Line: 1},
},
},
},
},
},
},
},
},
{
`foo ${"bar ${baz}"}`,
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "foo ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 1, Line: 1},
},
&ast.Output{
Posx: ast.Pos{Column: 7, Line: 1},
Exprs: []ast.Node{
&ast.LiteralNode{
Value: "bar ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 8, Line: 1},
},
&ast.VariableAccess{
Name: "baz",
Posx: ast.Pos{Column: 14, Line: 1},
},
},
},
},
},
},
{
"${foo[1]}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Index{
Posx: ast.Pos{Column: 6, Line: 1},
Target: &ast.VariableAccess{
Name: "foo",
Posx: ast.Pos{Column: 3, Line: 1},
},
Key: &ast.LiteralNode{
Value: 1,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 7, Line: 1},
},
},
},
},
},
{
"${foo[1]} - ${bar[0]}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Index{
Posx: ast.Pos{Column: 6, Line: 1},
Target: &ast.VariableAccess{
Name: "foo",
Posx: ast.Pos{Column: 3, Line: 1},
},
Key: &ast.LiteralNode{
Value: 1,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 7, Line: 1},
},
},
&ast.LiteralNode{
Value: " - ",
Typex: ast.TypeString,
Posx: ast.Pos{Column: 10, Line: 1},
},
&ast.Index{
Posx: ast.Pos{Column: 18, Line: 1},
Target: &ast.VariableAccess{
Name: "bar",
Posx: ast.Pos{Column: 15, Line: 1},
},
Key: &ast.LiteralNode{
Value: 0,
Typex: ast.TypeInt,
Posx: ast.Pos{Column: 19, Line: 1},
},
},
},
},
},
{
// * has higher precedence than +
"${42+2*2}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 3, Line: 1},
Value: 42,
Typex: ast.TypeInt,
},
&ast.Arithmetic{
Posx: ast.Pos{Column: 6, Line: 1},
Op: ast.ArithmeticOpMul,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 6, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 8, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
},
},
},
},
},
{
// parentheses override precedence rules
"${(42+2)*2}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpMul,
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 4, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 4, Line: 1},
Value: 42,
Typex: ast.TypeInt,
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 7, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 10, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
},
},
},
{
// Left-associative parsing of operators with equal precedence
"${42+2+2}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 3, Line: 1},
Value: 42,
Typex: ast.TypeInt,
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 6, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 8, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
},
},
},
{
// Unary - has higher precedence than addition
"${42+-2+2}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 3, Line: 1},
Value: 42,
Typex: ast.TypeInt,
},
&ast.Arithmetic{
Posx: ast.Pos{Column: 6, Line: 1},
Op: ast.ArithmeticOpSub,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 6, Line: 1},
Value: 0,
Typex: ast.TypeInt,
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 7, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
},
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 9, Line: 1},
Value: 2,
Typex: ast.TypeInt,
},
},
},
},
},
},
{
"${-46+5}",
false,
&ast.Output{
Posx: ast.Pos{Column: 1, Line: 1},
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpAdd,
Exprs: []ast.Node{
&ast.Arithmetic{
Posx: ast.Pos{Column: 3, Line: 1},
Op: ast.ArithmeticOpSub,
Exprs: []ast.Node{
&ast.LiteralNode{
Posx: ast.Pos{Column: 3, Line: 1},
Value: 0,
Typex: ast.TypeInt,
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 4, Line: 1},
Value: 46,
Typex: ast.TypeInt,
},
},
},
&ast.LiteralNode{
Posx: ast.Pos{Column: 7, Line: 1},
Value: 5,
Typex: ast.TypeInt,
},
},
},
},
},
},
{
"${foo=baz}",
true,
nil,
},
{
"${foo&baz}",
true,
nil,
},
{
"${foo|baz}",
true,
nil,
},
{
"${foo[1][2]}",
true,
nil,
},
{
`foo ${bar ${baz}}`,
true,
nil,
},
{
`foo ${${baz}}`,
true,
nil,
},
{
"${var",
true,
nil,
},
{
`${"unclosed`,
true,
nil,
},
{
`${"bar\nbaz}`,
true,
nil,
},
{
`${รถ(o("")`,
true,
nil,
},
{
`${"${"${"`,
true,
nil,
},
{
`${("$"`,
true,
nil,
},
{
`${("${("${"${"$"`,
true,
nil,
},
{
`${(p["$"`,
true,
nil,
},
{
`${e(e,e,`,
true,
nil,
},
{
"${file(/tmp/somefile)}",
true,
nil,
},
}
for _, tc := range cases {
ch := scanner.Scan(tc.Input, ast.Pos{Line: 1, Column: 1})
actual, err := Parse(ch)
if err != nil != tc.Error {
t.Errorf("\nError: %s\n\nInput: %s\n", err, tc.Input)
}
if !reflect.DeepEqual(actual, tc.Result) {
t.Errorf("\nGot: %#v\nWant: %#v\n\nInput: %s\n", actual, tc.Result, tc.Input)
}
}
}