github.com/expr-lang/expr@v1.16.9/vm/vm_test.go (about) 1 package vm_test 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "testing" 8 9 "github.com/expr-lang/expr/internal/testify/require" 10 11 "github.com/expr-lang/expr" 12 "github.com/expr-lang/expr/checker" 13 "github.com/expr-lang/expr/compiler" 14 "github.com/expr-lang/expr/conf" 15 "github.com/expr-lang/expr/parser" 16 "github.com/expr-lang/expr/vm" 17 ) 18 19 func TestRun_NilProgram(t *testing.T) { 20 _, err := vm.Run(nil, nil) 21 require.Error(t, err) 22 } 23 24 func TestRun_ReuseVM(t *testing.T) { 25 node, err := parser.Parse(`map(1..2, {#})`) 26 require.NoError(t, err) 27 28 program, err := compiler.Compile(node, nil) 29 require.NoError(t, err) 30 31 reuse := vm.VM{} 32 _, err = reuse.Run(program, nil) 33 require.NoError(t, err) 34 _, err = reuse.Run(program, nil) 35 require.NoError(t, err) 36 } 37 38 func TestRun_ReuseVM_for_different_variables(t *testing.T) { 39 v := vm.VM{} 40 41 program, err := expr.Compile(`let a = 1; a + 1`) 42 require.NoError(t, err) 43 out, err := v.Run(program, nil) 44 require.NoError(t, err) 45 require.Equal(t, 2, out) 46 47 program, err = expr.Compile(`let a = 2; a + 1`) 48 require.NoError(t, err) 49 out, err = v.Run(program, nil) 50 require.NoError(t, err) 51 require.Equal(t, 3, out) 52 53 program, err = expr.Compile(`let a = 2; let b = 2; a + b`) 54 require.NoError(t, err) 55 out, err = v.Run(program, nil) 56 require.NoError(t, err) 57 require.Equal(t, 4, out) 58 } 59 60 func TestRun_Cast(t *testing.T) { 61 input := `1` 62 63 tree, err := parser.Parse(input) 64 require.NoError(t, err) 65 66 program, err := compiler.Compile(tree, &conf.Config{Expect: reflect.Float64}) 67 require.NoError(t, err) 68 69 out, err := vm.Run(program, nil) 70 require.NoError(t, err) 71 72 require.Equal(t, float64(1), out) 73 } 74 75 func TestRun_Helpers(t *testing.T) { 76 values := []any{ 77 uint(1), 78 uint8(1), 79 uint16(1), 80 uint32(1), 81 uint64(1), 82 1, 83 int8(1), 84 int16(1), 85 int32(1), 86 int64(1), 87 float32(1), 88 float64(1), 89 } 90 ops := []string{"+", "-", "*", "/", "%", "==", ">=", "<=", "<", ">"} 91 92 for _, a := range values { 93 for _, b := range values { 94 for _, op := range ops { 95 96 if op == "%" { 97 switch a.(type) { 98 case float32, float64: 99 continue 100 } 101 switch b.(type) { 102 case float32, float64: 103 continue 104 } 105 } 106 107 input := fmt.Sprintf("a %v b", op) 108 env := map[string]any{ 109 "a": a, 110 "b": b, 111 } 112 113 config := conf.CreateNew() 114 115 tree, err := parser.Parse(input) 116 require.NoError(t, err) 117 118 _, err = checker.Check(tree, config) 119 require.NoError(t, err) 120 121 program, err := compiler.Compile(tree, config) 122 require.NoError(t, err) 123 124 _, err = vm.Run(program, env) 125 require.NoError(t, err) 126 } 127 } 128 } 129 } 130 131 type ErrorEnv struct { 132 InnerEnv InnerEnv 133 } 134 type InnerEnv struct{} 135 136 func (ErrorEnv) WillError(param string) (bool, error) { 137 if param == "yes" { 138 return false, errors.New("error") 139 } 140 return true, nil 141 } 142 143 func (InnerEnv) WillError(param string) (bool, error) { 144 if param == "yes" { 145 return false, errors.New("inner error") 146 } 147 return true, nil 148 } 149 150 func TestRun_MethodWithError(t *testing.T) { 151 input := `WillError("yes")` 152 153 tree, err := parser.Parse(input) 154 require.NoError(t, err) 155 156 env := ErrorEnv{} 157 funcConf := conf.New(env) 158 _, err = checker.Check(tree, funcConf) 159 require.NoError(t, err) 160 161 program, err := compiler.Compile(tree, funcConf) 162 require.NoError(t, err) 163 164 out, err := vm.Run(program, env) 165 require.EqualError(t, err, "error (1:1)\n | WillError(\"yes\")\n | ^") 166 require.Equal(t, nil, out) 167 168 selfErr := errors.Unwrap(err) 169 require.NotNil(t, err) 170 require.Equal(t, "error", selfErr.Error()) 171 } 172 173 func TestRun_FastMethods(t *testing.T) { 174 input := `hello() + world()` 175 176 tree, err := parser.Parse(input) 177 require.NoError(t, err) 178 179 env := map[string]any{ 180 "hello": func(...any) any { return "hello " }, 181 "world": func(...any) any { return "world" }, 182 } 183 funcConf := conf.New(env) 184 _, err = checker.Check(tree, funcConf) 185 require.NoError(t, err) 186 187 program, err := compiler.Compile(tree, funcConf) 188 require.NoError(t, err) 189 190 out, err := vm.Run(program, env) 191 require.NoError(t, err) 192 193 require.Equal(t, "hello world", out) 194 } 195 196 func TestRun_InnerMethodWithError(t *testing.T) { 197 input := `InnerEnv.WillError("yes")` 198 199 tree, err := parser.Parse(input) 200 require.NoError(t, err) 201 202 env := ErrorEnv{} 203 funcConf := conf.New(env) 204 program, err := compiler.Compile(tree, funcConf) 205 require.NoError(t, err) 206 207 out, err := vm.Run(program, env) 208 require.EqualError(t, err, "inner error (1:10)\n | InnerEnv.WillError(\"yes\")\n | .........^") 209 require.Equal(t, nil, out) 210 } 211 212 func TestRun_InnerMethodWithError_NilSafe(t *testing.T) { 213 input := `InnerEnv?.WillError("yes")` 214 215 tree, err := parser.Parse(input) 216 require.NoError(t, err) 217 218 env := ErrorEnv{} 219 funcConf := conf.New(env) 220 program, err := compiler.Compile(tree, funcConf) 221 require.NoError(t, err) 222 223 out, err := vm.Run(program, env) 224 require.EqualError(t, err, "inner error (1:11)\n | InnerEnv?.WillError(\"yes\")\n | ..........^") 225 require.Equal(t, nil, out) 226 } 227 228 func TestRun_TaggedFieldName(t *testing.T) { 229 input := `value` 230 231 tree, err := parser.Parse(input) 232 require.NoError(t, err) 233 234 env := struct { 235 V string `expr:"value"` 236 }{ 237 V: "hello world", 238 } 239 240 funcConf := conf.New(env) 241 _, err = checker.Check(tree, funcConf) 242 require.NoError(t, err) 243 244 program, err := compiler.Compile(tree, funcConf) 245 require.NoError(t, err) 246 247 out, err := vm.Run(program, env) 248 require.NoError(t, err) 249 250 require.Equal(t, "hello world", out) 251 } 252 253 func TestRun_OpInvalid(t *testing.T) { 254 program := &vm.Program{ 255 Bytecode: []vm.Opcode{vm.OpInvalid}, 256 Arguments: []int{0}, 257 } 258 259 _, err := vm.Run(program, nil) 260 require.EqualError(t, err, "invalid opcode") 261 }