mirror of
https://github.com/mikefarah/yq.git
synced 2025-01-23 14:16:10 +00:00
Added eval operator
This commit is contained in:
parent
77204551ab
commit
535799462f
@ -34,6 +34,7 @@ yq -i '.stuff = "foo"' myfile.yml # update myfile.yml inplace
|
|||||||
},
|
},
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
cmd.SetOut(cmd.OutOrStdout())
|
cmd.SetOut(cmd.OutOrStdout())
|
||||||
|
|
||||||
var format = logging.MustStringFormatter(
|
var format = logging.MustStringFormatter(
|
||||||
`%{color}%{time:15:04:05} %{shortfunc} [%{level:.4s}]%{color:reset} %{message}`,
|
`%{color}%{time:15:04:05} %{shortfunc} [%{level:.4s}]%{color:reset} %{message}`,
|
||||||
)
|
)
|
||||||
@ -47,6 +48,7 @@ yq -i '.stuff = "foo"' myfile.yml # update myfile.yml inplace
|
|||||||
}
|
}
|
||||||
|
|
||||||
logging.SetBackend(backend)
|
logging.SetBackend(backend)
|
||||||
|
yqlib.InitExpressionParser()
|
||||||
yqlib.XmlPreferences.AttributePrefix = xmlAttributePrefix
|
yqlib.XmlPreferences.AttributePrefix = xmlAttributePrefix
|
||||||
yqlib.XmlPreferences.ContentName = xmlContentName
|
yqlib.XmlPreferences.ContentName = xmlContentName
|
||||||
},
|
},
|
||||||
|
@ -63,7 +63,7 @@ func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) (yq
|
|||||||
|
|
||||||
if splitFileExp != "" {
|
if splitFileExp != "" {
|
||||||
colorsEnabled = forceColor
|
colorsEnabled = forceColor
|
||||||
splitExp, err := yqlib.NewExpressionParser().ParseExpression(splitFileExp)
|
splitExp, err := yqlib.ExpressionParser.ParseExpression(splitFileExp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bad split document expression: %w", err)
|
return nil, fmt.Errorf("bad split document expression: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,10 @@ type Evaluator interface {
|
|||||||
|
|
||||||
type allAtOnceEvaluator struct {
|
type allAtOnceEvaluator struct {
|
||||||
treeNavigator DataTreeNavigator
|
treeNavigator DataTreeNavigator
|
||||||
treeCreator ExpressionParser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAllAtOnceEvaluator() Evaluator {
|
func NewAllAtOnceEvaluator() Evaluator {
|
||||||
return &allAtOnceEvaluator{treeNavigator: NewDataTreeNavigator(), treeCreator: NewExpressionParser()}
|
return &allAtOnceEvaluator{treeNavigator: NewDataTreeNavigator()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *allAtOnceEvaluator) EvaluateNodes(expression string, nodes ...*yaml.Node) (*list.List, error) {
|
func (e *allAtOnceEvaluator) EvaluateNodes(expression string, nodes ...*yaml.Node) (*list.List, error) {
|
||||||
@ -35,7 +34,7 @@ func (e *allAtOnceEvaluator) EvaluateNodes(expression string, nodes ...*yaml.Nod
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCandidates *list.List) (*list.List, error) {
|
func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCandidates *list.List) (*list.List, error) {
|
||||||
node, err := e.treeCreator.ParseExpression(expression)
|
node, err := ExpressionParser.ParseExpression(expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ var evaluateNodesScenario = []expressionScenario{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAllAtOnceEvaluateNodes(t *testing.T) {
|
func TestAllAtOnceEvaluateNodes(t *testing.T) {
|
||||||
|
InitExpressionParser()
|
||||||
var evaluator = NewAllAtOnceEvaluator()
|
var evaluator = NewAllAtOnceEvaluator()
|
||||||
for _, tt := range evaluateNodesScenario {
|
for _, tt := range evaluateNodesScenario {
|
||||||
node := test.ParseData(tt.document)
|
node := test.ParseData(tt.document)
|
||||||
|
@ -77,6 +77,25 @@ will output
|
|||||||
a: "12"
|
a: "12"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Dynamically evaluate a path from an environment variable
|
||||||
|
The env variable can be any valid yq expression.
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- name: dog
|
||||||
|
- name: cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
myenv=".a.b[0].name" yq 'eval(strenv(myenv))' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
dog
|
||||||
|
```
|
||||||
|
|
||||||
## Dynamic key lookup with environment variable
|
## Dynamic key lookup with environment variable
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
48
pkg/yqlib/doc/operators/eval.md
Normal file
48
pkg/yqlib/doc/operators/eval.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Eval
|
||||||
|
|
||||||
|
Use `eval` to dynamically process an expression - for instance from an environment variable.
|
||||||
|
|
||||||
|
`eval` takes a single argument, and evaluates that as a `yq` expression. Any valid expression can be used, beit a path `.a.b.c | select(. == "cat")`, or an update `.a.b.c = "gogo"`.
|
||||||
|
|
||||||
|
Tip: This can be useful way parameterise complex scripts.
|
||||||
|
|
||||||
|
## Dynamically evaluate a path
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
pathExp: .a.b[] | select(.name == "cat")
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- name: dog
|
||||||
|
- name: cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq 'eval(.pathExp)' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
name: cat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dynamically update a path from an environment variable
|
||||||
|
The env variable can be any valid yq expression.
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- name: dog
|
||||||
|
- name: cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
myenv=".a.b[0].name" yq 'eval(strenv(myenv)) = "cow"' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- name: cow
|
||||||
|
- name: cat
|
||||||
|
```
|
||||||
|
|
7
pkg/yqlib/doc/operators/headers/eval.md
Normal file
7
pkg/yqlib/doc/operators/headers/eval.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Eval
|
||||||
|
|
||||||
|
Use `eval` to dynamically process an expression - for instance from an environment variable.
|
||||||
|
|
||||||
|
`eval` takes a single argument, and evaluates that as a `yq` expression. Any valid expression can be used, beit a path `.a.b.c | select(. == "cat")`, or an update `.a.b.c = "gogo"`.
|
||||||
|
|
||||||
|
Tip: This can be useful way parameterise complex scripts.
|
@ -11,7 +11,7 @@ type ExpressionNode struct {
|
|||||||
Rhs *ExpressionNode
|
Rhs *ExpressionNode
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpressionParser interface {
|
type ExpressionParserInterface interface {
|
||||||
ParseExpression(expression string) (*ExpressionNode, error)
|
ParseExpression(expression string) (*ExpressionNode, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ type expressionParserImpl struct {
|
|||||||
pathPostFixer expressionPostFixer
|
pathPostFixer expressionPostFixer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExpressionParser() ExpressionParser {
|
func newExpressionParser() ExpressionParserInterface {
|
||||||
return &expressionParserImpl{newExpressionTokeniser(), newExpressionPostFixer()}
|
return &expressionParserImpl{newExpressionTokeniser(), newExpressionPostFixer()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,66 +6,73 @@ import (
|
|||||||
"github.com/mikefarah/yq/v4/test"
|
"github.com/mikefarah/yq/v4/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getExpressionParser() ExpressionParserInterface {
|
||||||
|
if ExpressionParser == nil {
|
||||||
|
ExpressionParser = newExpressionParser()
|
||||||
|
}
|
||||||
|
return ExpressionParser
|
||||||
|
}
|
||||||
|
|
||||||
func TestParserNoMatchingCloseCollect(t *testing.T) {
|
func TestParserNoMatchingCloseCollect(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("[1,2")
|
_, err := getExpressionParser().ParseExpression("[1,2")
|
||||||
test.AssertResultComplex(t, "Bad expression, could not find matching `]`", err.Error())
|
test.AssertResultComplex(t, "Bad expression, could not find matching `]`", err.Error())
|
||||||
}
|
}
|
||||||
func TestParserNoMatchingCloseObjectInCollect(t *testing.T) {
|
func TestParserNoMatchingCloseObjectInCollect(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(`[{"b": "c"]`)
|
_, err := getExpressionParser().ParseExpression(`[{"b": "c"]`)
|
||||||
test.AssertResultComplex(t, "Bad expression, could not find matching `}`", err.Error())
|
test.AssertResultComplex(t, "Bad expression, could not find matching `}`", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNoMatchingCloseInCollect(t *testing.T) {
|
func TestParserNoMatchingCloseInCollect(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(`[(.a]`)
|
_, err := getExpressionParser().ParseExpression(`[(.a]`)
|
||||||
test.AssertResultComplex(t, "Bad expression, could not find matching `)`", err.Error())
|
test.AssertResultComplex(t, "Bad expression, could not find matching `)`", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNoMatchingCloseCollectObject(t *testing.T) {
|
func TestParserNoMatchingCloseCollectObject(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(`{"a": "b"`)
|
_, err := getExpressionParser().ParseExpression(`{"a": "b"`)
|
||||||
test.AssertResultComplex(t, "Bad expression, could not find matching `}`", err.Error())
|
test.AssertResultComplex(t, "Bad expression, could not find matching `}`", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNoMatchingCloseCollectInCollectObject(t *testing.T) {
|
func TestParserNoMatchingCloseCollectInCollectObject(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(`{"b": [1}`)
|
_, err := getExpressionParser().ParseExpression(`{"b": [1}`)
|
||||||
test.AssertResultComplex(t, "Bad expression, could not find matching `]`", err.Error())
|
test.AssertResultComplex(t, "Bad expression, could not find matching `]`", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNoMatchingCloseBracketInCollectObject(t *testing.T) {
|
func TestParserNoMatchingCloseBracketInCollectObject(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(`{"b": (1}`)
|
_, err := getExpressionParser().ParseExpression(`{"b": (1}`)
|
||||||
test.AssertResultComplex(t, "Bad expression, could not find matching `)`", err.Error())
|
test.AssertResultComplex(t, "Bad expression, could not find matching `)`", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNoArgsForTwoArgOp(t *testing.T) {
|
func TestParserNoArgsForTwoArgOp(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("=")
|
_, err := getExpressionParser().ParseExpression("=")
|
||||||
test.AssertResultComplex(t, "'=' expects 2 args but there is 0", err.Error())
|
test.AssertResultComplex(t, "'=' expects 2 args but there is 0", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserOneLhsArgsForTwoArgOp(t *testing.T) {
|
func TestParserOneLhsArgsForTwoArgOp(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(".a =")
|
_, err := getExpressionParser().ParseExpression(".a =")
|
||||||
test.AssertResultComplex(t, "'=' expects 2 args but there is 1", err.Error())
|
test.AssertResultComplex(t, "'=' expects 2 args but there is 1", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserOneRhsArgsForTwoArgOp(t *testing.T) {
|
func TestParserOneRhsArgsForTwoArgOp(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("= .a")
|
_, err := getExpressionParser().ParseExpression("= .a")
|
||||||
test.AssertResultComplex(t, "'=' expects 2 args but there is 1", err.Error())
|
test.AssertResultComplex(t, "'=' expects 2 args but there is 1", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserTwoArgsForTwoArgOp(t *testing.T) {
|
func TestParserTwoArgsForTwoArgOp(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression(".a = .b")
|
_, err := getExpressionParser().ParseExpression(".a = .b")
|
||||||
test.AssertResultComplex(t, nil, err)
|
test.AssertResultComplex(t, nil, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNoArgsForOneArgOp(t *testing.T) {
|
func TestParserNoArgsForOneArgOp(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("explode")
|
_, err := getExpressionParser().ParseExpression("explode")
|
||||||
test.AssertResultComplex(t, "'explode' expects 1 arg but received none", err.Error())
|
test.AssertResultComplex(t, "'explode' expects 1 arg but received none", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserOneArgForOneArgOp(t *testing.T) {
|
func TestParserOneArgForOneArgOp(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("explode(.)")
|
_, err := getExpressionParser().ParseExpression("explode(.)")
|
||||||
test.AssertResultComplex(t, nil, err)
|
test.AssertResultComplex(t, nil, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserExtraArgs(t *testing.T) {
|
func TestParserExtraArgs(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)")
|
_, err := getExpressionParser().ParseExpression("sortKeys(.) explode(.)")
|
||||||
test.AssertResultComplex(t, "Bad expression, please check expression syntax", err.Error())
|
test.AssertResultComplex(t, "Bad expression, please check expression syntax", err.Error())
|
||||||
}
|
}
|
||||||
|
@ -316,6 +316,8 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
|
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
|
||||||
lexer.Add([]byte(`length`), opToken(lengthOpType))
|
lexer.Add([]byte(`length`), opToken(lengthOpType))
|
||||||
|
|
||||||
|
lexer.Add([]byte(`eval`), opToken(evalOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`map`), opToken(mapOpType))
|
lexer.Add([]byte(`map`), opToken(mapOpType))
|
||||||
lexer.Add([]byte(`map_values`), opToken(mapValuesOpType))
|
lexer.Add([]byte(`map_values`), opToken(mapValuesOpType))
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ func decodeJson(t *testing.T, jsonString string) *CandidateNode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
exp, err := NewExpressionParser().ParseExpression(PrettyPrintExp)
|
exp, err := getExpressionParser().ParseExpression(PrettyPrintExp)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -124,7 +124,7 @@ func processJsonScenario(s formatScenario) string {
|
|||||||
expression = "."
|
expression = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
exp, err := NewExpressionParser().ParseExpression(expression)
|
exp, err := getExpressionParser().ParseExpression(expression)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -13,6 +13,14 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ExpressionParser ExpressionParserInterface
|
||||||
|
|
||||||
|
func InitExpressionParser() {
|
||||||
|
if ExpressionParser == nil {
|
||||||
|
ExpressionParser = newExpressionParser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type xmlPreferences struct {
|
type xmlPreferences struct {
|
||||||
AttributePrefix string
|
AttributePrefix string
|
||||||
ContentName string
|
ContentName string
|
||||||
@ -74,6 +82,7 @@ var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence:
|
|||||||
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
||||||
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
|
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
|
||||||
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
|
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
|
||||||
|
var evalOpType = &operationType{Type: "EVAL", NumArgs: 1, Precedence: 50, Handler: evalOperator}
|
||||||
var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 50, Handler: mapValuesOperator}
|
var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 50, Handler: mapValuesOperator}
|
||||||
var encodeOpType = &operationType{Type: "ENCODE", NumArgs: 0, Precedence: 50, Handler: encodeOperator}
|
var encodeOpType = &operationType{Type: "ENCODE", NumArgs: 0, Precedence: 50, Handler: encodeOperator}
|
||||||
var decodeOpType = &operationType{Type: "DECODE", NumArgs: 0, Precedence: 50, Handler: decodeOperator}
|
var decodeOpType = &operationType{Type: "DECODE", NumArgs: 0, Precedence: 50, Handler: decodeOperator}
|
||||||
|
@ -53,6 +53,16 @@ var envOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], ()::a: \"12\"\n",
|
"D0, P[], ()::a: \"12\"\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Dynamically evaluate a path from an environment variable",
|
||||||
|
subdescription: "The env variable can be any valid yq expression.",
|
||||||
|
document: `{a: {b: [{name: dog}, {name: cat}]}}`,
|
||||||
|
environmentVariable: ".a.b[0].name",
|
||||||
|
expression: `eval(strenv(myenv))`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a b 0 name], (!!str)::dog\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Dynamic key lookup with environment variable",
|
description: "Dynamic key lookup with environment variable",
|
||||||
environmentVariable: "cat",
|
environmentVariable: "cat",
|
||||||
|
42
pkg/yqlib/operator_eval.go
Normal file
42
pkg/yqlib/operator_eval.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
func evalOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
log.Debugf("Eval")
|
||||||
|
pathExpStrResults, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
expressions := make([]*ExpressionNode, pathExpStrResults.MatchingNodes.Len())
|
||||||
|
expIndex := 0
|
||||||
|
//parse every expression
|
||||||
|
for pathExpStrEntry := pathExpStrResults.MatchingNodes.Front(); pathExpStrEntry != nil; pathExpStrEntry = pathExpStrEntry.Next() {
|
||||||
|
expressionStrCandidate := pathExpStrEntry.Value.(*CandidateNode)
|
||||||
|
|
||||||
|
expressions[expIndex], err = ExpressionParser.ParseExpression(expressionStrCandidate.Node.Value)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
expIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
results := list.New()
|
||||||
|
|
||||||
|
for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
||||||
|
for expIndex = 0; expIndex < len(expressions); expIndex++ {
|
||||||
|
result, err := d.GetMatchingNodes(context, expressions[expIndex])
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
results.PushBackList(result.MatchingNodes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.ChildContext(results), nil
|
||||||
|
|
||||||
|
}
|
34
pkg/yqlib/operator_eval_test.go
Normal file
34
pkg/yqlib/operator_eval_test.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var evalOperatorScenarios = []expressionScenario{
|
||||||
|
|
||||||
|
{
|
||||||
|
description: "Dynamically evaluate a path",
|
||||||
|
document: `{pathExp: '.a.b[] | select(.name == "cat")', a: {b: [{name: dog}, {name: cat}]}}`,
|
||||||
|
expression: `eval(.pathExp)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a b 1], (!!map)::{name: cat}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Dynamically update a path from an environment variable",
|
||||||
|
subdescription: "The env variable can be any valid yq expression.",
|
||||||
|
document: `{a: {b: [{name: dog}, {name: cat}]}}`,
|
||||||
|
environmentVariable: ".a.b[0].name",
|
||||||
|
expression: `eval(strenv(myenv)) = "cow"`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::{a: {b: [{name: cow}, {name: cat}]}}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalOperatorsScenarios(t *testing.T) {
|
||||||
|
for _, tt := range evalOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
documentOperatorScenarios(t, "eval", evalOperatorScenarios)
|
||||||
|
}
|
@ -53,7 +53,7 @@ func readDocumentWithLeadingContent(content string, fakefilename string, fakeFil
|
|||||||
|
|
||||||
func testScenario(t *testing.T, s *expressionScenario) {
|
func testScenario(t *testing.T, s *expressionScenario) {
|
||||||
var err error
|
var err error
|
||||||
node, err := NewExpressionParser().ParseExpression(s.expression)
|
node, err := getExpressionParser().ParseExpression(s.expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(fmt.Errorf("Error parsing expression %v of %v: %w", s.expression, s.description, err))
|
t.Error(fmt.Errorf("Error parsing expression %v of %v: %w", s.expression, s.description, err))
|
||||||
return
|
return
|
||||||
@ -157,7 +157,7 @@ func formatYaml(yaml string, filename string) string {
|
|||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true)
|
printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true)
|
||||||
|
|
||||||
node, err := NewExpressionParser().ParseExpression(".. style= \"\"")
|
node, err := getExpressionParser().ParseExpression(".. style= \"\"")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -280,7 +280,7 @@ func documentOutput(t *testing.T, w *bufio.Writer, s expressionScenario, formatt
|
|||||||
var err error
|
var err error
|
||||||
printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true)
|
printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true)
|
||||||
|
|
||||||
node, err := NewExpressionParser().ParseExpression(s.expression)
|
node, err := getExpressionParser().ParseExpression(s.expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(fmt.Errorf("Error parsing expression %v of %v: %w", s.expression, s.description, err))
|
t.Error(fmt.Errorf("Error parsing expression %v of %v: %w", s.expression, s.description, err))
|
||||||
return
|
return
|
||||||
|
@ -289,7 +289,7 @@ func TestPrinterScalarWithLeadingCont(t *testing.T) {
|
|||||||
var writer = bufio.NewWriter(&output)
|
var writer = bufio.NewWriter(&output)
|
||||||
printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true)
|
printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true)
|
||||||
|
|
||||||
node, err := NewExpressionParser().ParseExpression(".a")
|
node, err := getExpressionParser().ParseExpression(".a")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -21,16 +21,15 @@ type StreamEvaluator interface {
|
|||||||
|
|
||||||
type streamEvaluator struct {
|
type streamEvaluator struct {
|
||||||
treeNavigator DataTreeNavigator
|
treeNavigator DataTreeNavigator
|
||||||
treeCreator ExpressionParser
|
|
||||||
fileIndex int
|
fileIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStreamEvaluator() StreamEvaluator {
|
func NewStreamEvaluator() StreamEvaluator {
|
||||||
return &streamEvaluator{treeNavigator: NewDataTreeNavigator(), treeCreator: NewExpressionParser()}
|
return &streamEvaluator{treeNavigator: NewDataTreeNavigator()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *streamEvaluator) EvaluateNew(expression string, printer Printer, leadingContent string) error {
|
func (s *streamEvaluator) EvaluateNew(expression string, printer Printer, leadingContent string) error {
|
||||||
node, err := s.treeCreator.ParseExpression(expression)
|
node, err := ExpressionParser.ParseExpression(expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -53,7 +52,7 @@ func (s *streamEvaluator) EvaluateNew(expression string, printer Printer, leadin
|
|||||||
|
|
||||||
func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error {
|
func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error {
|
||||||
var totalProcessDocs uint
|
var totalProcessDocs uint
|
||||||
node, err := s.treeCreator.ParseExpression(expression)
|
node, err := ExpressionParser.ParseExpression(expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ func decodeXml(t *testing.T, s formatScenario) *CandidateNode {
|
|||||||
expression = "."
|
expression = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
exp, err := NewExpressionParser().ParseExpression(expression)
|
exp, err := getExpressionParser().ParseExpression(expression)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
Loading…
Reference in New Issue
Block a user