Skip to content

Commit 3a78c21

Browse files
committed
feat: add disable short-circuiting option to compiler
1 parent 3d0aec6 commit 3a78c21

File tree

6 files changed

+89
-0
lines changed

6 files changed

+89
-0
lines changed

compiler/compiler.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,14 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
446446
c.emit(OpNot)
447447

448448
case "or", "||":
449+
if c.config.DisableSC {
450+
c.compile(node.Left)
451+
c.derefInNeeded(node.Left)
452+
c.compile(node.Right)
453+
c.derefInNeeded(node.Right)
454+
c.emit(OpOr)
455+
break
456+
}
449457
c.compile(node.Left)
450458
c.derefInNeeded(node.Left)
451459
end := c.emit(OpJumpIfTrue, placeholder)
@@ -455,6 +463,14 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
455463
c.patchJump(end)
456464

457465
case "and", "&&":
466+
if c.config.DisableSC {
467+
c.compile(node.Left)
468+
c.derefInNeeded(node.Left)
469+
c.compile(node.Right)
470+
c.derefInNeeded(node.Right)
471+
c.emit(OpAnd)
472+
break
473+
}
458474
c.compile(node.Left)
459475
c.derefInNeeded(node.Left)
460476
end := c.emit(OpJumpIfFalse, placeholder)

conf/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Config struct {
3535
Builtins FunctionsTable
3636
Disabled map[string]bool // disabled builtins
3737
NtCache nature.Cache
38+
DisableSC bool
3839
}
3940

4041
// CreateNew creates new config with default values.
@@ -46,6 +47,7 @@ func CreateNew() *Config {
4647
Functions: make(map[string]*builtin.Function),
4748
Builtins: make(map[string]*builtin.Function),
4849
Disabled: make(map[string]bool),
50+
DisableSC: false,
4951
}
5052
for _, f := range builtin.Builtins {
5153
c.Builtins[f.Name] = f

expr.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ func Optimize(b bool) Option {
126126
}
127127
}
128128

129+
// DisableShortCircuit turns short circuit off.
130+
func DisableShortCircuit() Option {
131+
return func(c *conf.Config) {
132+
c.DisableSC = true
133+
}
134+
}
135+
129136
// Patch adds visitor to list of visitors what will be applied before compiling AST to bytecode.
130137
func Patch(visitor ast.Visitor) Option {
131138
return func(c *conf.Config) {

expr_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,3 +2883,55 @@ func TestIssue807(t *testing.T) {
28832883
t.Fatalf("expected 'in' operator to return false for unexported field")
28842884
}
28852885
}
2886+
2887+
func ExampleDisableShortCircuit() {
2888+
OR := func(a, b bool) bool {
2889+
return a || b
2890+
}
2891+
2892+
env := map[string]any{
2893+
"foo": func() bool {
2894+
fmt.Println("foo")
2895+
return false
2896+
},
2897+
"bar": func() bool {
2898+
fmt.Println("bar")
2899+
return false
2900+
},
2901+
"OR": OR,
2902+
}
2903+
2904+
program, _ := expr.Compile("true || foo() or bar()", expr.Env(env), expr.Operator("or", "OR"), expr.Operator("||", "OR"))
2905+
got, _ := expr.Run(program, env)
2906+
fmt.Println(got)
2907+
2908+
// Output:
2909+
// foo
2910+
// bar
2911+
// true
2912+
}
2913+
2914+
func TestDisableShortCircuit(t *testing.T) {
2915+
count := 0
2916+
exprStr := "foo() or bar()"
2917+
env := map[string]any{
2918+
"foo": func() bool {
2919+
count++
2920+
return true
2921+
},
2922+
"bar": func() bool {
2923+
count++
2924+
return true
2925+
},
2926+
}
2927+
2928+
program, _ := expr.Compile(exprStr, expr.DisableShortCircuit())
2929+
got, _ := expr.Run(program, env)
2930+
assert.Equal(t, 2, count)
2931+
assert.True(t, got.(bool))
2932+
2933+
program, _ = expr.Compile(exprStr)
2934+
got, _ = expr.Run(program, env)
2935+
assert.Equal(t, 3, count)
2936+
assert.True(t, got.(bool))
2937+
}

vm/opcodes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,7 @@ const (
8484
OpProfileStart
8585
OpProfileEnd
8686
OpBegin
87+
OpAnd
88+
OpOr
8789
OpEnd // This opcode must be at the end of this list.
8890
)

vm/vm.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,16 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
560560
Len: array.Len(),
561561
})
562562

563+
case OpAnd:
564+
a := vm.pop()
565+
b := vm.pop()
566+
vm.push(a.(bool) && b.(bool))
567+
568+
case OpOr:
569+
a := vm.pop()
570+
b := vm.pop()
571+
vm.push(a.(bool) || b.(bool))
572+
563573
case OpEnd:
564574
vm.Scopes = vm.Scopes[:len(vm.Scopes)-1]
565575

0 commit comments

Comments
 (0)