1// Use the top level Evaluator or StreamEvaluator to evaluate expressions and return matches. 2// 3package yqlib 4 5import ( 6 "bytes" 7 "container/list" 8 "fmt" 9 10 logging "gopkg.in/op/go-logging.v1" 11 yaml "gopkg.in/yaml.v3" 12) 13 14var log = logging.MustGetLogger("yq-lib") 15 16type operationType struct { 17 Type string 18 NumArgs uint // number of arguments to the op 19 Precedence uint 20 Handler operatorHandler 21} 22 23// operators TODO: 24// - mergeEmpty (sets only if the document is empty, do I do that now?) 25 26var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator} 27var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator} 28var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator} 29 30var blockOpType = &operationType{Type: "BLOCK", Precedence: 10, NumArgs: 2, Handler: emptyOperator} 31 32var unionOpType = &operationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: unionOperator} 33 34var pipeOpType = &operationType{Type: "PIPE", NumArgs: 2, Precedence: 30, Handler: pipeOperator} 35 36var assignOpType = &operationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: assignUpdateOperator} 37var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: addAssignOperator} 38var subtractAssignOpType = &operationType{Type: "SUBTRACT_ASSIGN", NumArgs: 2, Precedence: 40, Handler: subtractAssignOperator} 39 40var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator} 41var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator} 42var assignVariableOpType = &operationType{Type: "ASSIGN_VARIABLE", NumArgs: 2, Precedence: 40, Handler: assignVariableOperator} 43var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator} 44var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator} 45var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator} 46var assignAliasOpType = &operationType{Type: "ASSIGN_ALIAS", NumArgs: 2, Precedence: 40, Handler: assignAliasOperator} 47 48var multiplyOpType = &operationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 42, Handler: multiplyOperator} 49var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler: addOperator} 50var subtractOpType = &operationType{Type: "SUBTRACT", NumArgs: 2, Precedence: 42, Handler: subtractOperator} 51var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator} 52 53var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator} 54var notEqualsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: notEqualsOperator} 55 56//createmap needs to be above union, as we use union to build the components of the objects 57var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 15, Handler: createMapOperator} 58 59var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator} 60 61var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator} 62var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator} 63var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator} 64var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator} 65var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator} 66var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator} 67var getCommentOpType = &operationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: getCommentsOperator} 68var getAnchorOpType = &operationType{Type: "GET_ANCHOR", NumArgs: 0, Precedence: 50, Handler: getAnchorOperator} 69var getAliasOptype = &operationType{Type: "GET_ALIAS", NumArgs: 0, Precedence: 50, Handler: getAliasOperator} 70var getDocumentIndexOpType = &operationType{Type: "GET_DOCUMENT_INDEX", NumArgs: 0, Precedence: 50, Handler: getDocumentIndexOperator} 71var getFilenameOpType = &operationType{Type: "GET_FILENAME", NumArgs: 0, Precedence: 50, Handler: getFilenameOperator} 72var getFileIndexOpType = &operationType{Type: "GET_FILE_INDEX", NumArgs: 0, Precedence: 50, Handler: getFileIndexOperator} 73var getPathOpType = &operationType{Type: "GET_PATH", NumArgs: 0, Precedence: 50, Handler: getPathOperator} 74 75var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: explodeOperator} 76var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator} 77var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator} 78var subStringOpType = &operationType{Type: "SUBSTR", NumArgs: 1, Precedence: 50, Handler: substituteStringOperator} 79var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator} 80 81var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator} 82 83var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator} 84var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 55, Handler: traversePathOperator} 85var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Precedence: 50, Handler: traverseArrayOperator} 86 87var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 55, Handler: selfOperator} 88var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator} 89var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator} 90var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator} 91var emptyOpType = &operationType{Type: "EMPTY", Precedence: 50, Handler: emptyOperator} 92 93var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator} 94 95var selectOpType = &operationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: selectOperator} 96var hasOpType = &operationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: hasOperator} 97var deleteChildOpType = &operationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: deleteChildOperator} 98var deleteImmediateChildOpType = &operationType{Type: "DELETE_IMMEDIATE_CHILD", NumArgs: 1, Precedence: 40, Handler: deleteImmediateChildOperator} 99 100type Operation struct { 101 OperationType *operationType 102 Value interface{} 103 StringValue string 104 CandidateNode *CandidateNode // used for Value Path elements 105 Preferences interface{} 106 UpdateAssign bool // used for assign ops, when true it means we evaluate the rhs given the lhs 107} 108 109func createValueOperation(value interface{}, stringValue string) *Operation { 110 var node yaml.Node = yaml.Node{Kind: yaml.ScalarNode} 111 node.Value = stringValue 112 113 switch value.(type) { 114 case float32, float64: 115 node.Tag = "!!float" 116 case int, int64, int32: 117 node.Tag = "!!int" 118 case bool: 119 node.Tag = "!!bool" 120 case string: 121 node.Tag = "!!str" 122 case nil: 123 node.Tag = "!!null" 124 } 125 126 return &Operation{ 127 OperationType: valueOpType, 128 Value: value, 129 StringValue: stringValue, 130 CandidateNode: &CandidateNode{Node: &node}, 131 } 132} 133 134// debugging purposes only 135func (p *Operation) toString() string { 136 if p.OperationType == traversePathOpType { 137 return fmt.Sprintf("%v", p.Value) 138 } else if p.OperationType == selfReferenceOpType { 139 return "SELF" 140 } else if p.OperationType == valueOpType { 141 return fmt.Sprintf("%v (%T)", p.Value, p.Value) 142 } else { 143 return fmt.Sprintf("%v", p.OperationType.Type) 144 } 145} 146 147//use for debugging only 148func NodesToString(collection *list.List) string { 149 if !log.IsEnabledFor(logging.DEBUG) { 150 return "" 151 } 152 153 result := "" 154 for el := collection.Front(); el != nil; el = el.Next() { 155 result = result + "\n" + NodeToString(el.Value.(*CandidateNode)) 156 } 157 return result 158} 159 160func NodeToString(node *CandidateNode) string { 161 if !log.IsEnabledFor(logging.DEBUG) { 162 return "" 163 } 164 value := node.Node 165 if value == nil { 166 return "-- nil --" 167 } 168 buf := new(bytes.Buffer) 169 encoder := yaml.NewEncoder(buf) 170 errorEncoding := encoder.Encode(value) 171 if errorEncoding != nil { 172 log.Error("Error debugging node, %v", errorEncoding.Error()) 173 } 174 encoder.Close() 175 tag := value.Tag 176 if value.Kind == yaml.DocumentNode { 177 tag = "doc" 178 } else if value.Kind == yaml.AliasNode { 179 tag = "alias" 180 } 181 return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, tag, buf.String()) 182} 183 184func KindString(kind yaml.Kind) string { 185 switch kind { 186 case yaml.ScalarNode: 187 return "ScalarNode" 188 case yaml.SequenceNode: 189 return "SequenceNode" 190 case yaml.MappingNode: 191 return "MappingNode" 192 case yaml.DocumentNode: 193 return "DocumentNode" 194 case yaml.AliasNode: 195 return "AliasNode" 196 default: 197 return "unknown!" 198 } 199} 200