Fixing op precedences

This commit is contained in:
Mike Farah 2021-02-03 17:20:54 +11:00
parent 9e56b364c2
commit 6efe4c4797
6 changed files with 41 additions and 28 deletions

View File

@ -79,3 +79,14 @@ then
yq eval '.a.[] | select(. == "*og") | [{"path":path, "value":.}]' sample.yml yq eval '.a.[] | select(. == "*og") | [{"path":path, "value":.}]' sample.yml
``` ```
will output will output
```yaml
- path:
- a
- 1
value: dog
- path:
- a
- 2
value: frog
```

View File

@ -70,7 +70,7 @@ func (p *expressionParserImpl) createExpressionTree(postFixPath []*Operation) (*
stack = append(stack, &newNode) stack = append(stack, &newNode)
} }
if len(stack) != 1 { if len(stack) != 1 {
return nil, fmt.Errorf("expected end of expression but found '%v', please check expression syntax", strings.TrimSpace(stack[1].Operation.StringValue)) return nil, fmt.Errorf("Bad expression, please check expression syntax")
} }
return stack[0], nil return stack[0], nil
} }

View File

@ -38,5 +38,5 @@ func TestPathTreeOneArgForOneArgOp(t *testing.T) {
func TestPathTreeExtraArgs(t *testing.T) { func TestPathTreeExtraArgs(t *testing.T) {
_, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)") _, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)")
test.AssertResultComplex(t, "expected end of expression but found 'explode', please check expression syntax", err.Error()) test.AssertResultComplex(t, "Bad expression, please check expression syntax", err.Error())
} }

View File

@ -43,15 +43,9 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
opener = openCollectObject opener = openCollectObject
collectOperator = collectObjectOpType collectOperator = collectObjectOpType
} }
itemsInMiddle := false
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener { for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
opStack, result = popOpToResult(opStack, result) opStack, result = popOpToResult(opStack, result)
itemsInMiddle = true
}
if !itemsInMiddle {
// must be an empty collection, add the empty object as a LHS parameter
result = append(result, &Operation{OperationType: emptyOpType})
log.Debugf("put EMPTY onto the result")
} }
if len(opStack) == 0 { if len(opStack) == 0 {
return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket") return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
@ -61,16 +55,11 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
log.Debugf("deleteing open bracket from opstack") log.Debugf("deleteing open bracket from opstack")
//and append a collect to the opStack //and append a collect to the opStack
//WHY AM I NOT PUTTING THIS STRAIGHT ONTO THE RESULT LIKE EMPTY?
result = append(result, &Operation{OperationType: collectOperator}) result = append(result, &Operation{OperationType: collectOperator})
log.Debugf("put collect onto the result") log.Debugf("put collect onto the result")
result = append(result, &Operation{OperationType: shortPipeOpType}) result = append(result, &Operation{OperationType: shortPipeOpType})
log.Debugf("put shortpipe onto the result") log.Debugf("put shortpipe onto the result")
// opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: shortPipeOpType}})
// log.Debugf("put shortPipe onto the opstack")
// opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: collectOperator}})
// log.Debugf("put collect onto the opstack")
case closeBracket: case closeBracket:
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket { for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
opStack, result = popOpToResult(opStack, result) opStack, result = popOpToResult(opStack, result)

View File

@ -19,9 +19,19 @@ var pathTests = []struct {
}, },
{ {
`[]`, `[]`,
append(make([]interface{}, 0), "[", "]"), append(make([]interface{}, 0), "[", "EMPTY", "]"),
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"), append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"),
}, },
{
`{}`,
append(make([]interface{}, 0), "{", "EMPTY", "}"),
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"),
},
{
`[{}]`, // [{}]
append(make([]interface{}, 0), "[", "{", "EMPTY", "}", "]"),
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE", "COLLECT", "SHORT_PIPE"),
},
{ {
`.b[.a]`, `.b[.a]`,
append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"), append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"),
@ -29,17 +39,17 @@ var pathTests = []struct {
}, },
{ {
`.[]`, `.[]`,
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "]"), append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"), append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
}, },
{ {
`.a[]`, `.a[]`,
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "]"), append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"), append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
}, },
{ {
`.a.[]`, `.a.[]`,
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "]"), append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"), append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
}, },
{ {
@ -54,7 +64,7 @@ var pathTests = []struct {
}, },
{ {
`.a[].c`, `.a[].c`,
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "c"), append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "c"),
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "c", "SHORT_PIPE"), append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "c", "SHORT_PIPE"),
}, },
{ {
@ -79,17 +89,17 @@ var pathTests = []struct {
}, },
{ {
`.a | .[].b == "apple"`, `.a | .[].b == "apple"`,
append(make([]interface{}, 0), "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"), append(make([]interface{}, 0), "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"),
append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"), append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"),
}, },
{ {
`(.a | .[].b) == "apple"`, `(.a | .[].b) == "apple"`,
append(make([]interface{}, 0), "(", "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"), append(make([]interface{}, 0), "(", "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"),
append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"), append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"),
}, },
{ {
`.[] | select(. == "*at")`, `.[] | select(. == "*at")`,
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"), append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"),
append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"), append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"),
}, },
{ {
@ -124,7 +134,7 @@ var pathTests = []struct {
}, },
{ {
`{.a: .c, .b.[]: .f.g[]}`, `{.a: .c, .b.[]: .f.g[]}`,
append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "TRAVERSE_ARRAY", "[", "]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "TRAVERSE_ARRAY", "[", "]", "}"), append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "}"),
append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "f", "g", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"), append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "f", "g", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"),
}, },
{ {
@ -177,11 +187,6 @@ var pathTests = []struct {
append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"), append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"),
append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"), append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"),
}, },
{
`{}`,
append(make([]interface{}, 0), "{", "}"),
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"),
},
} }
var tokeniser = newExpressionTokeniser() var tokeniser = newExpressionTokeniser()

View File

@ -409,6 +409,14 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
postProcessedTokens = append(postProcessedTokens, currentToken) postProcessedTokens = append(postProcessedTokens, currentToken)
if index != len(tokens)-1 &&
((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) ||
(currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) {
op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"}
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
}
if index != len(tokens)-1 && currentToken.CheckForPostTraverse && if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
tokens[index+1].TokenType == operationToken && tokens[index+1].TokenType == operationToken &&
tokens[index+1].Operation.OperationType == traversePathOpType { tokens[index+1].Operation.OperationType == traversePathOpType {