yq/pkg/yqlib/expression_postfix.go

137 lines
4.7 KiB
Go
Raw Normal View History

2020-11-03 23:48:43 +00:00
package yqlib
2020-09-20 12:40:09 +00:00
import (
"errors"
"fmt"
2020-10-17 11:10:47 +00:00
2020-11-22 02:50:32 +00:00
logging "gopkg.in/op/go-logging.v1"
2020-09-20 12:40:09 +00:00
)
2021-01-12 23:18:53 +00:00
type expressionPostFixer interface {
ConvertToPostfix([]*token) ([]*Operation, error)
2020-09-20 12:40:09 +00:00
}
2021-01-12 23:18:53 +00:00
type expressionPostFixerImpl struct {
2020-09-20 12:40:09 +00:00
}
2021-01-12 23:18:53 +00:00
func newExpressionPostFixer() expressionPostFixer {
return &expressionPostFixerImpl{}
2020-09-20 12:40:09 +00:00
}
func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
var newOp *token
2020-10-20 02:53:26 +00:00
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
2021-02-03 06:11:47 +00:00
log.Debugf("popped %v from opstack to results", newOp.toString(true))
2020-10-20 04:33:20 +00:00
return opStack, append(result, newOp.Operation)
2020-09-20 12:40:09 +00:00
}
func validateNoOpenTokens(token *token) error {
if token.TokenType == openCollect {
return fmt.Errorf(("Bad expression, could not find matching `]`"))
} else if token.TokenType == openCollectObject {
return fmt.Errorf(("Bad expression, could not find matching `}`"))
} else if token.TokenType == openBracket {
return fmt.Errorf(("Bad expression, could not find matching `)`"))
}
return nil
}
2021-01-12 23:18:53 +00:00
func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
2020-10-20 04:33:20 +00:00
var result []*Operation
2021-02-03 06:11:47 +00:00
// surround the whole thing with brackets
2021-02-03 04:51:26 +00:00
var opStack = []*token{{TokenType: openBracket}}
2021-01-11 22:55:55 +00:00
var tokens = append(infixTokens, &token{TokenType: closeBracket})
2020-09-20 12:40:09 +00:00
for _, currentToken := range tokens {
2021-02-03 06:11:47 +00:00
log.Debugf("postfix processing currentToken %v", currentToken.toString(true))
switch currentToken.TokenType {
2021-01-11 22:55:55 +00:00
case openBracket, openCollect, openCollectObject:
opStack = append(opStack, currentToken)
2021-02-03 06:11:47 +00:00
log.Debugf("put %v onto the opstack", currentToken.toString(true))
2021-01-11 22:55:55 +00:00
case closeCollect, closeCollectObject:
var opener tokenType = openCollect
var collectOperator *operationType = collectOpType
2021-01-11 22:55:55 +00:00
if currentToken.TokenType == closeCollectObject {
opener = openCollectObject
collectOperator = collectObjectOpType
2020-10-21 02:54:51 +00:00
}
2021-02-03 06:20:54 +00:00
2020-10-21 02:54:51 +00:00
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
missingClosingTokenErr := validateNoOpenTokens(opStack[len(opStack)-1])
if missingClosingTokenErr != nil {
return nil, missingClosingTokenErr
}
2020-10-16 01:29:26 +00:00
opStack, result = popOpToResult(opStack, result)
}
if len(opStack) == 0 {
return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
}
2021-02-03 06:11:47 +00:00
// now we should have [ as the last element on the opStack, get rid of it
2020-10-16 01:29:26 +00:00
opStack = opStack[0 : len(opStack)-1]
2021-11-25 09:24:51 +00:00
log.Debugf("deleting open bracket from opstack")
2021-02-03 06:11:47 +00:00
2021-05-21 04:18:24 +00:00
//and append a collect to the result
2021-11-30 02:07:27 +00:00
2021-05-09 05:36:33 +00:00
// hack - see if there's the optional traverse flag
2021-11-30 02:07:27 +00:00
// on the close op - move it to the traverse array op
2021-05-09 05:36:33 +00:00
// allows for .["cat"]?
prefs := traversePreferences{}
closeTokenMatch := string(currentToken.Match.Bytes)
if closeTokenMatch[len(closeTokenMatch)-1:] == "?" {
prefs.OptionalTraverse = true
}
2021-11-30 02:07:27 +00:00
result = append(result, &Operation{OperationType: collectOperator})
2021-02-03 06:11:47 +00:00
log.Debugf("put collect onto the result")
2021-11-30 02:04:32 +00:00
if opener != openCollect {
result = append(result, &Operation{OperationType: shortPipeOpType})
log.Debugf("put shortpipe onto the result")
}
2021-02-03 06:11:47 +00:00
2021-05-21 04:18:24 +00:00
//traverseArrayCollect is a sneaky op that needs to be included too
2021-11-30 02:07:27 +00:00
//when closing a ]
2021-05-21 04:18:24 +00:00
if len(opStack) > 0 && opStack[len(opStack)-1].Operation != nil && opStack[len(opStack)-1].Operation.OperationType == traverseArrayOpType {
2021-11-30 02:07:27 +00:00
opStack[len(opStack)-1].Operation.Preferences = prefs
2021-05-21 04:18:24 +00:00
opStack, result = popOpToResult(opStack, result)
}
2021-01-11 22:55:55 +00:00
case closeBracket:
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
missingClosingTokenErr := validateNoOpenTokens(opStack[len(opStack)-1])
if missingClosingTokenErr != nil {
return nil, missingClosingTokenErr
}
2020-09-20 12:40:09 +00:00
opStack, result = popOpToResult(opStack, result)
}
if len(opStack) == 0 {
return nil, errors.New("Bad path expression, got close brackets without matching opening bracket")
}
// now we should have ( as the last element on the opStack, get rid of it
opStack = opStack[0 : len(opStack)-1]
2020-10-11 00:24:22 +00:00
2020-10-10 04:24:37 +00:00
default:
var currentPrecedence = currentToken.Operation.OperationType.Precedence
2020-10-10 04:24:37 +00:00
// pop off higher precedent operators onto the result
2020-10-20 04:33:20 +00:00
for len(opStack) > 0 &&
2021-01-11 22:55:55 +00:00
opStack[len(opStack)-1].TokenType == operationToken &&
2021-02-03 06:11:47 +00:00
opStack[len(opStack)-1].Operation.OperationType.Precedence > currentPrecedence {
2020-10-10 04:24:37 +00:00
opStack, result = popOpToResult(opStack, result)
}
// add this operator to the opStack
opStack = append(opStack, currentToken)
2021-02-03 06:11:47 +00:00
log.Debugf("put %v onto the opstack", currentToken.toString(true))
2020-09-20 12:40:09 +00:00
}
}
2020-10-17 11:10:47 +00:00
log.Debugf("opstackLen: %v", len(opStack))
2020-10-17 11:10:47 +00:00
if log.IsEnabledFor(logging.DEBUG) {
log.Debugf("PostFix Result:")
for _, currentToken := range result {
log.Debugf("> %v", currentToken.toString())
2020-10-17 11:10:47 +00:00
}
}
2020-09-20 12:40:09 +00:00
return result, nil
}