yq/pkg/yqlib/treeops/path_postfix.go

147 lines
3.5 KiB
Go
Raw Normal View History

2020-10-08 23:59:03 +00:00
package treeops
2020-09-20 12:40:09 +00:00
import (
"errors"
"fmt"
)
var precedenceMap map[int]int
type PathElementType uint32
const (
PathKey PathElementType = 1 << iota
ArrayIndex
Operation
2020-10-11 00:24:22 +00:00
SelfReference
OpenBracket
CloseBracket
2020-09-20 12:40:09 +00:00
)
type OperationType uint32
const (
None OperationType = 1 << iota
2020-09-24 03:20:02 +00:00
Traverse
2020-09-20 12:40:09 +00:00
Or
And
Equals
2020-10-10 04:24:37 +00:00
Assign
2020-10-10 11:42:09 +00:00
DeleteChild
2020-09-20 12:40:09 +00:00
)
type PathElement struct {
PathElementType PathElementType
OperationType OperationType
Value interface{}
2020-10-09 04:05:45 +00:00
StringValue string
2020-09-20 12:40:09 +00:00
}
// debugging purposes only
func (p *PathElement) toString() string {
2020-09-24 03:20:02 +00:00
var result string = ``
2020-09-20 12:40:09 +00:00
switch p.PathElementType {
case PathKey:
2020-09-24 03:20:02 +00:00
result = result + fmt.Sprintf("PathKey - '%v'\n", p.Value)
2020-09-20 12:40:09 +00:00
case ArrayIndex:
2020-09-24 03:20:02 +00:00
result = result + fmt.Sprintf("ArrayIndex - '%v'\n", p.Value)
2020-10-11 00:24:22 +00:00
case SelfReference:
result = result + fmt.Sprintf("SELF\n")
2020-09-20 12:40:09 +00:00
case Operation:
result = result + "Operation - "
switch p.OperationType {
case Or:
result = result + "OR\n"
case And:
result = result + "AND\n"
case Equals:
result = result + "EQUALS\n"
2020-10-10 04:24:37 +00:00
case Assign:
result = result + "ASSIGN\n"
2020-09-24 03:20:02 +00:00
case Traverse:
result = result + "TRAVERSE\n"
2020-10-10 11:42:09 +00:00
case DeleteChild:
result = result + "DELETE CHILD\n"
2020-09-20 12:40:09 +00:00
}
2020-09-24 03:20:02 +00:00
2020-09-20 12:40:09 +00:00
}
return result
}
2020-10-11 00:24:22 +00:00
func createOperationPathElement(opToken *Token) PathElement {
var pathElement = PathElement{PathElementType: Operation, OperationType: opToken.OperationType}
2020-09-20 12:40:09 +00:00
return pathElement
}
type PathPostFixer interface {
2020-10-11 00:24:22 +00:00
ConvertToPostfix([]*Token) ([]*PathElement, error)
2020-09-20 12:40:09 +00:00
}
type pathPostFixer struct {
}
func NewPathPostFixer() PathPostFixer {
return &pathPostFixer{}
}
2020-10-11 00:24:22 +00:00
func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) {
var operatorToPushToPostFix *Token
2020-09-20 12:40:09 +00:00
opStack, operatorToPushToPostFix = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
var pathElement = createOperationPathElement(operatorToPushToPostFix)
return opStack, append(result, &pathElement)
}
2020-10-11 00:24:22 +00:00
func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, error) {
2020-09-20 12:40:09 +00:00
var result []*PathElement
// surround the whole thing with quotes
2020-10-11 00:24:22 +00:00
var opStack = []*Token{&Token{PathElementType: OpenBracket}}
var tokens = append(infixTokens, &Token{PathElementType: CloseBracket})
2020-09-20 12:40:09 +00:00
for _, token := range tokens {
2020-10-11 00:24:22 +00:00
switch token.PathElementType {
case PathKey, ArrayIndex, SelfReference:
var pathElement = PathElement{PathElementType: token.PathElementType, Value: token.Value, StringValue: token.StringValue}
2020-09-24 03:20:02 +00:00
result = append(result, &pathElement)
2020-10-11 00:24:22 +00:00
case OpenBracket:
2020-09-20 12:40:09 +00:00
opStack = append(opStack, token)
2020-10-11 00:24:22 +00:00
case CloseBracket:
for len(opStack) > 0 && opStack[len(opStack)-1].PathElementType != OpenBracket {
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:
2020-10-11 00:24:22 +00:00
var currentPrecedence = p.precendenceOf(token)
2020-10-10 04:24:37 +00:00
// pop off higher precedent operators onto the result
2020-10-11 00:24:22 +00:00
for len(opStack) > 0 && p.precendenceOf(opStack[len(opStack)-1]) >= currentPrecedence {
2020-10-10 04:24:37 +00:00
opStack, result = popOpToResult(opStack, result)
}
// add this operator to the opStack
opStack = append(opStack, token)
2020-09-20 12:40:09 +00:00
}
}
return result, nil
}
2020-10-11 00:24:22 +00:00
func (p *pathPostFixer) precendenceOf(token *Token) int {
switch token.OperationType {
case Or:
return 10
case And:
return 20
case Equals, DeleteChild:
return 30
case Assign:
return 35
case Traverse:
return 40
}
return 0
}