From 38c207a83991f90044e98db664af28e1f4b292f9 Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Fri, 7 Oct 2022 13:05:00 -0600 Subject: [PATCH 1/3] Add condition parser --- pkg/ottl/boolean_value.go | 24 +-- pkg/ottl/grammar.go | 16 +- pkg/ottl/parser.go | 73 +++++-- pkg/ottl/parser_test.go | 400 ++++++++++++++++++++------------------ 4 files changed, 282 insertions(+), 231 deletions(-) diff --git a/pkg/ottl/boolean_value.go b/pkg/ottl/boolean_value.go index 4f7b3928406c..673f24f5f195 100644 --- a/pkg/ottl/boolean_value.go +++ b/pkg/ottl/boolean_value.go @@ -18,8 +18,8 @@ import ( "fmt" ) -// boolExpressionEvaluator is a function that returns the result. -type boolExpressionEvaluator[K any] func(ctx K) bool +// BoolExpressionEvaluator is a function that returns the result. +type BoolExpressionEvaluator[K any] func(ctx K) bool func alwaysTrue[K any](K) bool { return true @@ -30,8 +30,8 @@ func alwaysFalse[K any](K) bool { } // builds a function that returns a short-circuited result of ANDing -// boolExpressionEvaluator funcs -func andFuncs[K any](funcs []boolExpressionEvaluator[K]) boolExpressionEvaluator[K] { +// BoolExpressionEvaluator funcs +func andFuncs[K any](funcs []BoolExpressionEvaluator[K]) BoolExpressionEvaluator[K] { return func(ctx K) bool { for _, f := range funcs { if !f(ctx) { @@ -43,8 +43,8 @@ func andFuncs[K any](funcs []boolExpressionEvaluator[K]) boolExpressionEvaluator } // builds a function that returns a short-circuited result of ORing -// boolExpressionEvaluator funcs -func orFuncs[K any](funcs []boolExpressionEvaluator[K]) boolExpressionEvaluator[K] { +// BoolExpressionEvaluator funcs +func orFuncs[K any](funcs []BoolExpressionEvaluator[K]) BoolExpressionEvaluator[K] { return func(ctx K) bool { for _, f := range funcs { if f(ctx) { @@ -55,7 +55,7 @@ func orFuncs[K any](funcs []boolExpressionEvaluator[K]) boolExpressionEvaluator[ } } -func (p *Parser[K]) newComparisonEvaluator(comparison *comparison) (boolExpressionEvaluator[K], error) { +func (p *Parser[K]) newComparisonEvaluator(comparison *comparison) (BoolExpressionEvaluator[K], error) { if comparison == nil { return alwaysTrue[K], nil } @@ -77,7 +77,7 @@ func (p *Parser[K]) newComparisonEvaluator(comparison *comparison) (boolExpressi } -func (p *Parser[K]) newBooleanExpressionEvaluator(expr *booleanExpression) (boolExpressionEvaluator[K], error) { +func (p *Parser[K]) newBooleanExpressionEvaluator(expr *booleanExpression) (BoolExpressionEvaluator[K], error) { if expr == nil { return alwaysTrue[K], nil } @@ -85,7 +85,7 @@ func (p *Parser[K]) newBooleanExpressionEvaluator(expr *booleanExpression) (bool if err != nil { return nil, err } - funcs := []boolExpressionEvaluator[K]{f} + funcs := []BoolExpressionEvaluator[K]{f} for _, rhs := range expr.Right { f, err := p.newBooleanTermEvaluator(rhs.Term) if err != nil { @@ -97,7 +97,7 @@ func (p *Parser[K]) newBooleanExpressionEvaluator(expr *booleanExpression) (bool return orFuncs(funcs), nil } -func (p *Parser[K]) newBooleanTermEvaluator(term *term) (boolExpressionEvaluator[K], error) { +func (p *Parser[K]) newBooleanTermEvaluator(term *term) (BoolExpressionEvaluator[K], error) { if term == nil { return alwaysTrue[K], nil } @@ -105,7 +105,7 @@ func (p *Parser[K]) newBooleanTermEvaluator(term *term) (boolExpressionEvaluator if err != nil { return nil, err } - funcs := []boolExpressionEvaluator[K]{f} + funcs := []BoolExpressionEvaluator[K]{f} for _, rhs := range term.Right { f, err := p.newBooleanValueEvaluator(rhs.Value) if err != nil { @@ -117,7 +117,7 @@ func (p *Parser[K]) newBooleanTermEvaluator(term *term) (boolExpressionEvaluator return andFuncs(funcs), nil } -func (p *Parser[K]) newBooleanValueEvaluator(value *booleanValue) (boolExpressionEvaluator[K], error) { +func (p *Parser[K]) newBooleanValueEvaluator(value *booleanValue) (BoolExpressionEvaluator[K], error) { if value == nil { return alwaysTrue[K], nil } diff --git a/pkg/ottl/grammar.go b/pkg/ottl/grammar.go index 7c2334f93af9..98960d9968ef 100644 --- a/pkg/ottl/grammar.go +++ b/pkg/ottl/grammar.go @@ -20,12 +20,17 @@ import ( "github.com/alecthomas/participle/v2/lexer" ) -// parsedStatement represents a parsed statement. It is the entry point into the statement DSL. -type parsedStatement struct { +// transformationStatement represents a transformation statement. It is the entry point into the statement DSL. +type transformationStatement struct { Invocation invocation `parser:"@@"` WhereClause *booleanExpression `parser:"( 'where' @@ )?"` } +// conditionStatement represents a condition statement. +type conditionStatement struct { + BooleanExpression *booleanExpression `parser:"@@"` +} + // booleanValue represents something that evaluates to a boolean -- // either an equality or inequality, explicit true or false, or // a parenthesized subexpression. @@ -151,13 +156,6 @@ type Field struct { MapKey *string `parser:"( '[' @String ']' )?"` } -// Statement holds a top level Statement for processing telemetry data. A Statement is a combination of a function -// invocation and the expression to match telemetry for invoking the function. -type Statement[K any] struct { - Function ExprFunc[K] - Condition boolExpressionEvaluator[K] -} - // byteSlice type for capturing byte slices type byteSlice []byte diff --git a/pkg/ottl/parser.go b/pkg/ottl/parser.go index 214b66d7d7fa..5c1427ac3d15 100644 --- a/pkg/ottl/parser.go +++ b/pkg/ottl/parser.go @@ -20,28 +20,63 @@ import ( "go.uber.org/multierr" ) +// Statement holds a top level statement for processing telemetry data. A Statement is a combination of a function +// invocation and the expression to match telemetry for invoking the function. +type Statement[K any] struct { + Function ExprFunc[K] + Condition BoolExpressionEvaluator[K] +} + type Parser[K any] struct { - functions map[string]interface{} - pathParser PathExpressionParser[K] - enumParser EnumParser - telemetrySettings component.TelemetrySettings + functions map[string]interface{} + pathParser PathExpressionParser[K] + enumParser EnumParser + telemetrySettings component.TelemetrySettings + transformationParser *participle.Parser[transformationStatement] + conditionParser *participle.Parser[conditionStatement] } func NewParser[K any](functions map[string]interface{}, pathParser PathExpressionParser[K], enumParser EnumParser, telemetrySettings component.TelemetrySettings) Parser[K] { return Parser[K]{ - functions: functions, - pathParser: pathParser, - enumParser: enumParser, - telemetrySettings: telemetrySettings, + functions: functions, + pathParser: pathParser, + enumParser: enumParser, + telemetrySettings: telemetrySettings, + transformationParser: newParser[transformationStatement](), + conditionParser: newParser[conditionStatement](), + } +} + +func (p *Parser[K]) ParseConditions(rawStatements []string) ([]BoolExpressionEvaluator[K], error) { + var statements []BoolExpressionEvaluator[K] + var errors error + + for _, statement := range rawStatements { + parsed, err := parseStatement(p.conditionParser, statement) + if err != nil { + errors = multierr.Append(errors, err) + continue + } + expression, err := p.newBooleanExpressionEvaluator(parsed.BooleanExpression) + if err != nil { + errors = multierr.Append(errors, err) + continue + } + statements = append(statements, expression) } + + if errors != nil { + return nil, errors + } + return statements, nil } -func (p *Parser[K]) ParseStatements(statements []string) ([]Statement[K], error) { - var parsedStatements []Statement[K] +func (p *Parser[K]) ParseStatements(rawStatements []string) ([]Statement[K], error) { + var statements []Statement[K] var errors error - for _, statement := range statements { - parsed, err := parseStatement(statement) + for _, statement := range rawStatements { + parsed, err := parseStatement(p.transformationParser, statement) if err != nil { errors = multierr.Append(errors, err) continue @@ -56,7 +91,7 @@ func (p *Parser[K]) ParseStatements(statements []string) ([]Statement[K], error) errors = multierr.Append(errors, err) continue } - parsedStatements = append(parsedStatements, Statement[K]{ + statements = append(statements, Statement[K]{ Function: function, Condition: expression, }) @@ -65,12 +100,10 @@ func (p *Parser[K]) ParseStatements(statements []string) ([]Statement[K], error) if errors != nil { return nil, errors } - return parsedStatements, nil + return statements, nil } -var parser = newParser() - -func parseStatement(raw string) (*parsedStatement, error) { +func parseStatement[G transformationStatement | conditionStatement](parser *participle.Parser[G], raw string) (*G, error) { parsed, err := parser.ParseString("", raw) if err != nil { return nil, err @@ -78,11 +111,11 @@ func parseStatement(raw string) (*parsedStatement, error) { return parsed, nil } -// newParser returns a parser that can be used to read a string into a parsedStatement. An error will be returned if the string +// newParser returns a parser that can be used to read a string into a transformationStatement. An error will be returned if the string // is not formatted for the DSL. -func newParser() *participle.Parser[parsedStatement] { +func newParser[K any]() *participle.Parser[K] { lex := buildLexer() - parser, err := participle.Build[parsedStatement]( + parser, err := participle.Build[K]( participle.Lexer(lex), participle.Unquote("String"), participle.Elide("whitespace"), diff --git a/pkg/ottl/parser_test.go b/pkg/ottl/parser_test.go index b78a96dcc28d..b909a967cfa3 100644 --- a/pkg/ottl/parser_test.go +++ b/pkg/ottl/parser_test.go @@ -30,16 +30,16 @@ func booleanp(b boolean) *boolean { return &b } -func Test_parse(t *testing.T) { +func Test_parseTransformationStatement(t *testing.T) { tests := []struct { name string statement string - expected *parsedStatement + expected *transformationStatement }{ { name: "invocation with string", statement: `set("foo")`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -54,7 +54,7 @@ func Test_parse(t *testing.T) { { name: "invocation with float", statement: `met(1.2)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "met", Arguments: []value{ @@ -69,7 +69,7 @@ func Test_parse(t *testing.T) { { name: "invocation with int", statement: `fff(12)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "fff", Arguments: []value{ @@ -84,7 +84,7 @@ func Test_parse(t *testing.T) { { name: "complex invocation", statement: `set("foo", getSomething(bear.honey))`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -118,7 +118,7 @@ func Test_parse(t *testing.T) { { name: "complex path", statement: `set(foo.attributes["bar"].cat, "dog")`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -149,7 +149,7 @@ func Test_parse(t *testing.T) { { name: "where == clause", statement: `set(foo.attributes["bar"].cat, "dog") where name == "fido"`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -200,7 +200,7 @@ func Test_parse(t *testing.T) { { name: "where != clause", statement: `set(foo.attributes["bar"].cat, "dog") where name != "fido"`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -251,7 +251,7 @@ func Test_parse(t *testing.T) { { name: "ignore extra spaces", statement: `set ( foo.attributes[ "bar"].cat, "dog") where name=="fido"`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -302,7 +302,7 @@ func Test_parse(t *testing.T) { { name: "handle quotes", statement: `set("fo\"o")`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -317,7 +317,7 @@ func Test_parse(t *testing.T) { { name: "invocation with boolean false", statement: `convert_gauge_to_sum("cumulative", false)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "convert_gauge_to_sum", Arguments: []value{ @@ -335,7 +335,7 @@ func Test_parse(t *testing.T) { { name: "invocation with boolean true", statement: `convert_gauge_to_sum("cumulative", true)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "convert_gauge_to_sum", Arguments: []value{ @@ -353,7 +353,7 @@ func Test_parse(t *testing.T) { { name: "invocation with bytes", statement: `set(attributes["bytes"], 0x0102030405060708)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -378,7 +378,7 @@ func Test_parse(t *testing.T) { { name: "invocation with nil", statement: `set(attributes["test"], nil)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -403,7 +403,7 @@ func Test_parse(t *testing.T) { { name: "invocation with Enum", statement: `set(attributes["test"], TEST_ENUM)`, - expected: &parsedStatement{ + expected: &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -426,17 +426,17 @@ func Test_parse(t *testing.T) { }, }, } - + parser := newParser[transformationStatement]() for _, tt := range tests { t.Run(tt.statement, func(t *testing.T) { - parsed, err := parseStatement(tt.statement) + parsed, err := parseStatement(parser, tt.statement) assert.NoError(t, err) assert.EqualValues(t, tt.expected, parsed) }) } } -func Test_parse_failure(t *testing.T) { +func Test_parseTransformationStatement_failure(t *testing.T) { tests := []string{ `set(`, `set("foo)`, @@ -459,9 +459,10 @@ func Test_parse_failure(t *testing.T) { `set("foo") where (name == "fido"))`, `set("foo") where ((name == "fido")`, } + parser := newParser[transformationStatement]() for _, tt := range tests { t.Run(tt, func(t *testing.T) { - _, err := parseStatement(tt) + _, err := parseStatement(parser, tt) assert.Error(t, err) }) } @@ -483,8 +484,8 @@ func testParsePath(val *Path) (GetSetter[interface{}], error) { // Helper for test cases where the WHERE clause is all that matters. // Parse string should start with `set(name, "test") where`... -func setNameTest(b *booleanExpression) *parsedStatement { - return &parsedStatement{ +func setNameTest(b *booleanExpression) *transformationStatement { + return &transformationStatement{ Invocation: invocation{ Function: "set", Arguments: []value{ @@ -509,165 +510,179 @@ func setNameTest(b *booleanExpression) *parsedStatement { func Test_parseWhere(t *testing.T) { tests := []struct { statement string - expected *parsedStatement + expected *conditionStatement }{ { statement: `true`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(true), + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(true), + }, }, }, - }), + }, }, { statement: `true and false`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(true), - }, - Right: []*opAndBooleanValue{ - { - Operator: "and", - Value: &booleanValue{ - ConstExpr: booleanp(false), + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(true), + }, + Right: []*opAndBooleanValue{ + { + Operator: "and", + Value: &booleanValue{ + ConstExpr: booleanp(false), + }, }, }, }, }, - }), + }, }, { statement: `true and true and false`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(true), - }, - Right: []*opAndBooleanValue{ - { - Operator: "and", - Value: &booleanValue{ - ConstExpr: booleanp(true), - }, + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(true), }, - { - Operator: "and", - Value: &booleanValue{ - ConstExpr: booleanp(false), + Right: []*opAndBooleanValue{ + { + Operator: "and", + Value: &booleanValue{ + ConstExpr: booleanp(true), + }, + }, + { + Operator: "and", + Value: &booleanValue{ + ConstExpr: booleanp(false), + }, }, }, }, }, - }), + }, }, { statement: `true or false`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(true), + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(true), + }, }, - }, - Right: []*opOrTerm{ - { - Operator: "or", - Term: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), + Right: []*opOrTerm{ + { + Operator: "or", + Term: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, }, }, }, }, - }), + }, }, { statement: `false and true or false`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), - }, - Right: []*opAndBooleanValue{ - { - Operator: "and", - Value: &booleanValue{ - ConstExpr: booleanp(true), + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, + Right: []*opAndBooleanValue{ + { + Operator: "and", + Value: &booleanValue{ + ConstExpr: booleanp(true), + }, }, }, }, - }, - Right: []*opOrTerm{ - { - Operator: "or", - Term: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), + Right: []*opOrTerm{ + { + Operator: "or", + Term: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, }, }, }, }, - }), + }, }, { statement: `(false and true) or false`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - SubExpr: &booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), - }, - Right: []*opAndBooleanValue{ - { - Operator: "and", - Value: &booleanValue{ - ConstExpr: booleanp(true), + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + SubExpr: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, + Right: []*opAndBooleanValue{ + { + Operator: "and", + Value: &booleanValue{ + ConstExpr: booleanp(true), + }, }, }, }, }, }, }, - }, - Right: []*opOrTerm{ - { - Operator: "or", - Term: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), + Right: []*opOrTerm{ + { + Operator: "or", + Term: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, }, }, }, }, - }), + }, }, { statement: `false and (true or false)`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), - }, - Right: []*opAndBooleanValue{ - { - Operator: "and", - Value: &booleanValue{ - SubExpr: &booleanExpression{ - Left: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(true), + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, + Right: []*opAndBooleanValue{ + { + Operator: "and", + Value: &booleanValue{ + SubExpr: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(true), + }, }, - }, - Right: []*opOrTerm{ - { - Operator: "or", - Term: &term{ - Left: &booleanValue{ - ConstExpr: booleanp(false), + Right: []*opOrTerm{ + { + Operator: "or", + Term: &term{ + Left: &booleanValue{ + ConstExpr: booleanp(false), + }, }, }, }, @@ -677,111 +692,115 @@ func Test_parseWhere(t *testing.T) { }, }, }, - }), + }, }, { statement: `name != "foo" and name != "bar"`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - Comparison: &comparison{ - Left: value{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + Comparison: &comparison{ + Left: value{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, - }, - Op: NE, - Right: value{ - String: ottltest.Strp("foo"), + Op: NE, + Right: value{ + String: ottltest.Strp("foo"), + }, }, }, - }, - Right: []*opAndBooleanValue{ - { - Operator: "and", - Value: &booleanValue{ - Comparison: &comparison{ - Left: value{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Right: []*opAndBooleanValue{ + { + Operator: "and", + Value: &booleanValue{ + Comparison: &comparison{ + Left: value{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, - }, - Op: NE, - Right: value{ - String: ottltest.Strp("bar"), + Op: NE, + Right: value{ + String: ottltest.Strp("bar"), + }, }, }, }, }, }, }, - }), + }, }, { statement: `name == "foo" or name == "bar"`, - expected: setNameTest(&booleanExpression{ - Left: &term{ - Left: &booleanValue{ - Comparison: &comparison{ - Left: value{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + expected: &conditionStatement{ + BooleanExpression: &booleanExpression{ + Left: &term{ + Left: &booleanValue{ + Comparison: &comparison{ + Left: value{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, - }, - Op: EQ, - Right: value{ - String: ottltest.Strp("foo"), + Op: EQ, + Right: value{ + String: ottltest.Strp("foo"), + }, }, }, }, - }, - Right: []*opOrTerm{ - { - Operator: "or", - Term: &term{ - Left: &booleanValue{ - Comparison: &comparison{ - Left: value{ - Path: &Path{ - Fields: []Field{ - { - Name: "name", + Right: []*opOrTerm{ + { + Operator: "or", + Term: &term{ + Left: &booleanValue{ + Comparison: &comparison{ + Left: value{ + Path: &Path{ + Fields: []Field{ + { + Name: "name", + }, }, }, }, - }, - Op: EQ, - Right: value{ - String: ottltest.Strp("bar"), + Op: EQ, + Right: value{ + String: ottltest.Strp("bar"), + }, }, }, }, }, }, }, - }), + }, }, } // create a test name that doesn't confuse vscode so we can rerun tests with one click pat := regexp.MustCompile("[^a-zA-Z0-9]+") + parser := newParser[conditionStatement]() for _, tt := range tests { name := pat.ReplaceAllString(tt.statement, "_") t.Run(name, func(t *testing.T) { - statement := `set(name, "test") where ` + tt.statement - parsed, err := parseStatement(statement) + parsed, err := parseStatement(parser, tt.statement) assert.NoError(t, err) assert.Equal(t, tt.expected, parsed) }) @@ -830,12 +849,13 @@ func Test_parseStatement(t *testing.T) { {`drop() where attributes["path"] == "/healthcheck"`, false}, } pat := regexp.MustCompile("[^a-zA-Z0-9]+") + parser := newParser[transformationStatement]() for _, tt := range tests { name := pat.ReplaceAllString(tt.statement, "_") t.Run(name, func(t *testing.T) { - _, err := parseStatement(tt.statement) + _, err := parseStatement(parser, tt.statement) if (err != nil) != tt.wantErr { - t.Errorf("parseStatement(%s) error = %v, wantErr %v", tt.statement, err, tt.wantErr) + t.Errorf("parseTransformationStatement(%s) error = %v, wantErr %v", tt.statement, err, tt.wantErr) return } }) From 9a369833e5b22c8c9b82637b3d03538de123e079 Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Fri, 7 Oct 2022 13:12:27 -0600 Subject: [PATCH 2/3] add changelog entry --- .chloggen/ottl-condition-parser.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 .chloggen/ottl-condition-parser.yaml diff --git a/.chloggen/ottl-condition-parser.yaml b/.chloggen/ottl-condition-parser.yaml new file mode 100755 index 000000000000..d1bb1e6d5d28 --- /dev/null +++ b/.chloggen/ottl-condition-parser.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/ottl + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Adds a ParseConditions function that allows parsing ottl statements that contain only conditions. + +# One or more tracking issues related to the change +issues: [14783] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: From c70274ab80b6a3f42582f0bdeabc15defc853c68 Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Fri, 7 Oct 2022 13:21:04 -0600 Subject: [PATCH 3/3] Fix lint --- pkg/ottl/parser_test.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/pkg/ottl/parser_test.go b/pkg/ottl/parser_test.go index b909a967cfa3..979c08273dd1 100644 --- a/pkg/ottl/parser_test.go +++ b/pkg/ottl/parser_test.go @@ -482,31 +482,6 @@ func testParsePath(val *Path) (GetSetter[interface{}], error) { return nil, fmt.Errorf("bad path %v", val) } -// Helper for test cases where the WHERE clause is all that matters. -// Parse string should start with `set(name, "test") where`... -func setNameTest(b *booleanExpression) *transformationStatement { - return &transformationStatement{ - Invocation: invocation{ - Function: "set", - Arguments: []value{ - { - Path: &Path{ - Fields: []Field{ - { - Name: "name", - }, - }, - }, - }, - { - String: ottltest.Strp("test"), - }, - }, - }, - WhereClause: b, - } -} - func Test_parseWhere(t *testing.T) { tests := []struct { statement string