Browse Source

feat: implement string function of expr (#2032)

Signed-off-by: Viking <1162777073@qq.com>
Viking 1 year ago
parent
commit
a49a91c8b6
4 changed files with 520 additions and 0 deletions
  1. 208 0
      pkg/ast/expr.go
  2. 18 0
      pkg/ast/expr_ref.go
  3. 237 0
      pkg/ast/expr_test.go
  4. 57 0
      pkg/ast/statement.go

+ 208 - 0
pkg/ast/expr.go

@@ -14,6 +14,11 @@
 
 package ast
 
+import (
+	"fmt"
+	"strconv"
+)
+
 type Node interface {
 	node()
 }
@@ -26,6 +31,8 @@ type NameNode interface {
 type Expr interface {
 	Node
 	expr()
+	// String function for the explain grammar, convert Expr to String
+	String() string
 }
 
 type Literal interface {
@@ -82,41 +89,101 @@ type Wildcard struct {
 
 func (pe *ParenExpr) expr() {}
 func (pe *ParenExpr) node() {}
+func (pe *ParenExpr) String() string {
+	e := ""
+	if pe.Expr != nil {
+		e += pe.Expr.String()
+	}
+	return "parenExpr:{ " + e + " }"
+}
 
 func (ae *ArrowExpr) expr() {}
 func (ae *ArrowExpr) node() {}
+func (ae *ArrowExpr) String() string {
+	e := ""
+	if ae.Expr != nil {
+		e += ae.Expr.String()
+	}
+	return "arrowExpr:{ " + e + " }"
+}
 
 func (be *BracketExpr) expr() {}
 func (be *BracketExpr) node() {}
+func (be *BracketExpr) String() string {
+	e := ""
+	if be.Expr != nil {
+		e += be.Expr.String()
+	}
+	return "bracketExpr:{ " + e + " }"
+}
 
 func (be *ColonExpr) expr() {}
 func (be *ColonExpr) node() {}
+func (be *ColonExpr) String() string {
+	s := ""
+	e := ""
+	if be.Start != nil {
+		s += "start:{ " + be.Start.String() + " }"
+	}
+	if be.End != nil {
+		if be.Start != nil {
+			e += ", "
+		}
+		e += "end:{ " + be.End.String() + " }"
+	}
+	return "ColonExpr:{ " + s + e + " }"
+}
 
 func (be *IndexExpr) expr() {}
 func (be *IndexExpr) node() {}
+func (be *IndexExpr) String() string {
+	i := ""
+	if be.Index != nil {
+		i += be.Index.String()
+	}
+	return i
+}
 
 func (w *Wildcard) expr() {}
 func (w *Wildcard) node() {}
+func (w *Wildcard) String() string {
+	return Tokens[w.Token]
+}
 
 func (bl *BooleanLiteral) expr()    {}
 func (bl *BooleanLiteral) literal() {}
 func (bl *BooleanLiteral) node()    {}
+func (bl *BooleanLiteral) String() string {
+	return strconv.FormatBool(bl.Val)
+}
 
 func (tl *TimeLiteral) expr()    {}
 func (tl *TimeLiteral) literal() {}
 func (tl *TimeLiteral) node()    {}
+func (tl *TimeLiteral) String() string {
+	return Tokens[tl.Val]
+}
 
 func (il *IntegerLiteral) expr()    {}
 func (il *IntegerLiteral) literal() {}
 func (il *IntegerLiteral) node()    {}
+func (il *IntegerLiteral) String() string {
+	return strconv.Itoa(il.Val)
+}
 
 func (nl *NumberLiteral) expr()    {}
 func (nl *NumberLiteral) literal() {}
 func (nl *NumberLiteral) node()    {}
+func (nl *NumberLiteral) String() string {
+	return fmt.Sprintf("%f", nl.Val)
+}
 
 func (sl *StringLiteral) expr()    {}
 func (sl *StringLiteral) literal() {}
 func (sl *StringLiteral) node()    {}
+func (sl *StringLiteral) String() string {
+	return sl.Val
+}
 
 type FuncType int
 
@@ -145,6 +212,24 @@ type Call struct {
 func (c *Call) expr()    {}
 func (c *Call) literal() {}
 func (c *Call) node()    {}
+func (c *Call) String() string {
+	args := ""
+	if c.Args != nil {
+		args = ", args:["
+		for i, arg := range c.Args {
+			args += arg.String()
+			if i != len(c.Args)-1 {
+				args += ", "
+			}
+		}
+		args += "]"
+	}
+	when := ""
+	if c.WhenExpr != nil {
+		when += ", when:{ " + c.WhenExpr.String() + " }"
+	}
+	return "Call:{ name:" + c.Name + args + when + " }"
+}
 
 type PartitionExpr struct {
 	Exprs []Expr
@@ -152,6 +237,16 @@ type PartitionExpr struct {
 
 func (pe *PartitionExpr) expr() {}
 func (pe *PartitionExpr) node() {}
+func (pe *PartitionExpr) String() string {
+	e := ""
+	for i, expr := range pe.Exprs {
+		e += expr.String()
+		if i != len(pe.Exprs)-1 {
+			e += ", "
+		}
+	}
+	return "PartitionExpr:[ " + e + " ]"
+}
 
 type BinaryExpr struct {
 	OP  Token
@@ -161,6 +256,18 @@ type BinaryExpr struct {
 
 func (be *BinaryExpr) expr() {}
 func (be *BinaryExpr) node() {}
+func (be *BinaryExpr) String() string {
+	info := ""
+	if be.LHS != nil && be.RHS != nil {
+		t := Tokens[be.OP]
+		if t == "[]" {
+			info += "binaryExpr:{ " + be.LHS.String() + "[" + be.RHS.String() + "] }"
+			return info
+		}
+		info += "binaryExpr:{ " + be.LHS.String() + " " + t + " " + be.RHS.String() + " }"
+	}
+	return info
+}
 
 type WhenClause struct {
 	// The condition Expression
@@ -170,6 +277,13 @@ type WhenClause struct {
 
 func (w *WhenClause) expr() {}
 func (w *WhenClause) node() {}
+func (w *WhenClause) String() string {
+	e := ""
+	if w.Expr != nil {
+		e += w.Expr.String()
+	}
+	return "whenClause:{ " + e + " }"
+}
 
 type CaseExpr struct {
 	// The compare value Expression. It can be a value Expression or nil.
@@ -181,6 +295,29 @@ type CaseExpr struct {
 
 func (c *CaseExpr) expr() {}
 func (c *CaseExpr) node() {}
+func (c *CaseExpr) String() string {
+	v := ""
+	if c.Value != nil {
+		v += "value:{ " + c.Value.String() + " }"
+	}
+	w := ""
+	if c.WhenClauses != nil && len(c.WhenClauses) != 0 {
+		if c.Value != nil {
+			w += ", "
+		}
+		w += "whenClauses:["
+		for i, clause := range c.WhenClauses {
+			if clause.Expr != nil {
+				w += "{ " + clause.String() + " }"
+				if i != len(c.WhenClauses)-1 {
+					w += ", "
+				}
+			}
+		}
+		w += "]"
+	}
+	return "caseExprValue:{ " + v + w + " }"
+}
 
 type ValueSetExpr struct {
 	LiteralExprs []Expr // ("A", "B", "C") or (1, 2, 3)
@@ -189,6 +326,27 @@ type ValueSetExpr struct {
 
 func (c *ValueSetExpr) expr() {}
 func (c *ValueSetExpr) node() {}
+func (c *ValueSetExpr) String() string {
+	le := ""
+	if c.LiteralExprs != nil && len(c.LiteralExprs) != 0 {
+		le += "literalExprs:["
+		for i, expr := range c.LiteralExprs {
+			le += expr.String()
+			if i != len(c.LiteralExprs)-1 {
+				le += ", "
+			}
+		}
+		le += "]"
+	}
+	a := ""
+	if c.ArrayExpr != nil {
+		if c.LiteralExprs != nil && len(c.LiteralExprs) != 0 {
+			a += ", "
+		}
+		a += "arrayExpr:{ " + c.ArrayExpr.String() + " }"
+	}
+	return "valueSetExpr:{ " + le + a + " }"
+}
 
 type BetweenExpr struct {
 	Lower  Expr
@@ -197,6 +355,20 @@ type BetweenExpr struct {
 
 func (b *BetweenExpr) expr() {}
 func (b *BetweenExpr) node() {}
+func (b *BetweenExpr) String() string {
+	low := ""
+	high := ""
+	if b.Lower != nil {
+		low += b.Lower.String()
+	}
+	if b.Higher != nil {
+		if b.Lower != nil {
+			high += ", "
+		}
+		high += b.Higher.String()
+	}
+	return "betweenExpr:{ " + low + high + " }"
+}
 
 type LimitExpr struct {
 	LimitCount *IntegerLiteral
@@ -204,6 +376,12 @@ type LimitExpr struct {
 
 func (l *LimitExpr) expr() {}
 func (l *LimitExpr) node() {}
+func (l *LimitExpr) String() string {
+	if l.LimitCount != nil {
+		return "limitExpr:{ " + l.LimitCount.String() + " }"
+	}
+	return ""
+}
 
 type StreamName string
 
@@ -221,6 +399,20 @@ type MetaRef struct {
 
 func (fr *MetaRef) expr() {}
 func (fr *MetaRef) node() {}
+func (fr *MetaRef) String() string {
+	sn := ""
+	n := ""
+	if fr.StreamName != "" {
+		sn += "streamName:" + string(fr.StreamName)
+	}
+	if fr.Name != "" {
+		if fr.StreamName != "" {
+			n += ", "
+		}
+		n += "fieldName:" + fr.Name
+	}
+	return "metaRef:{ " + sn + n + " }"
+}
 
 type JsonFieldRef struct {
 	Name string
@@ -228,6 +420,9 @@ type JsonFieldRef struct {
 
 func (fr *JsonFieldRef) expr() {}
 func (fr *JsonFieldRef) node() {}
+func (fr *JsonFieldRef) String() string {
+	return "jsonFieldName:" + fr.Name
+}
 
 type ColFuncField struct {
 	Name string
@@ -236,3 +431,16 @@ type ColFuncField struct {
 
 func (fr *ColFuncField) expr() {}
 func (fr *ColFuncField) node() {}
+func (fr *ColFuncField) String() string {
+	e := ""
+	if fr.Name != "" {
+		e += "name: " + fr.Name
+	}
+	if fr.Expr != nil {
+		if fr.Name != "" {
+			e += ", "
+		}
+		e += "expr:{ " + fr.Expr.String() + " }"
+	}
+	return "colFuncField:{ " + e + " }"
+}

+ 18 - 0
pkg/ast/expr_ref.go

@@ -27,6 +27,9 @@ type LikePattern struct {
 
 func (l *LikePattern) expr() {}
 func (l *LikePattern) node() {}
+func (l *LikePattern) String() string {
+	return "likePattern:" + l.Pattern.String()
+}
 
 func (l *LikePattern) Compile(likestr string) (*regexp.Regexp, error) {
 	regstr := strings.ReplaceAll(strings.NewReplacer(
@@ -64,6 +67,21 @@ func (fr *FieldRef) IsColumn() bool {
 	return fr.StreamName != AliasStream && fr.StreamName != ""
 }
 
+func (fr *FieldRef) String() string {
+	sn := ""
+	n := ""
+	if fr.StreamName != "" {
+		sn += string(fr.StreamName)
+	}
+	if fr.Name != "" {
+		if fr.StreamName != "" {
+			n += "."
+		}
+		n += fr.Name
+	}
+	return sn + n
+}
+
 func (fr *FieldRef) IsAlias() bool {
 	return fr.StreamName == AliasStream
 }

+ 237 - 0
pkg/ast/expr_test.go

@@ -0,0 +1,237 @@
+package ast
+
+import (
+	"math"
+	"regexp"
+	"testing"
+
+	"github.com/lf-edge/ekuiper/internal/testx"
+)
+
+func init() {
+	testx.InitEnv()
+}
+
+func Test_exprStringPlan(t *testing.T) {
+	re1, _ := regexp.Compile("^foo$")
+	test := []struct {
+		e   Expr
+		res string
+	}{
+		{
+			e: &BetweenExpr{
+				Lower: &IntegerLiteral{
+					Val: 0,
+				},
+				Higher: &IntegerLiteral{
+					Val: 10,
+				},
+			},
+			res: "betweenExpr:{ 0, 10 }",
+		},
+		{
+			e: &BinaryExpr{
+				OP: SUBSET,
+				LHS: &FieldRef{
+					StreamName: "src1",
+					Name:       "myarray",
+				},
+				RHS: &IndexExpr{Index: &FieldRef{
+					StreamName: "src1",
+					Name:       "temp",
+				}},
+			},
+			res: "binaryExpr:{ src1.myarray[src1.temp] }",
+		},
+		{
+			e:   &BooleanLiteral{Val: true},
+			res: "true",
+		},
+		{
+			e: &Call{Name: "count", FuncId: 0, Args: []Expr{&Wildcard{
+				Token: ASTERISK,
+			}}, FuncType: FuncTypeAgg},
+			res: "Call:{ name:count, args:[*] }",
+		},
+		{
+			e: &CaseExpr{
+				WhenClauses: []*WhenClause{
+					{
+						Expr: &BinaryExpr{
+							OP: BETWEEN,
+							LHS: &Call{
+								Name:     "lag",
+								FuncId:   0,
+								FuncType: FuncType(0),
+								Args: []Expr{
+									&FieldRef{
+										StreamName: "src1",
+										Name:       "temp",
+									},
+								},
+								CachedField: "$$a_lag_0",
+								Cached:      true,
+								WhenExpr: &BinaryExpr{
+									OP: GT,
+									LHS: &Call{
+										Name:     "lag",
+										FuncId:   1,
+										FuncType: FuncType(0),
+										Args: []Expr{
+											&FieldRef{
+												StreamName: "src1",
+												Name:       "id1",
+											},
+										},
+										CachedField: "$$a_lag_1",
+										Cached:      true,
+									},
+									RHS: &IntegerLiteral{
+										Val: 1,
+									},
+								},
+							},
+							RHS: &BetweenExpr{
+								Lower: &IntegerLiteral{
+									Val: 0,
+								},
+								Higher: &IntegerLiteral{
+									Val: 10,
+								},
+							},
+						},
+						Result: &IntegerLiteral{
+							Val: 1,
+						},
+					},
+					{
+						&BinaryExpr{
+							OP: BETWEEN,
+							LHS: &Call{
+								Name:     "lag",
+								FuncId:   0,
+								FuncType: FuncType(0),
+								Args: []Expr{
+									&FieldRef{
+										StreamName: "src1",
+										Name:       "temp",
+									},
+								},
+								CachedField: "$$a_lag_0",
+								Cached:      true,
+								WhenExpr: &BinaryExpr{
+									OP: GT,
+									LHS: &Call{
+										Name:     "lag",
+										FuncId:   1,
+										FuncType: FuncType(0),
+										Args: []Expr{
+											&FieldRef{
+												StreamName: "src1",
+												Name:       "id1",
+											},
+										},
+										CachedField: "$$a_lag_1",
+										Cached:      true,
+									},
+									RHS: &IntegerLiteral{
+										Val: 1,
+									},
+								},
+							},
+							RHS: &BetweenExpr{
+								Lower: &IntegerLiteral{
+									Val: 0,
+								},
+								Higher: &IntegerLiteral{
+									Val: 10,
+								},
+							},
+						},
+						&IntegerLiteral{
+							Val: 2,
+						},
+					},
+				},
+				ElseClause: &IntegerLiteral{
+					Val: 0,
+				},
+				Value: &IntegerLiteral{
+					Val: 12,
+				},
+			},
+			res: "caseExprValue:{ value:{ 12 }, whenClauses:[{ whenClause:{ binaryExpr:{ Call:{ name:lag, args:[src1.temp], when:{ binaryExpr:{ Call:{ name:lag, args:[src1.id1] } > 1 } } } BETWEEN betweenExpr:{ 0, 10 } } } }, { whenClause:{ binaryExpr:{ Call:{ name:lag, args:[src1.temp], when:{ binaryExpr:{ Call:{ name:lag, args:[src1.id1] } > 1 } } } BETWEEN betweenExpr:{ 0, 10 } } } }] }",
+		},
+		{
+			e:   &JsonFieldRef{Name: "Device"},
+			res: "jsonFieldName:Device",
+		},
+		{
+			e:   &NumberLiteral{Val: 1.23},
+			res: "1.230000",
+		},
+		{
+			e:   &StringLiteral{Val: "v1"},
+			res: "v1",
+		},
+		{
+			e:   &TimeLiteral{Val: 2},
+			res: "WS",
+		},
+		{
+			e: &MetaRef{
+				Name:       "device",
+				StreamName: DefaultStream,
+			},
+			res: "metaRef:{ streamName:$$default, fieldName:device }",
+		},
+		{
+			e:   &PartitionExpr{Exprs: []Expr{&FieldRef{Name: "temp", StreamName: "src1"}, &FieldRef{Name: "current", StreamName: "src2"}}},
+			res: "PartitionExpr:[ src1.temp, src2.current ]",
+		},
+		{
+			e:   &SortField{Uname: "name", Name: "name", Ascending: true, FieldExpr: &FieldRef{Name: "name", StreamName: DefaultStream}},
+			res: "sortField:{ name:name, ascending:true, fieldExpr:{ $$default.name } }",
+		},
+		{
+			e:   &BracketExpr{Expr: &ColonExpr{Start: &IntegerLiteral{Val: 0}, End: &IntegerLiteral{Val: math.MinInt32}}},
+			res: "bracketExpr:{ ColonExpr:{ start:{ 0 }, end:{ -2147483648 } } }",
+		},
+		{
+			e:   &ArrowExpr{Expr: &ColonExpr{Start: &IntegerLiteral{Val: 0}, End: &IntegerLiteral{Val: math.MinInt32}}},
+			res: "arrowExpr:{ ColonExpr:{ start:{ 0 }, end:{ -2147483648 } } }",
+		},
+		{
+			e: &ValueSetExpr{
+				LiteralExprs: []Expr{&StringLiteral{"A"}, &StringLiteral{"B"}},
+				ArrayExpr:    &StringLiteral{"A, B"},
+			},
+			res: "valueSetExpr:{ literalExprs:[A, B], arrayExpr:{ A, B } }",
+		},
+		{
+			e: &ColFuncField{
+				Name: "ABC",
+				Expr: &StringLiteral{Val: ""},
+			},
+			res: "colFuncField:{ name: ABC, expr:{  } }",
+		},
+		{
+			e:   &LikePattern{Expr: &StringLiteral{Val: "foo"}, Pattern: re1},
+			res: "likePattern:^foo$",
+		},
+		{
+			e: &LimitExpr{
+				LimitCount: &IntegerLiteral{Val: 10},
+			},
+			res: "limitExpr:{ 10 }",
+		},
+	}
+
+	for i := 0; i < len(test); i++ {
+		res := test[i].res
+		str := test[i].e.String()
+		if str != res {
+			t.Errorf("case %d: expect validate %v but got %v", i, res, str)
+		}
+	}
+}

+ 57 - 0
pkg/ast/statement.go

@@ -14,6 +14,8 @@
 
 package ast
 
+import "strconv"
+
 type Statement interface {
 	stmt()
 	Node
@@ -119,6 +121,23 @@ const (
 	CROSS_JOIN
 )
 
+func (j JoinType) String() string {
+	switch j {
+	case LEFT_JOIN:
+		return "LEFT_JOIN"
+	case INNER_JOIN:
+		return "INNER_JOIN"
+	case RIGHT_JOIN:
+		return "RIGHT_JOIN"
+	case FULL_JOIN:
+		return "FULL_JOIN"
+	case CROSS_JOIN:
+		return "CROSS_JOIN"
+	default:
+		return ""
+	}
+}
+
 type Join struct {
 	Name     string
 	Alias    string
@@ -172,6 +191,24 @@ const (
 	COUNT_WINDOW
 )
 
+func (w WindowType) String() string {
+	switch w {
+	case NOT_WINDOW:
+		return "NOT_WINDOW"
+	case TUMBLING_WINDOW:
+		return "TUMBLING_WINDOW"
+	case HOPPING_WINDOW:
+		return "HOPPING_WINDOW"
+	case SLIDING_WINDOW:
+		return "SLIDING_WINDOW"
+	case SESSION_WINDOW:
+		return "SESSION_WINDOW"
+	case COUNT_WINDOW:
+		return "COUNT_WINDOW"
+	}
+	return ""
+}
+
 type Window struct {
 	TriggerCondition Expr
 	WindowType       WindowType
@@ -193,6 +230,26 @@ type SortField struct {
 	Expr
 }
 
+func (sf *SortField) String() string {
+	fe := ""
+	if sf.FieldExpr != nil {
+		fe += ", fieldExpr:{ " + sf.FieldExpr.String() + " }"
+	}
+	return "sortField:{ name:" + sf.Name + ", ascending:" + strconv.FormatBool(sf.Ascending) + fe + " }"
+}
+
+func (wd *Window) String() string {
+	tu := ""
+	if wd.TimeUnit != nil {
+		tu += ", timeUnit: " + wd.TimeUnit.String() + " "
+	}
+	filter := ""
+	if wd.Filter != nil {
+		filter += ", " + wd.Filter.String()
+	}
+	return "window:{ windowType:" + wd.WindowType.String() + tu + filter + " }"
+}
+
 type SortFields []SortField
 
 func (d SortFields) node() {}