Skip to content

Commit 13cc016

Browse files
支持切片语法
1 parent 89d59b8 commit 13cc016

File tree

8 files changed

+1097
-414
lines changed

8 files changed

+1097
-414
lines changed

pkg/ast/expr.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,9 @@ type SliceExpr struct {
319319
Obj *Node
320320
Start *Node
321321
End *Node
322+
Step *Node
323+
Colon2 *Node
322324
LBracket token.LnColPos
323-
Colon token.LnColPos
324325
RBracket token.LnColPos
325326
}
326327

@@ -329,15 +330,21 @@ func (e *SliceExpr) IsExpr() bool {
329330
}
330331

331332
func (e *SliceExpr) String() string {
332-
startStr := "nil"
333+
startStr := ""
333334
if e.Start != nil {
334335
startStr = e.Start.String()
335336
}
336-
endStr := "nil"
337+
endStr := ""
337338
if e.End != nil {
338339
endStr = e.End.String()
339340
}
340-
return fmt.Sprintf("%s[%s:%s]", e.Obj.String(), startStr, endStr)
341+
stepStr := ""
342+
if e.Step != nil {
343+
stepStr = ":" + e.Step.String()
344+
} else if e.Colon2 != nil {
345+
stepStr = ":"
346+
}
347+
return fmt.Sprintf("%s[%s:%s%s]", e.Obj.String(), startStr, endStr, stepStr)
341348
}
342349

343350
type AssignmentExpr struct {

pkg/engine/runtime/checkstmt.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ func RunStmtCheck(ctx *Task, ctxCheck *ContextCheck, node *ast.Node) *errchain.P
7171

7272
case ast.TypeCallExpr:
7373
return RunCallExprCheck(ctx, ctxCheck, node.CallExpr())
74+
case ast.TypeSliceExpr:
75+
return RunSliceExprCheck(ctx, ctxCheck, node.SliceExpr())
7476

7577
case ast.TypeIfelseStmt:
7678
return RunIfElseStmtCheck(ctx, ctxCheck, node.IfelseStmt())
@@ -191,6 +193,30 @@ func RunCallExprCheck(ctx *Task, ctxCheck *ContextCheck, expr *ast.CallExpr) *er
191193
return funcCheck(ctx, expr)
192194
}
193195

196+
func RunSliceExprCheck(ctx *Task, ctxCheck *ContextCheck, expr *ast.SliceExpr) *errchain.PlError {
197+
if err := RunStmtCheck(ctx, ctxCheck, expr.Obj); err != nil {
198+
return err
199+
}
200+
201+
if expr.Start != nil {
202+
if err := RunStmtCheck(ctx, ctxCheck, expr.Start); err != nil {
203+
return err
204+
}
205+
}
206+
207+
if expr.End != nil {
208+
if err := RunStmtCheck(ctx, ctxCheck, expr.End); err != nil {
209+
return err
210+
}
211+
}
212+
if expr.End != nil {
213+
if err := RunStmtCheck(ctx, ctxCheck, expr.Step); err != nil {
214+
return err
215+
}
216+
}
217+
return nil
218+
}
219+
194220
func RunAssignmentExprCheck(ctx *Task, ctxCheck *ContextCheck, expr *ast.AssignmentExpr) *errchain.PlError {
195221
if err := RunStmtCheck(ctx, ctxCheck, expr.RHS); err != nil {
196222
return err

pkg/engine/runtime/runtime.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ func RunStmt(ctx *Task, node *ast.Node) (any, ast.DType, *errchain.PlError) {
381381
return RunAssignmentExpr(ctx, node.AssignmentExpr())
382382
case ast.TypeCallExpr:
383383
return RunCallExpr(ctx, node.CallExpr())
384+
case ast.TypeSliceExpr:
385+
return RunSliceExpr(ctx, node.SliceExpr())
384386
case ast.TypeInExpr:
385387
return RunInExpr(ctx, node.InExpr())
386388
case ast.TypeListLiteral:
@@ -1006,7 +1008,133 @@ func RunCallExpr(ctx *Task, expr *ast.CallExpr) (any, ast.DType, *errchain.PlErr
10061008
}
10071009
return nil, ast.Void, nil
10081010
}
1011+
func RunSliceExpr(ctx *Task, expr *ast.SliceExpr) (any, ast.DType, *errchain.PlError) {
1012+
obj, objT, err := RunStmt(ctx, expr.Obj)
1013+
if err != nil {
1014+
return nil, ast.Invalid, err
1015+
}
1016+
var start, end, step any
1017+
var startT, endT, stepT ast.DType
1018+
if expr.Start != nil {
1019+
start, startT, err = RunStmt(ctx, expr.Start)
1020+
if err != nil {
1021+
return nil, ast.Invalid, err
1022+
}
1023+
}
1024+
if expr.End != nil {
1025+
end, endT, err = RunStmt(ctx, expr.End)
1026+
if err != nil {
1027+
return nil, ast.Invalid, err
1028+
}
1029+
}
1030+
if expr.Step != nil {
1031+
step, stepT, err = RunStmt(ctx, expr.Step)
1032+
if err != nil {
1033+
return nil, ast.Invalid, err
1034+
}
1035+
}
1036+
var startInt, endInt, stepInt int
1037+
var length int
1038+
switch objT { //nolint:exhaustive
1039+
case ast.String:
1040+
length = len(obj.(string))
1041+
case ast.List, ast.DType(ast.TypeSliceExpr):
1042+
length = len(obj.([]any))
1043+
default:
1044+
return nil, ast.Invalid, NewRunError(ctx, "invalid obj type", expr.Obj.StartPos())
1045+
}
10091046

1047+
if step != nil {
1048+
if stepT != ast.Int {
1049+
return nil, ast.Invalid, NewRunError(ctx, "invalid step type", expr.Step.StartPos())
1050+
}
1051+
stepInt = cast.ToInt(step)
1052+
if stepInt == 0 {
1053+
return nil, ast.Invalid, NewRunError(ctx, "step must be non-zero", expr.Step.StartPos())
1054+
}
1055+
} else {
1056+
stepInt = 1
1057+
}
1058+
1059+
if start != nil {
1060+
if startT != ast.Int {
1061+
return nil, ast.Invalid, NewRunError(ctx, "invalid start type", expr.Start.StartPos())
1062+
}
1063+
startInt = cast.ToInt(start)
1064+
if startInt < 0 {
1065+
startInt = length + startInt
1066+
}
1067+
} else if stepInt > 0 {
1068+
startInt = 0
1069+
} else {
1070+
startInt = length - 1
1071+
}
1072+
1073+
if end != nil {
1074+
if endT != ast.Int {
1075+
return nil, ast.Invalid, NewRunError(ctx, "invalid end type", expr.End.StartPos())
1076+
}
1077+
endInt = cast.ToInt(end)
1078+
if endInt < 0 {
1079+
endInt = length + endInt
1080+
}
1081+
} else if stepInt > 0 {
1082+
endInt = length
1083+
} else {
1084+
endInt = -1
1085+
}
1086+
1087+
switch objT {
1088+
case ast.String:
1089+
str := obj.(string)
1090+
if stepInt > 0 {
1091+
result := ""
1092+
if startInt < 0 {
1093+
startInt = 0
1094+
}
1095+
for i := startInt; i < endInt && i < length; i += stepInt {
1096+
result += string(str[i])
1097+
}
1098+
return result, ast.String, nil
1099+
} else {
1100+
result := ""
1101+
if startInt > length-1 {
1102+
startInt = length - 1
1103+
}
1104+
for i := startInt; i > endInt && i >= 0; i += stepInt {
1105+
result += string(str[i])
1106+
}
1107+
return result, ast.String, nil
1108+
}
1109+
default:
1110+
list := obj.([]any)
1111+
if stepInt > 0 {
1112+
if startInt < 0 {
1113+
startInt = 0
1114+
}
1115+
if endInt > length {
1116+
endInt = length
1117+
}
1118+
result := make([]any, 0, (endInt-startInt+stepInt-1)/stepInt)
1119+
for i := startInt; i < endInt; i += stepInt {
1120+
result = append(result, list[i])
1121+
}
1122+
return result, ast.List, nil
1123+
} else {
1124+
if startInt > length-1 {
1125+
startInt = length - 1
1126+
}
1127+
if endInt < 0 {
1128+
endInt = -1
1129+
}
1130+
result := make([]any, 0, (startInt-endInt-stepInt-1)/(-stepInt))
1131+
for i := startInt; i > endInt; i += stepInt {
1132+
result = append(result, list[i])
1133+
}
1134+
return result, ast.List, nil
1135+
}
1136+
}
1137+
}
10101138
func typePromotion(l ast.DType, r ast.DType) ast.DType {
10111139
if l == ast.Float || r == ast.Float {
10121140
return ast.Float

pkg/engine/runtime/runtime_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,160 @@ func TestUnaryExpr(t *testing.T) {
392392
"abc": "abcd",
393393
}, inData.data)
394394
}
395+
func TestSliceExpr(t *testing.T) {
396+
cases := []struct {
397+
name string
398+
pl string
399+
v map[string]any
400+
fail bool
401+
}{
402+
{
403+
name: "valid slice string with positive step",
404+
pl: `
405+
s = "hello"
406+
v1 = s[1:4:2]
407+
v2 = s[1:4]
408+
v3 = s[1:]
409+
v4 = s[:]
410+
v5 = s[-1:10]
411+
v6 = s[10:-1]
412+
v7 = s[-10:]
413+
v8 = s[::2]
414+
add_key("v1", v1)
415+
add_key("v2", v2)
416+
add_key("v3", v3)
417+
add_key("v4", v4)
418+
add_key("v5", v5)
419+
add_key("v6", v6)
420+
add_key("v7", v7)
421+
add_key("v8", v8)
422+
`,
423+
v: map[string]any{
424+
"v1": "el",
425+
"v2": "ell",
426+
"v3": "ello",
427+
"v4": "hello",
428+
"v5": "o",
429+
"v6": "",
430+
"v7": "hello",
431+
"v8": "hlo",
432+
},
433+
},
434+
{
435+
name: "valid slice string with negative step",
436+
pl: `
437+
s = "hello"
438+
v1 = s[4:1:-1]
439+
v2 = s[::-2]
440+
v3 = s[10:-10:-1]
441+
v4 = s[-10:1:-1]
442+
v5 = s[-1:-6:-1]
443+
add_key("v1", v1)
444+
add_key("v2", v2)
445+
add_key("v3", v3)
446+
add_key("v4", v4)
447+
add_key("v5", v5)
448+
`,
449+
v: map[string]any{
450+
"v1": "oll",
451+
"v2": "olh",
452+
"v3": "olleh",
453+
"v4": "",
454+
"v5": "olleh",
455+
},
456+
},
457+
{
458+
name: "nested slice string",
459+
pl: `
460+
s = "hello"
461+
v1 = s[10:-10:-1][::-2]
462+
v2 = s[-1:-6:-1][::2]
463+
add_key("v1", v1)
464+
add_key("v2", v2)
465+
`,
466+
v: map[string]any{
467+
"v1": "hlo",
468+
"v2": "olh",
469+
},
470+
},
471+
{
472+
name: "valid slice list with positive step",
473+
pl: `
474+
l = [1, 2, 3, 4, 5]
475+
v1 = l[1:4:2]
476+
v2 = l[1:10]
477+
add_key("v1", v1)
478+
add_key("v2", v2)
479+
`,
480+
v: map[string]any{
481+
"v1": "[2,4]",
482+
"v2": "[2,3,4,5]",
483+
},
484+
},
485+
{
486+
name: "slice list with negative step",
487+
pl: `
488+
l = [1, 2, 3, 4, 5]
489+
v1 = l[4:1:-1]
490+
add_key("v1", v1)
491+
`,
492+
v: map[string]any{
493+
"v1": "[5,4,3]",
494+
},
495+
},
496+
{
497+
name: "slice with invalid step",
498+
pl: `
499+
s = "hello"
500+
result = s[1:4:0]
501+
add_key("result", result)
502+
`,
503+
fail: true,
504+
},
505+
}
506+
507+
for _, c := range cases {
508+
t.Run(c.name, func(t *testing.T) {
509+
stmts, err := parseScript(c.pl)
510+
if err != nil {
511+
if c.fail {
512+
return
513+
}
514+
t.Fatal(err)
515+
}
516+
517+
script := &Script{
518+
CallRef: nil,
519+
FuncCall: map[string]FuncCall{
520+
"add_key": addkeytest,
521+
},
522+
Name: "abc",
523+
Namespace: "default",
524+
Content: c.pl,
525+
Ast: stmts,
526+
}
527+
errR := script.Check(map[string]FuncCheck{
528+
"add_key": addkeycheck,
529+
})
530+
if errR != nil {
531+
t.Fatal(*errR)
532+
}
533+
534+
inData := &inputImpl{
535+
data: map[string]any{},
536+
}
395537

538+
errR = script.Run(inData, nil)
539+
if errR != nil {
540+
if c.fail {
541+
return
542+
}
543+
t.Fatal(errR.Error())
544+
}
545+
assert.Equal(t, c.v, inData.data)
546+
})
547+
}
548+
}
396549
func TestUnaryErrExpr(t *testing.T) {
397550
pl := `
398551
if !ckfn("") {

0 commit comments

Comments
 (0)