diff --git a/pkg/compiler/compiler_func_test.go b/pkg/compiler/compiler_func_test.go index cc402ff0..bf185bdd 100644 --- a/pkg/compiler/compiler_func_test.go +++ b/pkg/compiler/compiler_func_test.go @@ -6,54 +6,43 @@ import ( func TestFunctionCall(t *testing.T) { RunUseCases(t, []UseCase{ + //{ + // "RETURN TYPENAME(1)", + // "int", + // nil, + //}, + //{ + // "WAIT(10) RETURN 1", + // 1, + // nil, + //}, + //{ + // "LET duration = 10 WAIT(duration) RETURN 1", + // 1, + // nil, + //}, { - "RETURN TYPENAME(1)", - "int", + "RETURN (FALSE OR T::FAIL())?", nil, - }, - { - "WAIT(10) RETURN 1", - 1, nil, }, { - "LET duration = 10 WAIT(duration) RETURN 1", - 1, + "RETURN T::FAIL()?", + nil, nil, }, //{ - // "RETURN (FALSE OR T::FAIL())?", - // nil, - // nil, - //}, - //{ - // "RETURN ERROR()?", - // nil, - // nil, + // `FOR i IN [1, 2, 3, 4] + // LET duration = 10 + // + // WAIT(duration) + // + // RETURN i * 2`, + // []int{2, 4, 6, 8}, + // ShouldEqualJSON, //}, }) - // - //Convey("Should compile function call inside FOR IN statement", t, func() { - // c := compiler.New() - // - // p, err := c.Compile(` - // FOR i IN [1, 2, 3, 4] - // LET duration = 10 - // - // WAIT(duration) - // - // RETURN i * 2 - // `) - // - // So(err, ShouldBeNil) - // - // out, err := p.Run(context.Background()) - // - // So(err, ShouldBeNil) - // - // So(string(out), ShouldEqual, `[2,4,6,8]`) - //}) // //Convey("Should handle errors when ? is used", t, func() { // c := compiler.New() diff --git a/pkg/compiler/visitor.go b/pkg/compiler/visitor.go index 902fb95f..2c51d11f 100644 --- a/pkg/compiler/visitor.go +++ b/pkg/compiler/visitor.go @@ -297,7 +297,7 @@ func (v *visitor) VisitFunctionCallExpression(ctx *fql.FunctionCallExpressionCon name += call.FunctionName().GetText() - //regularCall := ctx.ErrorOperator() == nil + isNonOptional := ctx.ErrorOperator() == nil v.emit(runtime.OpConstant, v.addConstant(values.String(name))) @@ -310,17 +310,41 @@ func (v *visitor) VisitFunctionCallExpression(ctx *fql.FunctionCallExpressionCon switch size { case 0: - v.emit(runtime.OpCall, 0) + if isNonOptional { + v.emit(runtime.OpCall, 0) + } else { + v.emit(runtime.OpCallOptional, 0) + } case 1: - v.emit(runtime.OpCall1, 1) + if isNonOptional { + v.emit(runtime.OpCall1, 1) + } else { + v.emit(runtime.OpCall1Optional, 1) + } case 2: - v.emit(runtime.OpCall2, 2) + if isNonOptional { + v.emit(runtime.OpCall2, 2) + } else { + v.emit(runtime.OpCall2Optional, 2) + } case 3: - v.emit(runtime.OpCall3, 3) + if isNonOptional { + v.emit(runtime.OpCall3, 3) + } else { + v.emit(runtime.OpCall3Optional, 3) + } case 4: - v.emit(runtime.OpCall4, 4) + if isNonOptional { + v.emit(runtime.OpCall4, 4) + } else { + v.emit(runtime.OpCall4Optional, 4) + } default: - v.emit(runtime.OpCallN, size) + if isNonOptional { + v.emit(runtime.OpCallN, size) + } else { + v.emit(runtime.OpCallNOptional, size) + } } return nil diff --git a/pkg/runtime/opcodes.go b/pkg/runtime/opcodes.go index f89c9420..e95883c7 100644 --- a/pkg/runtime/opcodes.go +++ b/pkg/runtime/opcodes.go @@ -42,11 +42,17 @@ const ( OpRegexpPositive OpRegexpNegative OpCall + OpCallOptional OpCall1 + OpCall1Optional OpCall2 + OpCall2Optional OpCall3 + OpCall3Optional OpCall4 + OpCall4Optional OpCallN + OpCallNOptional OpPush OpPop OpPopClose diff --git a/pkg/runtime/vm.go b/pkg/runtime/vm.go index 5556321f..166edd7d 100644 --- a/pkg/runtime/vm.go +++ b/pkg/runtime/vm.go @@ -283,28 +283,32 @@ loop: reg := program.Constants[arg].(*values.Regexp) stack.Push(!reg.Match(stack.Pop())) - case OpCall: + case OpCall, OpCallOptional: fnName := stack.Pop().String() res, err := vm.env.GetFunction(fnName)(ctx) if err == nil { stack.Push(res) + } else if op == OpCallOptional { + stack.Push(values.None) } else { return nil, err } - case OpCall1: + case OpCall1, OpCall1Optional: arg := stack.Pop() fnName := stack.Pop().String() res, err := vm.env.GetFunction(fnName)(ctx, arg) if err == nil { stack.Push(res) + } else if op == OpCall1Optional { + stack.Push(values.None) } else { return nil, err } - case OpCall2: + case OpCall2, OpCall2Optional: arg2 := stack.Pop() arg1 := stack.Pop() fnName := stack.Pop().String() @@ -312,11 +316,13 @@ loop: if err == nil { stack.Push(res) + } else if op == OpCall2Optional { + stack.Push(values.None) } else { return nil, err } - case OpCall3: + case OpCall3, OpCall3Optional: arg3 := stack.Pop() arg2 := stack.Pop() arg1 := stack.Pop() @@ -325,11 +331,13 @@ loop: if err == nil { stack.Push(res) + } else if op == OpCall3Optional { + stack.Push(values.None) } else { return nil, err } - case OpCall4: + case OpCall4, OpCall4Optional: arg4 := stack.Pop() arg3 := stack.Pop() arg2 := stack.Pop() @@ -339,10 +347,12 @@ loop: if err == nil { stack.Push(res) + } else if op == OpCall4Optional { + stack.Push(values.None) } else { return nil, err } - case OpCallN: + case OpCallN, OpCallNOptional: // pop arguments from the stack // and push them to the arguments array // in reverse order because stack is LIFO and arguments array is FIFO @@ -361,6 +371,8 @@ loop: if err == nil { stack.Push(res) + } else if op == OpCallNOptional { + stack.Push(values.None) } else { return nil, err }