Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pkg/ottl] Add grammar utility to extract paths from statements #35174

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions pkg/ottl/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ func (b *booleanValue) checkForCustomError() error {
return nil
}

func (b *booleanValue) accept(v grammarVisitor) {
if b.Comparison != nil {
b.Comparison.accept(v)
}
if b.ConstExpr != nil && b.ConstExpr.Converter != nil {
b.ConstExpr.Converter.accept(v)
}
if b.SubExpr != nil {
b.SubExpr.accept(v)
}
}

// opAndBooleanValue represents the right side of an AND boolean expression.
type opAndBooleanValue struct {
Operator string `parser:"@OpAnd"`
Expand All @@ -67,6 +79,12 @@ func (b *opAndBooleanValue) checkForCustomError() error {
return b.Value.checkForCustomError()
}

func (b *opAndBooleanValue) accept(v grammarVisitor) {
if b.Value != nil {
b.Value.accept(v)
}
}

// term represents an arbitrary number of boolean values joined by AND.
type term struct {
Left *booleanValue `parser:"@@"`
Expand All @@ -87,6 +105,17 @@ func (b *term) checkForCustomError() error {
return nil
}

func (b *term) accept(v grammarVisitor) {
if b.Left != nil {
b.Left.accept(v)
}
for _, r := range b.Right {
if r != nil {
r.accept(v)
}
}
}

// opOrTerm represents the right side of an OR boolean expression.
type opOrTerm struct {
Operator string `parser:"@OpOr"`
Expand All @@ -97,6 +126,12 @@ func (b *opOrTerm) checkForCustomError() error {
return b.Term.checkForCustomError()
}

func (b *opOrTerm) accept(v grammarVisitor) {
if b.Term != nil {
b.Term.accept(v)
}
}

// booleanExpression represents a true/false decision expressed
// as an arbitrary number of terms separated by OR.
type booleanExpression struct {
Expand All @@ -118,6 +153,17 @@ func (b *booleanExpression) checkForCustomError() error {
return nil
}

func (b *booleanExpression) accept(v grammarVisitor) {
if b.Left != nil {
b.Left.accept(v)
}
for _, r := range b.Right {
if r != nil {
r.accept(v)
}
}
}

// compareOp is the type of a comparison operator.
type compareOp int

Expand Down Expand Up @@ -187,6 +233,11 @@ func (c *comparison) checkForCustomError() error {
return err
}

func (c *comparison) accept(v grammarVisitor) {
c.Left.accept(v)
c.Right.accept(v)
}

// editor represents the function call of a statement.
type editor struct {
Function string `parser:"@(Lowercase(Uppercase | Lowercase)*)"`
Expand All @@ -210,13 +261,28 @@ func (i *editor) checkForCustomError() error {
return nil
}

func (i *editor) accept(v grammarVisitor) {
v.visitEditor(i)
for _, arg := range i.Arguments {
arg.accept(v)
}
}

// converter represents a converter function call.
type converter struct {
Function string `parser:"@(Uppercase(Uppercase | Lowercase)*)"`
Arguments []argument `parser:"'(' ( @@ ( ',' @@ )* )? ')'"`
Keys []key `parser:"( @@ )*"`
}

func (c *converter) accept(v grammarVisitor) {
if c.Arguments != nil {
for _, a := range c.Arguments {
a.accept(v)
}
}
}

type argument struct {
Name string `parser:"(@(Lowercase(Uppercase | Lowercase)*) Equal)?"`
Value value `parser:"( @@"`
Expand All @@ -227,6 +293,10 @@ func (a *argument) checkForCustomError() error {
return a.Value.checkForCustomError()
}

func (a *argument) accept(v grammarVisitor) {
a.Value.accept(v)
}

// value represents a part of a parsed statement which is resolved to a value of some sort. This can be a telemetry path
// mathExpression, function call, or literal.
type value struct {
Expand All @@ -251,8 +321,27 @@ func (v *value) checkForCustomError() error {
return nil
}

func (v *value) accept(vis grammarVisitor) {
vis.visitValue(v)
if v.Literal != nil {
v.Literal.accept(vis)
}
if v.MathExpression != nil {
v.MathExpression.accept(vis)
}
if v.Map != nil {
v.Map.accept(vis)
}
if v.List != nil {
for _, i := range v.List.Values {
i.accept(vis)
}
}
}

// path represents a telemetry path mathExpression.
type path struct {
Pos lexer.Position
TylerHelmuth marked this conversation as resolved.
Show resolved Hide resolved
Context string `parser:"(@Lowercase '.')?"`
Fields []field `parser:"@@ ( '.' @@ )*"`
}
Expand All @@ -276,6 +365,14 @@ type mapValue struct {
Values []mapItem `parser:"'{' (@@ ','?)* '}'"`
}

func (m *mapValue) accept(v grammarVisitor) {
for _, i := range m.Values {
if i.Value != nil {
i.Value.accept(v)
}
}
}

type mapItem struct {
Key *string `parser:"@String ':'"`
Value *value `parser:"@@"`
Expand Down Expand Up @@ -326,6 +423,19 @@ func (m *mathExprLiteral) checkForCustomError() error {
return nil
}

func (m *mathExprLiteral) accept(v grammarVisitor) {
v.visitMathExprLiteral(m)
if m.Path != nil {
v.visitPath(m.Path)
}
if m.Editor != nil {
m.Editor.accept(v)
}
if m.Converter != nil {
m.Converter.accept(v)
}
}

type mathValue struct {
Literal *mathExprLiteral `parser:"( @@"`
SubExpression *mathExpression `parser:"| '(' @@ ')' )"`
Expand All @@ -338,6 +448,15 @@ func (m *mathValue) checkForCustomError() error {
return m.SubExpression.checkForCustomError()
}

func (m *mathValue) accept(v grammarVisitor) {
if m.Literal != nil {
m.Literal.accept(v)
}
if m.SubExpression != nil {
m.SubExpression.accept(v)
}
}

type opMultDivValue struct {
Operator mathOp `parser:"@OpMultDiv"`
Value *mathValue `parser:"@@"`
Expand All @@ -347,6 +466,12 @@ func (m *opMultDivValue) checkForCustomError() error {
return m.Value.checkForCustomError()
}

func (m *opMultDivValue) accept(v grammarVisitor) {
if m.Value != nil {
m.Value.accept(v)
}
}

type addSubTerm struct {
Left *mathValue `parser:"@@"`
Right []*opMultDivValue `parser:"@@*"`
Expand All @@ -366,6 +491,17 @@ func (m *addSubTerm) checkForCustomError() error {
return nil
}

func (m *addSubTerm) accept(v grammarVisitor) {
if m.Left != nil {
m.Left.accept(v)
}
for _, r := range m.Right {
if r != nil {
r.accept(v)
}
}
}

type opAddSubTerm struct {
Operator mathOp `parser:"@OpAddSub"`
Term *addSubTerm `parser:"@@"`
Expand All @@ -375,6 +511,12 @@ func (m *opAddSubTerm) checkForCustomError() error {
return m.Term.checkForCustomError()
}

func (m *opAddSubTerm) accept(v grammarVisitor) {
if m.Term != nil {
m.Term.accept(v)
}
}

type mathExpression struct {
Left *addSubTerm `parser:"@@"`
Right []*opAddSubTerm `parser:"@@*"`
Expand All @@ -394,6 +536,19 @@ func (m *mathExpression) checkForCustomError() error {
return nil
}

func (m *mathExpression) accept(v grammarVisitor) {
if m.Left != nil {
m.Left.accept(v)
}
if m.Right != nil {
for _, r := range m.Right {
if r != nil {
r.accept(v)
}
}
}
}

type mathOp int

const (
Expand Down Expand Up @@ -464,3 +619,11 @@ func buildLexer() *lexer.StatefulDefinition {
{Name: "whitespace", Pattern: `\s+`},
})
}

// grammarVisitor allows accessing the grammar AST nodes using the visitor pattern.
type grammarVisitor interface {
TylerHelmuth marked this conversation as resolved.
Show resolved Hide resolved
visitPath(v *path)
visitEditor(v *editor)
visitValue(v *value)
visitMathExprLiteral(v *mathExprLiteral)
}
Loading