Fixing op precedences

This commit is contained in:
Mike Farah 2021-02-03 17:11:47 +11:00
parent 85ec32e3db
commit 9e56b364c2
5 changed files with 33 additions and 23 deletions

View File

@ -79,14 +79,3 @@ 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

@ -20,20 +20,22 @@ func newExpressionPostFixer() expressionPostFixer {
func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) { func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
var newOp *token var newOp *token
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1] opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
log.Debugf("popped %v from opstack to results", newOp.toString(true))
return opStack, append(result, newOp.Operation) return opStack, append(result, newOp.Operation)
} }
func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) { func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
var result []*Operation var result []*Operation
// surround the whole thing with quotes // surround the whole thing with brackets
var opStack = []*token{{TokenType: openBracket}} var opStack = []*token{{TokenType: openBracket}}
var tokens = append(infixTokens, &token{TokenType: closeBracket}) var tokens = append(infixTokens, &token{TokenType: closeBracket})
for _, currentToken := range tokens { for _, currentToken := range tokens {
log.Debugf("postfix processing currentToken %v, %v", currentToken.toString(), currentToken.Operation) log.Debugf("postfix processing currentToken %v", currentToken.toString(true))
switch currentToken.TokenType { switch currentToken.TokenType {
case openBracket, openCollect, openCollectObject: case openBracket, openCollect, openCollectObject:
opStack = append(opStack, currentToken) opStack = append(opStack, currentToken)
log.Debugf("put %v onto the opstack", currentToken.toString(true))
case closeCollect, closeCollectObject: case closeCollect, closeCollectObject:
var opener tokenType = openCollect var opener tokenType = openCollect
var collectOperator *operationType = collectOpType var collectOperator *operationType = collectOpType
@ -49,15 +51,26 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
if !itemsInMiddle { if !itemsInMiddle {
// must be an empty collection, add the empty object as a LHS parameter // must be an empty collection, add the empty object as a LHS parameter
result = append(result, &Operation{OperationType: emptyOpType}) 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")
} }
// now we should have [] as the last element on the opStack, get rid of it // now we should have [ as the last element on the opStack, get rid of it
opStack = opStack[0 : len(opStack)-1] opStack = opStack[0 : len(opStack)-1]
log.Debugf("deleteing open bracket from opstack")
//and append a collect to the opStack //and append a collect to the opStack
opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: shortPipeOpType}}) //WHY AM I NOT PUTTING THIS STRAIGHT ONTO THE RESULT LIKE EMPTY?
opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: collectOperator}}) result = append(result, &Operation{OperationType: collectOperator})
log.Debugf("put collect onto the result")
result = append(result, &Operation{OperationType: shortPipeOpType})
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)
@ -73,11 +86,12 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
// pop off higher precedent operators onto the result // pop off higher precedent operators onto the result
for len(opStack) > 0 && for len(opStack) > 0 &&
opStack[len(opStack)-1].TokenType == operationToken && opStack[len(opStack)-1].TokenType == operationToken &&
opStack[len(opStack)-1].Operation.OperationType.Precedence >= currentPrecedence { opStack[len(opStack)-1].Operation.OperationType.Precedence > currentPrecedence {
opStack, result = popOpToResult(opStack, result) opStack, result = popOpToResult(opStack, result)
} }
// add this operator to the opStack // add this operator to the opStack
opStack = append(opStack, currentToken) opStack = append(opStack, currentToken)
log.Debugf("put %v onto the opstack", currentToken.toString(true))
} }
} }

View File

@ -12,6 +12,11 @@ var pathTests = []struct {
expectedTokens []interface{} expectedTokens []interface{}
expectedPostFix []interface{} expectedPostFix []interface{}
}{ }{
{
`.a | .b | .c`,
append(make([]interface{}, 0), "a", "PIPE", "b", "PIPE", "c"),
append(make([]interface{}, 0), "a", "b", "c", "PIPE", "PIPE"),
},
{ {
`[]`, `[]`,
append(make([]interface{}, 0), "[", "]"), append(make([]interface{}, 0), "[", "]"),
@ -190,7 +195,7 @@ func TestPathParsing(t *testing.T) {
} }
var tokenValues []interface{} var tokenValues []interface{}
for _, token := range tokens { for _, token := range tokens {
tokenValues = append(tokenValues, token.toString()) tokenValues = append(tokenValues, token.toString(false))
} }
test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path)) test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path))

View File

@ -34,9 +34,11 @@ type token struct {
} }
func (t *token) toString() string { func (t *token) toString(detail bool) string {
if t.TokenType == operationToken { if t.TokenType == operationToken {
log.Debug("toString, its an op") if detail {
return fmt.Sprintf("%v (%v)", t.Operation.toString(), t.Operation.OperationType.Precedence)
}
return t.Operation.toString() return t.Operation.toString()
} else if t.TokenType == openBracket { } else if t.TokenType == openBracket {
return "(" return "("
@ -354,7 +356,7 @@ func (p *expressionTokeniserImpl) Tokenise(expression string) ([]*token, error)
if tok != nil { if tok != nil {
currentToken := tok.(*token) currentToken := tok.(*token)
log.Debugf("Tokenising %v", currentToken.toString()) log.Debugf("Tokenising %v", currentToken.toString(true))
tokens = append(tokens, currentToken) tokens = append(tokens, currentToken)
} }
if err != nil { if err != nil {

View File

@ -72,10 +72,10 @@ var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50
var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator} var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator}
var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator} var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator}
var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: traversePathOperator} var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 51, Handler: traversePathOperator}
var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Precedence: 50, Handler: traverseArrayOperator} var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Precedence: 50, Handler: traverseArrayOperator}
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: selfOperator} var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 51, Handler: selfOperator}
var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator} var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator}
var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator} var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator}
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator} var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}