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-10-11 23:44:33 +00:00
|
|
|
type OperationType struct {
|
|
|
|
Type string
|
|
|
|
NumArgs uint // number of arguments to the op
|
|
|
|
Precedence uint
|
|
|
|
Handler OperatorHandler
|
|
|
|
}
|
|
|
|
|
2020-10-30 01:40:44 +00:00
|
|
|
// operators TODO:
|
2020-11-16 01:09:57 +00:00
|
|
|
// - add print test, particular for streams with multilpe docs
|
|
|
|
// (one that print doc1, then doc 3) in two calls
|
|
|
|
// (one that prints doc1, then doc 3) in one call
|
2020-11-06 03:37:01 +00:00
|
|
|
// - write in place
|
|
|
|
// - get path operator (like doc index)
|
|
|
|
// - get file index op (like doc index)
|
|
|
|
// - get file name op (like doc index)
|
2020-10-30 01:40:44 +00:00
|
|
|
// - mergeAppend (merges and appends arrays)
|
2020-11-02 00:20:38 +00:00
|
|
|
// - mergeEmpty (sets only if the document is empty, do I do that now?)
|
|
|
|
// - updateTag - not recursive
|
2020-11-03 23:48:43 +00:00
|
|
|
// - get tag (tag)
|
2020-10-30 01:40:44 +00:00
|
|
|
// - compare ??
|
|
|
|
// - validate ??
|
2020-11-02 00:20:38 +00:00
|
|
|
// - exists
|
2020-10-30 01:40:44 +00:00
|
|
|
|
2020-10-17 11:10:47 +00:00
|
|
|
var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOperator}
|
|
|
|
var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: AndOperator}
|
|
|
|
|
|
|
|
var Union = &OperationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: UnionOperator}
|
2020-10-16 01:29:26 +00:00
|
|
|
|
2020-11-02 00:20:38 +00:00
|
|
|
var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator}
|
2020-10-19 05:14:29 +00:00
|
|
|
var AssignAttributes = &OperationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: AssignAttributesOperator}
|
2020-11-02 00:20:38 +00:00
|
|
|
var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: AssignStyleOperator}
|
2020-11-06 00:23:26 +00:00
|
|
|
var AssignComment = &OperationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: AssignCommentsOperator}
|
2020-11-02 00:20:38 +00:00
|
|
|
|
2020-10-18 21:36:33 +00:00
|
|
|
var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 40, Handler: MultiplyOperator}
|
|
|
|
|
2020-10-16 01:29:26 +00:00
|
|
|
var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator}
|
2020-10-21 01:54:58 +00:00
|
|
|
var CreateMap = &OperationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: CreateMapOperator}
|
2020-10-16 01:29:26 +00:00
|
|
|
var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: PipeOperator}
|
|
|
|
|
|
|
|
var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator}
|
2020-10-17 11:10:47 +00:00
|
|
|
var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: CollectOperator}
|
2020-11-02 00:20:38 +00:00
|
|
|
var GetStyle = &OperationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: GetStyleOperator}
|
2020-11-06 00:45:18 +00:00
|
|
|
var GetComment = &OperationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: GetCommentsOperator}
|
2020-11-06 01:11:38 +00:00
|
|
|
var GetDocumentIndex = &OperationType{Type: "GET_DOCUMENT_INDEX", NumArgs: 0, Precedence: 50, Handler: GetDocumentIndexOperator}
|
2020-11-02 00:20:38 +00:00
|
|
|
|
|
|
|
var Explode = &OperationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: ExplodeOperator}
|
|
|
|
|
2020-10-21 01:54:58 +00:00
|
|
|
var CollectObject = &OperationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: CollectObjectOperator}
|
2020-10-20 02:53:26 +00:00
|
|
|
var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
|
|
|
|
|
|
|
|
var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
|
2020-10-20 04:33:20 +00:00
|
|
|
var SelfReference = &OperationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: SelfOperator}
|
|
|
|
var ValueOp = &OperationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: ValueOperator}
|
2020-10-20 05:27:30 +00:00
|
|
|
var Not = &OperationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: NotOperator}
|
2020-11-13 02:19:54 +00:00
|
|
|
var Empty = &OperationType{Type: "EMPTY", NumArgs: 50, Handler: EmptyOperator}
|
2020-10-20 02:53:26 +00:00
|
|
|
|
2020-10-18 00:31:36 +00:00
|
|
|
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
2020-10-11 23:44:33 +00:00
|
|
|
|
2020-10-16 01:29:26 +00:00
|
|
|
// not sure yet
|
|
|
|
|
2020-10-17 11:10:47 +00:00
|
|
|
var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: SelectOperator}
|
|
|
|
|
2020-11-14 02:38:44 +00:00
|
|
|
var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: DeleteChildOperator}
|
2020-10-12 01:24:59 +00:00
|
|
|
|
|
|
|
// var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35}
|
|
|
|
// filters matches if they have the existing path
|
2020-10-11 23:44:33 +00:00
|
|
|
|
2020-10-20 04:33:20 +00:00
|
|
|
type Operation struct {
|
2020-10-20 02:53:26 +00:00
|
|
|
OperationType *OperationType
|
|
|
|
Value interface{}
|
|
|
|
StringValue string
|
|
|
|
CandidateNode *CandidateNode // used for Value Path elements
|
2020-10-29 23:56:45 +00:00
|
|
|
Preferences interface{}
|
2020-10-11 23:44:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-20 04:33:20 +00:00
|
|
|
func CreateValueOperation(value interface{}, stringValue string) *Operation {
|
|
|
|
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: ValueOp,
|
|
|
|
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 {
|
2020-10-20 02:53:26 +00:00
|
|
|
if p.OperationType == TraversePath {
|
|
|
|
return fmt.Sprintf("%v", p.Value)
|
|
|
|
} else if p.OperationType == DocumentFilter {
|
|
|
|
return fmt.Sprintf("d%v", p.Value)
|
|
|
|
} else if p.OperationType == SelfReference {
|
|
|
|
return "SELF"
|
|
|
|
} else if p.OperationType == ValueOp {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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!"
|
|
|
|
}
|
|
|
|
}
|