yq/pkg/yqlib/lib.go

374 lines
15 KiB
Go
Raw Normal View History

2021-01-12 22:35:57 +00:00
// Use the top level Evaluator or StreamEvaluator to evaluate expressions and return matches.
//
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"
2021-09-02 05:26:44 +00:00
"strconv"
"strings"
2020-10-08 23:59:03 +00:00
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
)
2022-02-01 03:47:51 +00:00
var ExpressionParser ExpressionParserInterface
func InitExpressionParser() {
if ExpressionParser == nil {
ExpressionParser = newExpressionParser()
}
}
2021-12-21 05:52:54 +00:00
type xmlPreferences struct {
AttributePrefix string
ContentName string
}
2020-11-30 05:05:07 +00:00
var log = logging.MustGetLogger("yq-lib")
var PrettyPrintExp = `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`
// GetLogger returns the yq logger instance.
func GetLogger() *logging.Logger {
return log
}
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
}
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
2021-02-15 06:31:12 +00:00
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}
2021-02-15 03:27:00 +00:00
var blockOpType = &operationType{Type: "BLOCK", Precedence: 10, NumArgs: 2, Handler: emptyOperator}
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}
2021-03-24 21:12:01 +00:00
var subtractAssignOpType = &operationType{Type: "SUBTRACT_ASSIGN", NumArgs: 2, Precedence: 40, Handler: subtractAssignOperator}
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}
2021-02-03 04:51:26 +00:00
var assignVariableOpType = &operationType{Type: "ASSIGN_VARIABLE", NumArgs: 2, Precedence: 40, Handler: assignVariableOperator}
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: 42, Handler: multiplyOperator}
2022-01-22 05:40:17 +00:00
var multiplyAssignOpType = &operationType{Type: "MULTIPLY_ASSIGN", NumArgs: 2, Precedence: 42, Handler: multiplyAssignOperator}
var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler: addOperator}
2021-03-24 21:12:01 +00:00
var subtractOpType = &operationType{Type: "SUBTRACT", NumArgs: 2, Precedence: 42, Handler: subtractOperator}
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator}
2020-10-18 21:36:33 +00:00
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
2021-02-04 22:54:03 +00:00
var notEqualsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: notEqualsOperator}
//createmap needs to be above union, as we use union to build the components of the objects
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 15, 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: 1, Precedence: 50, Handler: collectOperator}
2021-11-30 23:32:36 +00:00
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
2022-02-01 03:47:51 +00:00
var evalOpType = &operationType{Type: "EVAL", NumArgs: 1, Precedence: 50, Handler: evalOperator}
2021-11-30 23:32:36 +00:00
var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 50, Handler: mapValuesOperator}
2021-10-22 01:37:47 +00:00
var encodeOpType = &operationType{Type: "ENCODE", NumArgs: 0, Precedence: 50, Handler: encodeOperator}
var decodeOpType = &operationType{Type: "DECODE", NumArgs: 0, Precedence: 50, Handler: decodeOperator}
2021-05-09 05:12:50 +00:00
2021-05-14 04:29:55 +00:00
var anyOpType = &operationType{Type: "ANY", NumArgs: 0, Precedence: 50, Handler: anyOperator}
var allOpType = &operationType{Type: "ALL", NumArgs: 0, Precedence: 50, Handler: allOperator}
2021-09-15 05:18:10 +00:00
var containsOpType = &operationType{Type: "CONTAINS", NumArgs: 1, Precedence: 50, Handler: containsOperator}
2021-05-14 05:01:44 +00:00
var anyConditionOpType = &operationType{Type: "ANY_CONDITION", NumArgs: 1, Precedence: 50, Handler: anyOperator}
var allConditionOpType = &operationType{Type: "ALL_CONDITION", NumArgs: 1, Precedence: 50, Handler: allOperator}
2021-05-14 04:29:55 +00:00
2021-05-09 03:59:23 +00:00
var toEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence: 50, Handler: toEntriesOperator}
2021-05-09 05:12:50 +00:00
var fromEntriesOpType = &operationType{Type: "FROM_ENTRIES", NumArgs: 0, Precedence: 50, Handler: fromEntriesOperator}
var withEntriesOpType = &operationType{Type: "WITH_ENTRIES", NumArgs: 1, Precedence: 50, Handler: withEntriesOperator}
2021-09-12 11:52:02 +00:00
var withOpType = &operationType{Type: "WITH", NumArgs: 1, Precedence: 50, Handler: withOperator}
2021-01-14 03:25:31 +00:00
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
2021-02-04 01:39:04 +00:00
var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator}
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator}
2021-11-23 22:57:35 +00:00
var getKeyOpType = &operationType{Type: "GET_KEY", NumArgs: 0, Precedence: 50, Handler: getKeyOperator}
2021-11-23 23:16:48 +00:00
var getParentOpType = &operationType{Type: "GET_PARENT", NumArgs: 0, Precedence: 50, Handler: getParentOperator}
2021-11-23 22:57:35 +00:00
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}
2021-11-28 02:25:22 +00:00
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
2021-12-04 02:54:12 +00:00
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}
var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}
2021-01-14 03:46:50 +00:00
var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator}
2021-04-15 00:09:41 +00:00
var subStringOpType = &operationType{Type: "SUBSTR", NumArgs: 1, Precedence: 50, Handler: substituteStringOperator}
2021-07-07 12:40:46 +00:00
var matchOpType = &operationType{Type: "MATCH", NumArgs: 1, Precedence: 50, Handler: matchOperator}
2021-07-11 01:08:18 +00:00
var captureOpType = &operationType{Type: "CAPTURE", NumArgs: 1, Precedence: 50, Handler: captureOperator}
var testOpType = &operationType{Type: "TEST", NumArgs: 1, Precedence: 50, Handler: testOperator}
2021-01-14 04:05:50 +00:00
var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator}
2020-11-02 00:20:38 +00:00
2021-11-16 04:29:16 +00:00
var loadOpType = &operationType{Type: "LOAD", NumArgs: 1, Precedence: 50, Handler: loadYamlOperator}
2021-01-14 04:45:07 +00:00
var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator}
var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator}
2021-02-04 01:39:04 +00:00
var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 55, Handler: traversePathOperator}
2021-02-03 04:51:26 +00:00
var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Precedence: 50, Handler: traverseArrayOperator}
2020-10-20 02:53:26 +00:00
2021-02-04 01:39:04 +00:00
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 55, 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}
2021-02-15 03:27:00 +00:00
var emptyOpType = &operationType{Type: "EMPTY", Precedence: 50, Handler: emptyOperator}
2020-10-20 02:53:26 +00:00
var envsubstOpType = &operationType{Type: "ENVSUBST", NumArgs: 0, Precedence: 50, Handler: envsubstOperator}
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}
2021-05-13 23:43:52 +00:00
var uniqueOpType = &operationType{Type: "UNIQUE", NumArgs: 0, Precedence: 50, Handler: unique}
var uniqueByOpType = &operationType{Type: "UNIQUE_BY", NumArgs: 1, Precedence: 50, Handler: uniqueBy}
2021-10-26 04:07:50 +00:00
var groupByOpType = &operationType{Type: "GROUP_BY", NumArgs: 1, Precedence: 50, Handler: groupBy}
2021-10-26 04:42:25 +00:00
var flattenOpType = &operationType{Type: "FLATTEN_BY", NumArgs: 0, Precedence: 50, Handler: flattenOp}
var deleteChildOpType = &operationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: deleteChildOperator}
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
}
2021-09-07 06:58:34 +00:00
func recurseNodeArrayEqual(lhs *yaml.Node, rhs *yaml.Node) bool {
if len(lhs.Content) != len(rhs.Content) {
return false
}
for index := 0; index < len(lhs.Content); index = index + 1 {
if !recursiveNodeEqual(lhs.Content[index], rhs.Content[index]) {
return false
}
}
return true
}
func findInArray(array *yaml.Node, item *yaml.Node) int {
for index := 0; index < len(array.Content); index = index + 1 {
if recursiveNodeEqual(array.Content[index], item) {
return index
}
}
return -1
}
func recurseNodeObjectEqual(lhs *yaml.Node, rhs *yaml.Node) bool {
if len(lhs.Content) != len(rhs.Content) {
return false
}
for index := 0; index < len(lhs.Content); index = index + 2 {
key := lhs.Content[index]
value := lhs.Content[index+1]
indexInRhs := findInArray(rhs, key)
if indexInRhs == -1 || !recursiveNodeEqual(value, rhs.Content[indexInRhs+1]) {
return false
}
}
return true
}
func recursiveNodeEqual(lhs *yaml.Node, rhs *yaml.Node) bool {
2022-01-22 02:47:22 +00:00
if lhs.Kind != rhs.Kind {
2021-09-07 06:58:34 +00:00
return false
2022-01-22 02:47:22 +00:00
}
if lhs.Kind == yaml.ScalarNode {
//process custom tags of scalar nodes.
//dont worry about matching tags of maps or arrays.
lhsTag := lhs.Tag
rhsTag := rhs.Tag
if !strings.HasPrefix(lhsTag, "!!") {
// custom tag - we have to have a guess
lhsTag = guessTagFromCustomType(lhs)
}
if !strings.HasPrefix(rhsTag, "!!") {
// custom tag - we have to have a guess
rhsTag = guessTagFromCustomType(rhs)
}
if lhsTag != rhsTag {
return false
}
}
if lhs.Tag == "!!null" {
2021-09-07 06:58:34 +00:00
return true
} else if lhs.Kind == yaml.ScalarNode {
return lhs.Value == rhs.Value
} else if lhs.Kind == yaml.SequenceNode {
return recurseNodeArrayEqual(lhs, rhs)
} else if lhs.Kind == yaml.MappingNode {
return recurseNodeObjectEqual(lhs, rhs)
}
return false
}
2021-09-02 05:26:44 +00:00
// yaml numbers can be hex encoded...
func parseInt(numberString string) (string, int64, error) {
if strings.HasPrefix(numberString, "0x") ||
strings.HasPrefix(numberString, "0X") {
num, err := strconv.ParseInt(numberString[2:], 16, 64)
2021-09-02 05:26:44 +00:00
return "0x%X", num, err
}
num, err := strconv.ParseInt(numberString, 10, 64)
2021-09-02 05:26:44 +00:00
return "%v", num, err
}
2021-07-07 12:40:46 +00:00
func createScalarNode(value interface{}, stringValue string) *yaml.Node {
var node = &yaml.Node{Kind: yaml.ScalarNode}
2020-10-20 04:33:20 +00:00
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
}
2021-07-07 12:40:46 +00:00
return node
}
func headAndLineComment(node *yaml.Node) string {
return headComment(node) + lineComment(node)
}
func headComment(node *yaml.Node) string {
return strings.Replace(node.HeadComment, "#", "", 1)
}
func lineComment(node *yaml.Node) string {
return strings.Replace(node.LineComment, "#", "", 1)
}
func footComment(node *yaml.Node) string {
return strings.Replace(node.FootComment, "#", "", 1)
}
2021-07-07 12:40:46 +00:00
func createValueOperation(value interface{}, stringValue string) *Operation {
var node *yaml.Node = createScalarNode(value, stringValue)
2020-10-20 04:33:20 +00:00
return &Operation{
OperationType: valueOpType,
2020-10-20 04:33:20 +00:00
Value: value,
StringValue: stringValue,
2021-07-07 12:40:46 +00:00
CandidateNode: &CandidateNode{Node: node},
2020-10-20 04:33:20 +00:00
}
}
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 == nil {
return "OP IS NIL"
}
if p.OperationType == traversePathOpType {
2020-10-20 02:53:26 +00:00
return fmt.Sprintf("%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 ""
}
2021-09-05 01:07:40 +00:00
result := fmt.Sprintf("%v results\n", collection.Len())
2020-10-10 11:42:09 +00:00
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())
}
errorClosingEncoder := encoder.Close()
if errorClosingEncoder != nil {
log.Error("Error closing encoder: ", errorClosingEncoder.Error())
}
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
}
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!"
}
}