Skip to content

Commit

Permalink
feat: add the "in" operator
Browse files Browse the repository at this point in the history
  • Loading branch information
vircoys committed Jun 25, 2023
1 parent 3a82402 commit 5d7745f
Show file tree
Hide file tree
Showing 7 changed files with 617 additions and 333 deletions.
13 changes: 13 additions & 0 deletions pkg/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (

TypeListInitExpr
TypeMapInitExpr
TypeInExpr

TypeParenExpr

Expand Down Expand Up @@ -56,6 +57,8 @@ func (t NodeType) String() string {
return "Invalid"
case TypeIdentifier:
return "Identifier"
case TypeInExpr:
return "InExpr"
case TypeStringLiteral:
return "StringLiteral"
case TypeIntegerLiteral:
Expand Down Expand Up @@ -148,6 +151,7 @@ type Node struct {

AttrExpr *AttrExpr
IndexExpr *IndexExpr
InExpr *InExpr

ArithmeticExpr *ArithmeticExpr
ConditionalExpr *ConditionalExpr
Expand Down Expand Up @@ -207,6 +211,8 @@ func (node *Node) String() string {
return node.ContinueStmt.String()
case TypeBreakStmt:
return node.BreakStmt.String()
case TypeInExpr:
return node.InExpr.String()
}
return "node conv to string failed"
}
Expand Down Expand Up @@ -302,6 +308,13 @@ func WrapConditionExpr(node *ConditionalExpr) *Node {
}
}

func WrapInExpr(node *InExpr) *Node {
return &Node{
NodeType: TypeInExpr,
InExpr: node,
}
}

func WrapAssignmentExpr(node *AssignmentExpr) *Node {
return &Node{
NodeType: TypeAssignmentExpr,
Expand Down
14 changes: 14 additions & 0 deletions pkg/ast/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,20 @@ func (e *ConditionalExpr) String() string {
return fmt.Sprintf("%s %s %s", e.LHS.String(), e.Op, e.RHS.String())
}

type InExpr struct {
Op Op
LHS, RHS *Node
OpPos token.LnColPos
}

func (e *InExpr) IsExpr() bool {
return true
}

func (e *InExpr) String() string {
return fmt.Sprintf("%s %s %s", e.LHS.String(), e.Op, e.RHS.String())
}

type ArithmeticExpr struct {
Op Op
LHS, RHS *Node
Expand Down
60 changes: 58 additions & 2 deletions pkg/engine/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package runtime
import (
"fmt"
"reflect"
"strings"

"github.com/GuanceCloud/platypus/pkg/ast"
"github.com/GuanceCloud/platypus/pkg/errchain"
Expand Down Expand Up @@ -362,6 +363,8 @@ func RunStmt(ctx *Context, node *ast.Node) (any, ast.DType, *errchain.PlError) {
return RunAssignmentExpr(ctx, node.AssignmentExpr)
case ast.TypeCallExpr:
return RunCallExpr(ctx, node.CallExpr)
case ast.TypeInExpr:
return RunInExpr(ctx, node.InExpr)
case ast.TypeListInitExpr:
return RunListInitExpr(ctx, node.ListInitExpr)
case ast.TypeIdentifier:
Expand Down Expand Up @@ -545,6 +548,59 @@ func RunParenExpr(ctx *Context, expr *ast.ParenExpr) (any, ast.DType, *errchain.

// BinarayExpr

func RunInExpr(ctx *Context, expr *ast.InExpr) (any, ast.DType, *errchain.PlError) {
lhs, lhsT, err := RunStmt(ctx, expr.LHS)
if err != nil {
return nil, ast.Invalid, err
}

rhs, rhsT, err := RunStmt(ctx, expr.RHS)
if err != nil {
return nil, ast.Invalid, err
}

switch rhsT {
case ast.String:
if lhsT != ast.String {
return false, ast.Bool, NewRunError(ctx, fmt.Sprintf(
"unsupported lhs data type: %s", lhsT), expr.OpPos)
}
if s, ok := lhs.(string); ok {
if v, ok := rhs.(string); ok {
return strings.Contains(v, s), ast.Bool, nil
}
}

return false, ast.Bool, nil
case ast.Map:
if lhsT != ast.String {
return false, ast.Bool, NewRunError(ctx, fmt.Sprintf(
"unsupported lhs data type: %s", lhsT), expr.OpPos)
}
if s, ok := lhs.(string); ok {
if v, ok := rhs.(map[string]any); ok {
if _, ok := v[s]; ok {
return true, ast.Bool, nil
}
}
}
return false, ast.Bool, nil
case ast.List:
if v, ok := rhs.([]any); ok {
for _, elem := range v {
if reflect.DeepEqual(lhs, elem) {
return true, ast.Bool, nil
}
}
}
return false, ast.Bool, nil

default:
return false, ast.Bool, NewRunError(ctx, fmt.Sprintf(
"unsupported rhs data type: %s", rhsT), expr.OpPos)
}
}

func RunConditionExpr(ctx *Context, expr *ast.ConditionalExpr) (any, ast.DType, *errchain.PlError) {
lhs, lhsT, err := RunStmt(ctx, expr.LHS)
if err != nil {
Expand Down Expand Up @@ -593,9 +649,9 @@ func RunArithmeticExpr(ctx *Context, expr *ast.ArithmeticExpr) (any, ast.DType,
return nil, ast.Invalid, errOpInt
}

if !arithType(lhsValType) {
if !arithType(rhsValType) {
return nil, ast.Invalid, NewRunError(ctx, fmt.Sprintf(
"unsupported rhs data type: %s", lhsValType), expr.OpPos)
"unsupported rhs data type: %s", rhsValType), expr.OpPos)
}

// string
Expand Down
161 changes: 159 additions & 2 deletions pkg/engine/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ add_key(len2, len("123"))
},
Name: "abc",
Namespace: "default",
Category: "",
FilePath: "",
Content: pl,
Ast: stmts,
Expand Down Expand Up @@ -198,6 +197,155 @@ add_key(len2, len("123"))
}, inData.data)
}

func TestForInStmt(t *testing.T) {
cases := []struct {
n string
s string
e map[string]any
fail bool
}{
{
n: "t1",
s: `
a = 0
b = {"a":1, "v":2}
for x in b {
a = a + b[x]
}
add_key("a", a)
`,
e: map[string]any{
"a": int64(3),
},
},
{
n: "t1",
s: `
a = ""
for x in "defgh" {
a = a + x
}
add_key("a", a)
`,
e: map[string]any{
"a": "defgh",
},
},
}

for _, c := range cases {
t.Run(c.n, func(t *testing.T) {
stmts, err := parseScript(c.s)
if err != nil {
if c.fail {
return
}
t.Fatal(err)
}

script := &Script{
CallRef: nil,
FuncCall: map[string]FuncCall{
"add_key": addkeytest,
},
Name: "abc",
Namespace: "default",
Content: c.s,
Ast: stmts,
}
errR := CheckScript(script, map[string]FuncCheck{
"add_key": addkeycheck,
})
if errR != nil {
t.Fatal(*errR)
}

inData := &inputImpl{
data: map[string]any{},
}

errR = RunScriptWithRMapIn(script, inData, nil)
if errR != nil {
t.Fatal(errR.Error())
}
assert.Equal(t, c.e, inData.data)
})
}
}

func TestInExpr(t *testing.T) {

pl := `
add_key("t1", "a" in [1,"a"])
add_key("t1_1", nil in [1,"a"])
add_key("t1_2", 1 in [1,"a"])
add_key("t1_3", nil in [nil,"a"])
add_key("t2", "def" in "abcdef")
add_key("t2_1", "x" in "abcdef")
add_key("t3", "a" in {"a": 1})
add_key("t3_1", "b" in {"a": 1})
add_key("t3_2", "a" in {})
x = 1
y = [1]
add_key("t4", x in y)
`
stmts, err := parseScript(pl)
if err != nil {
t.Fatal(err)
}

script := &Script{
CallRef: nil,
FuncCall: map[string]FuncCall{
"test": callexprtest,
"add_key": addkeytest,
"len": lentest,
},
Name: "abc",
Namespace: "default",
Category: "",
FilePath: "",
Content: pl,
Ast: stmts,
}
errR := CheckScript(script, map[string]FuncCheck{
"add_key": addkeycheck,
"len": lencheck,
})
if errR != nil {
t.Fatal(*errR)
}

inData := &inputImpl{
data: map[string]any{},
}

errR = RunScriptWithRMapIn(script, inData, nil)
if errR != nil {
t.Fatal(errR.Error())
}
assert.Equal(t, map[string]any{
"t1": true,
"t1_1": false,
"t1_2": true,
"t1_3": true,
"t2": true,
"t2_1": false,
"t3": true,
"t3_1": false,
"t3_2": false,

"t4": true,
}, inData.data)
}

func TestCondTrue(t *testing.T) {
cases := []struct {
val any
Expand Down Expand Up @@ -595,7 +743,16 @@ func callexprtest(ctx *Context, callExpr *ast.CallExpr) *errchain.PlError {
}

func addkeytest(ctx *Context, callExpr *ast.CallExpr) *errchain.PlError {
key := callExpr.Param[0].Identifier.Name
var key string
switch callExpr.Param[0].NodeType {
case ast.TypeIdentifier:
key = callExpr.Param[0].Identifier.Name
case ast.TypeStringLiteral:
key = callExpr.Param[0].StringLiteral.Val
default:
return NewRunError(ctx, "key type", callExpr.NamePos)
}

if len(callExpr.Param) > 1 {
val, dtype, err := RunStmt(ctx, callExpr.Param[1])
if err != nil {
Expand Down
14 changes: 10 additions & 4 deletions pkg/parser/gram.y
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ NIL NULL IF ELIF ELSE
paren_expr
index_expr
attr_expr
/* in_expr */
in_expr
expr
map_init
map_init_start
Expand All @@ -114,6 +114,7 @@ NIL NULL IF ELIF ELSE
%right EQ
%left OR
%left AND
%left IN
%left GTE GT NEQ EQEQ LTE LT
%left ADD SUB
%left MUL DIV MOD
Expand Down Expand Up @@ -170,7 +171,7 @@ value_stmt: expr
;

/* expression */
expr : array_elem | list_init | map_init | paren_expr | call_expr | binary_expr | attr_expr | index_expr ; // arithmeticExpr
expr : array_elem | list_init | map_init | paren_expr | call_expr | binary_expr | attr_expr | index_expr | in_expr; // arithmeticExpr


break_stmt: BREAK
Expand All @@ -186,8 +187,8 @@ continue_stmt: CONTINUE
for identifier IN list_init
for identifier IN string
*/
for_in_stmt : FOR identifier IN expr stmt_block
{ $$ = yylex.(*parser).newForInStmt($2, $4, $5, $1, $3) }
for_in_stmt : FOR in_expr stmt_block
{ $$ = yylex.(*parser).newForInStmt($2, $3, $1) }
;


Expand Down Expand Up @@ -251,6 +252,11 @@ empty_block : LEFT_BRACE RIGHT_BRACE
;


in_expr : expr IN expr
{ $$ = yylex.(*parser).newInExpr($1, $3, $2) }
;


call_expr : identifier LEFT_PAREN function_args RIGHT_PAREN
{
$$ = yylex.(*parser).newCallExpr($1, $3, $2, $4)
Expand Down
Loading

0 comments on commit 5d7745f

Please sign in to comment.