yq/pkg/yqlib/lib.go

203 lines
8.5 KiB
Go
Raw Normal View History

2020-11-03 23:48:43 +00:00
package yqlib
2020-10-08 23:59:03 +00:00
import (
"bytes"
2020-10-21 01:54:58 +00:00
"container/list"
2020-10-08 23:59:03 +00:00
"fmt"
2020-11-14 02:38:44 +00:00
logging "gopkg.in/op/go-logging.v1"
yaml "gopkg.in/yaml.v3"
2020-10-08 23:59:03 +00:00
)
2020-11-30 05:05:07 +00:00
var log = logging.MustGetLogger("yq-lib")
type operationType struct {
2020-10-11 23:44:33 +00:00
Type string
NumArgs uint // number of arguments to the op
Precedence uint
Handler operatorHandler
2020-10-11 23:44:33 +00:00
}
2020-10-30 01:40:44 +00:00
// operators TODO:
2020-11-02 00:20:38 +00:00
// - mergeEmpty (sets only if the document is empty, do I do that now?)
2020-10-30 01:40:44 +00:00
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
2020-10-17 11:10:47 +00:00
var unionOpType = &operationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: unionOperator}
2020-10-16 01:29:26 +00:00
var pipeOpType = &operationType{Type: "PIPE", NumArgs: 2, Precedence: 30, Handler: pipeOperator}
var assignOpType = &operationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: assignUpdateOperator}
var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: addAssignOperator}
2020-11-19 05:45:05 +00:00
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator}
var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator}
var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator}
var assignAliasOpType = &operationType{Type: "ASSIGN_ALIAS", NumArgs: 2, Precedence: 40, Handler: assignAliasOperator}
2020-11-02 00:20:38 +00:00
var multiplyOpType = &operationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 45, Handler: multiplyOperator}
var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 45, Handler: addOperator}
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 45, Handler: alternativeOperator}
2020-10-18 21:36:33 +00:00
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: createMapOperator}
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
2020-10-16 01:29:26 +00:00
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator}
var getCommentOpType = &operationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: getCommentsOperator}
var getAnchorOpType = &operationType{Type: "GET_ANCHOR", NumArgs: 0, Precedence: 50, Handler: getAnchorOperator}
var getAliasOptype = &operationType{Type: "GET_ALIAS", NumArgs: 0, Precedence: 50, Handler: getAliasOperator}
var getDocumentIndexOpType = &operationType{Type: "GET_DOCUMENT_INDEX", NumArgs: 0, Precedence: 50, Handler: getDocumentIndexOperator}
var getFilenameOpType = &operationType{Type: "GET_FILENAME", NumArgs: 0, Precedence: 50, Handler: getFilenameOperator}
var getFileIndexOpType = &operationType{Type: "GET_FILE_INDEX", NumArgs: 0, Precedence: 50, Handler: getFileIndexOperator}
var getPathOpType = &operationType{Type: "GET_PATH", NumArgs: 0, Precedence: 50, Handler: getPathOperator}
2020-11-02 00:20:38 +00:00
var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: explodeOperator}
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
2020-11-02 00:20:38 +00:00
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 traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 1, Precedence: 50, Handler: traverseArrayOperator}
2020-10-20 02:53:26 +00:00
var documentFilterOpType = &operationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: traversePathOperator}
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 50, 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}
var emptyOpType = &operationType{Type: "EMPTY", NumArgs: 50, Handler: emptyOperator}
2020-10-20 02:53:26 +00:00
var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator}
2020-10-11 23:44:33 +00:00
var selectOpType = &operationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: selectOperator}
var hasOpType = &operationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: hasOperator}
var deleteChildOpType = &operationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: deleteChildOperator}
var deleteImmediateChildOpType = &operationType{Type: "DELETE_IMMEDIATE_CHILD", NumArgs: 1, Precedence: 40, Handler: deleteImmediateChildOperator}
2020-10-12 01:24:59 +00:00
2020-10-20 04:33:20 +00:00
type Operation struct {
OperationType *operationType
2020-10-20 02:53:26 +00:00
Value interface{}
StringValue string
CandidateNode *CandidateNode // used for Value Path elements
2020-10-29 23:56:45 +00:00
Preferences interface{}
2021-01-11 22:55:55 +00:00
UpdateAssign bool // used for assign ops, when true it means we evaluate the rhs given the lhs
2020-10-11 23:44:33 +00:00
}
func createValueOperation(value interface{}, stringValue string) *Operation {
2020-10-20 04:33:20 +00:00
var node yaml.Node = yaml.Node{Kind: yaml.ScalarNode}
node.Value = stringValue
switch value.(type) {
case float32, float64:
node.Tag = "!!float"
case int, int64, int32:
node.Tag = "!!int"
case bool:
node.Tag = "!!bool"
case string:
node.Tag = "!!str"
2020-10-20 04:40:11 +00:00
case nil:
node.Tag = "!!null"
2020-10-20 04:33:20 +00:00
}
return &Operation{
OperationType: valueOpType,
2020-10-20 04:33:20 +00:00
Value: value,
StringValue: stringValue,
CandidateNode: &CandidateNode{Node: &node},
}
}
2020-10-11 23:44:33 +00:00
// debugging purposes only
2020-10-20 04:33:20 +00:00
func (p *Operation) toString() string {
if p.OperationType == traversePathOpType {
2020-10-20 02:53:26 +00:00
return fmt.Sprintf("%v", p.Value)
} else if p.OperationType == documentFilterOpType {
2020-10-20 02:53:26 +00:00
return fmt.Sprintf("d%v", p.Value)
} else if p.OperationType == selfReferenceOpType {
2020-10-20 02:53:26 +00:00
return "SELF"
} else if p.OperationType == valueOpType {
2020-10-20 02:53:26 +00:00
return fmt.Sprintf("%v (%T)", p.Value, p.Value)
} else {
return fmt.Sprintf("%v", p.OperationType.Type)
2020-10-11 23:44:33 +00:00
}
}
2020-10-10 11:42:09 +00:00
//use for debugging only
2020-10-21 01:54:58 +00:00
func NodesToString(collection *list.List) string {
2020-10-10 11:42:09 +00:00
if !log.IsEnabledFor(logging.DEBUG) {
return ""
}
result := ""
for el := collection.Front(); el != nil; el = el.Next() {
result = result + "\n" + NodeToString(el.Value.(*CandidateNode))
}
return result
}
2020-10-08 23:59:03 +00:00
func NodeToString(node *CandidateNode) string {
if !log.IsEnabledFor(logging.DEBUG) {
return ""
}
value := node.Node
if value == nil {
2020-10-17 11:10:47 +00:00
return "-- nil --"
2020-10-08 23:59:03 +00:00
}
buf := new(bytes.Buffer)
encoder := yaml.NewEncoder(buf)
errorEncoding := encoder.Encode(value)
if errorEncoding != nil {
log.Error("Error debugging node, %v", errorEncoding.Error())
}
encoder.Close()
2020-10-27 05:45:16 +00:00
tag := value.Tag
if value.Kind == yaml.DocumentNode {
tag = "doc"
2020-10-29 23:56:45 +00:00
} else if value.Kind == yaml.AliasNode {
tag = "alias"
2020-10-27 05:45:16 +00:00
}
return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, tag, buf.String())
2020-10-08 23:59:03 +00:00
}
// EvaluateNodes takes an expression and one or more yaml nodes, returning a list of matching candidate nodes
func EvaluateNodes(expression string, nodes ...*yaml.Node) (*list.List, error) {
inputCandidates := list.New()
for _, node := range nodes {
inputCandidates.PushBack(&CandidateNode{Node: node})
}
return EvaluateCandidateNodes(expression, inputCandidates)
}
// EvaluateCandidateNodes takes an expression and list of candidate nodes, returning a list of matching candidate nodes
func EvaluateCandidateNodes(expression string, inputCandidates *list.List) (*list.List, error) {
node, err := treeCreator.ParsePath(expression)
if err != nil {
return nil, err
}
return treeNavigator.GetMatchingNodes(inputCandidates, node)
}
2020-10-08 23:59:03 +00:00
func KindString(kind yaml.Kind) string {
switch kind {
case yaml.ScalarNode:
return "ScalarNode"
case yaml.SequenceNode:
return "SequenceNode"
case yaml.MappingNode:
return "MappingNode"
case yaml.DocumentNode:
return "DocumentNode"
case yaml.AliasNode:
return "AliasNode"
default:
return "unknown!"
}
}