diff --git a/pkg/yqlib/doc/Path.md b/pkg/yqlib/doc/Path.md index 9bf1f404..1748c486 100644 --- a/pkg/yqlib/doc/Path.md +++ b/pkg/yqlib/doc/Path.md @@ -79,14 +79,3 @@ then yq eval '.a.[] | select(. == "*og") | [{"path":path, "value":.}]' sample.yml ``` will output -```yaml -- path: - - a - - 1 - value: dog -- path: - - a - - 2 - value: frog -``` - diff --git a/pkg/yqlib/expression_postfix.go b/pkg/yqlib/expression_postfix.go index 7a4a707e..2fbba373 100644 --- a/pkg/yqlib/expression_postfix.go +++ b/pkg/yqlib/expression_postfix.go @@ -20,20 +20,22 @@ func newExpressionPostFixer() expressionPostFixer { func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) { var newOp *token 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) } func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) { var result []*Operation - // surround the whole thing with quotes + // surround the whole thing with brackets var opStack = []*token{{TokenType: openBracket}} var tokens = append(infixTokens, &token{TokenType: closeBracket}) 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 { case openBracket, openCollect, openCollectObject: opStack = append(opStack, currentToken) + log.Debugf("put %v onto the opstack", currentToken.toString(true)) case closeCollect, closeCollectObject: var opener tokenType = openCollect var collectOperator *operationType = collectOpType @@ -49,15 +51,26 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope 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 { 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] + log.Debugf("deleteing open bracket from opstack") + //and append a collect to the opStack - opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: shortPipeOpType}}) - opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: collectOperator}}) + //WHY AM I NOT PUTTING THIS STRAIGHT ONTO THE RESULT LIKE EMPTY? + 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: for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket { opStack, result = popOpToResult(opStack, result) @@ -73,11 +86,12 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope // pop off higher precedent operators onto the result for len(opStack) > 0 && 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) } // add this operator to the opStack opStack = append(opStack, currentToken) + log.Debugf("put %v onto the opstack", currentToken.toString(true)) } } diff --git a/pkg/yqlib/expression_processing_test.go b/pkg/yqlib/expression_processing_test.go index 2c76d140..1e7d3b62 100644 --- a/pkg/yqlib/expression_processing_test.go +++ b/pkg/yqlib/expression_processing_test.go @@ -12,6 +12,11 @@ var pathTests = []struct { expectedTokens []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), "[", "]"), @@ -190,7 +195,7 @@ func TestPathParsing(t *testing.T) { } var tokenValues []interface{} 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)) diff --git a/pkg/yqlib/expression_tokeniser.go b/pkg/yqlib/expression_tokeniser.go index 3c68633f..82f383c7 100644 --- a/pkg/yqlib/expression_tokeniser.go +++ b/pkg/yqlib/expression_tokeniser.go @@ -34,9 +34,11 @@ type token struct { } -func (t *token) toString() string { +func (t *token) toString(detail bool) string { 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() } else if t.TokenType == openBracket { return "(" @@ -354,7 +356,7 @@ func (p *expressionTokeniserImpl) Tokenise(expression string) ([]*token, error) if tok != nil { currentToken := tok.(*token) - log.Debugf("Tokenising %v", currentToken.toString()) + log.Debugf("Tokenising %v", currentToken.toString(true)) tokens = append(tokens, currentToken) } if err != nil { diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index baa30c85..1372fedf 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -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 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 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 envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator} var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}