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 (
|
2020-10-21 01:54:58 +00:00
|
|
|
"container/list"
|
2020-10-08 23:59:03 +00:00
|
|
|
"fmt"
|
2022-05-06 03:46:14 +00:00
|
|
|
"math"
|
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"
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-30 05:05:07 +00:00
|
|
|
var log = logging.MustGetLogger("yq-lib")
|
|
|
|
|
2022-01-21 09:26:09 +00:00
|
|
|
var PrettyPrintExp = `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`
|
|
|
|
|
2021-11-29 23:51:49 +00:00
|
|
|
// GetLogger returns the yq logger instance.
|
|
|
|
func GetLogger() *logging.Logger {
|
|
|
|
return log
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func recurseNodeArrayEqual(lhs *CandidateNode, rhs *CandidateNode) bool {
|
2021-09-07 06:58:34 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func findInArray(array *CandidateNode, item *CandidateNode) int {
|
2021-09-07 06:58:34 +00:00
|
|
|
|
|
|
|
for index := 0; index < len(array.Content); index = index + 1 {
|
|
|
|
if recursiveNodeEqual(array.Content[index], item) {
|
|
|
|
return index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func findKeyInMap(dataMap *CandidateNode, item *CandidateNode) int {
|
2022-03-09 03:38:02 +00:00
|
|
|
|
2022-08-01 00:28:34 +00:00
|
|
|
for index := 0; index < len(dataMap.Content); index = index + 2 {
|
|
|
|
if recursiveNodeEqual(dataMap.Content[index], item) {
|
2022-03-09 03:38:02 +00:00
|
|
|
return index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func recurseNodeObjectEqual(lhs *CandidateNode, rhs *CandidateNode) bool {
|
2021-09-07 06:58:34 +00:00
|
|
|
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]
|
|
|
|
|
2022-02-07 00:55:55 +00:00
|
|
|
indexInRHS := findInArray(rhs, key)
|
2021-09-07 06:58:34 +00:00
|
|
|
|
2022-02-07 00:55:55 +00:00
|
|
|
if indexInRHS == -1 || !recursiveNodeEqual(value, rhs.Content[indexInRHS+1]) {
|
2021-09-07 06:58:34 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func parseSnippet(value string) (*CandidateNode, error) {
|
2022-10-30 05:29:42 +00:00
|
|
|
if value == "" {
|
2023-10-18 01:11:53 +00:00
|
|
|
return &CandidateNode{
|
|
|
|
Kind: ScalarNode,
|
2022-10-30 05:29:42 +00:00
|
|
|
Tag: "!!null",
|
|
|
|
}, nil
|
|
|
|
}
|
2022-10-28 03:16:46 +00:00
|
|
|
decoder := NewYamlDecoder(ConfiguredYamlPreferences)
|
|
|
|
err := decoder.Init(strings.NewReader(value))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-10-18 01:11:53 +00:00
|
|
|
result, err := decoder.Decode()
|
2022-11-09 10:36:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-03-11 23:22:35 +00:00
|
|
|
if result.Tag == "!!str" {
|
|
|
|
// use the original string value, as
|
|
|
|
// decoding drops new lines
|
|
|
|
return createScalarNode(value, value), nil
|
|
|
|
}
|
2022-11-15 00:35:31 +00:00
|
|
|
result.Line = 0
|
|
|
|
result.Column = 0
|
|
|
|
return result, err
|
2022-07-27 02:26:22 +00:00
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func recursiveNodeEqual(lhs *CandidateNode, rhs *CandidateNode) 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
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
if lhs.Kind == ScalarNode {
|
2022-01-22 02:47:22 +00:00
|
|
|
//process custom tags of scalar nodes.
|
|
|
|
//dont worry about matching tags of maps or arrays.
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
lhsTag := lhs.guessTagFromCustomType()
|
|
|
|
rhsTag := rhs.guessTagFromCustomType()
|
2022-01-22 02:47:22 +00:00
|
|
|
|
|
|
|
if lhsTag != rhsTag {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if lhs.Tag == "!!null" {
|
2021-09-07 06:58:34 +00:00
|
|
|
return true
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
} else if lhs.Kind == ScalarNode {
|
2021-09-07 06:58:34 +00:00
|
|
|
return lhs.Value == rhs.Value
|
2023-10-18 01:11:53 +00:00
|
|
|
} else if lhs.Kind == SequenceNode {
|
2021-09-07 06:58:34 +00:00
|
|
|
return recurseNodeArrayEqual(lhs, rhs)
|
2023-10-18 01:11:53 +00:00
|
|
|
} else if lhs.Kind == MappingNode {
|
2021-09-07 06:58:34 +00:00
|
|
|
return recurseNodeObjectEqual(lhs, rhs)
|
|
|
|
}
|
|
|
|
return false
|
2022-02-20 03:29:52 +00:00
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
// yaml numbers can be hex and octal encoded...
|
2022-05-06 03:46:14 +00:00
|
|
|
func parseInt64(numberString string) (string, int64, error) {
|
2021-09-02 05:26:44 +00:00
|
|
|
if strings.HasPrefix(numberString, "0x") ||
|
|
|
|
strings.HasPrefix(numberString, "0X") {
|
2021-12-20 22:30:08 +00:00
|
|
|
num, err := strconv.ParseInt(numberString[2:], 16, 64)
|
2021-09-02 05:26:44 +00:00
|
|
|
return "0x%X", num, err
|
2023-10-18 01:11:53 +00:00
|
|
|
} else if strings.HasPrefix(numberString, "0o") {
|
|
|
|
num, err := strconv.ParseInt(numberString[2:], 8, 64)
|
|
|
|
return "0o%o", num, err
|
2021-09-02 05:26:44 +00:00
|
|
|
}
|
2021-12-20 22:30:08 +00:00
|
|
|
num, err := strconv.ParseInt(numberString, 10, 64)
|
2021-09-02 05:26:44 +00:00
|
|
|
return "%v", num, err
|
|
|
|
}
|
|
|
|
|
2022-05-22 11:19:59 +00:00
|
|
|
func parseInt(numberString string) (int, error) {
|
2023-10-18 01:11:53 +00:00
|
|
|
_, parsed, err := parseInt64(numberString)
|
2022-05-06 03:46:14 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2022-05-22 11:19:59 +00:00
|
|
|
return 0, err
|
2022-10-29 07:15:21 +00:00
|
|
|
} else if parsed > math.MaxInt || parsed < math.MinInt {
|
|
|
|
return 0, fmt.Errorf("%v is not within [%v, %v]", parsed, math.MinInt, math.MaxInt)
|
2022-05-06 03:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-05-22 11:19:59 +00:00
|
|
|
return int(parsed), err
|
2022-05-06 03:46:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func headAndLineComment(node *CandidateNode) string {
|
2022-01-15 00:57:59 +00:00
|
|
|
return headComment(node) + lineComment(node)
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func headComment(node *CandidateNode) string {
|
2022-01-15 00:57:59 +00:00
|
|
|
return strings.Replace(node.HeadComment, "#", "", 1)
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func lineComment(node *CandidateNode) string {
|
2022-01-15 00:57:59 +00:00
|
|
|
return strings.Replace(node.LineComment, "#", "", 1)
|
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func footComment(node *CandidateNode) string {
|
2022-01-15 00:57:59 +00:00
|
|
|
return strings.Replace(node.FootComment, "#", "", 1)
|
|
|
|
}
|
|
|
|
|
2022-08-29 04:13:15 +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 ""
|
|
|
|
}
|
2023-10-18 01:11:53 +00:00
|
|
|
if node == nil {
|
2020-10-17 11:10:47 +00:00
|
|
|
return "-- nil --"
|
2020-10-08 23:59:03 +00:00
|
|
|
}
|
2023-10-18 01:11:53 +00:00
|
|
|
tag := node.Tag
|
|
|
|
if node.Kind == AliasNode {
|
|
|
|
tag = "alias"
|
2020-10-08 23:59:03 +00:00
|
|
|
}
|
2023-10-18 01:11:53 +00:00
|
|
|
valueToUse := node.Value
|
|
|
|
if valueToUse == "" {
|
|
|
|
valueToUse = fmt.Sprintf("%v kids", len(node.Content))
|
2021-06-29 15:08:50 +00:00
|
|
|
}
|
2023-10-18 01:11:53 +00:00
|
|
|
return fmt.Sprintf(`D%v, P%v, %v (%v)::%v`, node.GetDocument(), node.GetNicePath(), KindString(node.Kind), tag, valueToUse)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NodeContentToString(node *CandidateNode, depth int) string {
|
|
|
|
if !log.IsEnabledFor(logging.DEBUG) {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
var sb strings.Builder
|
|
|
|
for _, child := range node.Content {
|
|
|
|
for i := 0; i < depth; i++ {
|
|
|
|
sb.WriteString(" ")
|
|
|
|
}
|
|
|
|
sb.WriteString("- ")
|
|
|
|
sb.WriteString(NodeToString(child))
|
|
|
|
sb.WriteString("\n")
|
|
|
|
sb.WriteString(NodeContentToString(child, depth+1))
|
2020-10-27 05:45:16 +00:00
|
|
|
}
|
2023-10-18 01:11:53 +00:00
|
|
|
return sb.String()
|
2020-10-08 23:59:03 +00:00
|
|
|
}
|
|
|
|
|
2023-10-18 01:11:53 +00:00
|
|
|
func KindString(kind Kind) string {
|
2020-10-08 23:59:03 +00:00
|
|
|
switch kind {
|
2023-10-18 01:11:53 +00:00
|
|
|
case ScalarNode:
|
2020-10-08 23:59:03 +00:00
|
|
|
return "ScalarNode"
|
2023-10-18 01:11:53 +00:00
|
|
|
case SequenceNode:
|
2020-10-08 23:59:03 +00:00
|
|
|
return "SequenceNode"
|
2023-10-18 01:11:53 +00:00
|
|
|
case MappingNode:
|
2020-10-08 23:59:03 +00:00
|
|
|
return "MappingNode"
|
2023-10-18 01:11:53 +00:00
|
|
|
case AliasNode:
|
2020-10-08 23:59:03 +00:00
|
|
|
return "AliasNode"
|
|
|
|
default:
|
|
|
|
return "unknown!"
|
|
|
|
}
|
|
|
|
}
|