mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
298 lines
5.0 KiB
Go
298 lines
5.0 KiB
Go
package compiler_test
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/expr-lang/expr"
|
|
"github.com/expr-lang/expr/test/playground"
|
|
"github.com/expr-lang/expr/vm"
|
|
"github.com/expr-lang/expr/vm/runtime"
|
|
)
|
|
|
|
type B struct {
|
|
_ byte
|
|
_ byte
|
|
C struct {
|
|
_ byte
|
|
_ byte
|
|
_ byte
|
|
D int
|
|
}
|
|
}
|
|
|
|
type Env struct {
|
|
A struct {
|
|
_ byte
|
|
B B
|
|
Map map[string]B
|
|
Ptr *int
|
|
}
|
|
}
|
|
|
|
func TestCompile(t *testing.T) {
|
|
type test struct {
|
|
input string
|
|
program vm.Program
|
|
}
|
|
var tests = []test{
|
|
{
|
|
`65535`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
math.MaxUint16,
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpPush,
|
|
},
|
|
Arguments: []int{0},
|
|
},
|
|
},
|
|
{
|
|
`.5`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
.5,
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpPush,
|
|
},
|
|
Arguments: []int{0},
|
|
},
|
|
},
|
|
{
|
|
`true`,
|
|
vm.Program{
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpTrue,
|
|
},
|
|
Arguments: []int{0},
|
|
},
|
|
},
|
|
{
|
|
`"string"`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
"string",
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpPush,
|
|
},
|
|
Arguments: []int{0},
|
|
},
|
|
},
|
|
{
|
|
`"string" == "string"`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
"string",
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpPush,
|
|
vm.OpPush,
|
|
vm.OpEqualString,
|
|
},
|
|
Arguments: []int{0, 0, 0},
|
|
},
|
|
},
|
|
{
|
|
`1000000 == 1000000`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
int64(1000000),
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpPush,
|
|
vm.OpPush,
|
|
vm.OpEqualInt,
|
|
},
|
|
Arguments: []int{0, 0, 0},
|
|
},
|
|
},
|
|
{
|
|
`-1`,
|
|
vm.Program{
|
|
Constants: []any{1},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpPush,
|
|
vm.OpNegate,
|
|
},
|
|
Arguments: []int{0, 0},
|
|
},
|
|
},
|
|
{
|
|
`true && true || true`,
|
|
vm.Program{
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpTrue,
|
|
vm.OpJumpIfFalse,
|
|
vm.OpPop,
|
|
vm.OpTrue,
|
|
vm.OpJumpIfTrue,
|
|
vm.OpPop,
|
|
vm.OpTrue,
|
|
},
|
|
Arguments: []int{0, 2, 0, 0, 2, 0, 0},
|
|
},
|
|
},
|
|
{
|
|
`true && (true || true)`,
|
|
vm.Program{
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpTrue,
|
|
vm.OpJumpIfFalse,
|
|
vm.OpPop,
|
|
vm.OpTrue,
|
|
vm.OpJumpIfTrue,
|
|
vm.OpPop,
|
|
vm.OpTrue,
|
|
},
|
|
Arguments: []int{0, 5, 0, 0, 2, 0, 0},
|
|
},
|
|
},
|
|
{
|
|
`A.B.C.D`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
&runtime.Field{
|
|
Index: []int{0, 1, 2, 3},
|
|
Path: []string{"A", "B", "C", "D"},
|
|
},
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpLoadField,
|
|
},
|
|
Arguments: []int{0},
|
|
},
|
|
},
|
|
{
|
|
`A?.B.C.D`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
&runtime.Field{
|
|
Index: []int{0},
|
|
Path: []string{"A"},
|
|
},
|
|
&runtime.Field{
|
|
Index: []int{1, 2, 3},
|
|
Path: []string{"B", "C", "D"},
|
|
},
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpLoadField,
|
|
vm.OpJumpIfNil,
|
|
vm.OpFetchField,
|
|
},
|
|
Arguments: []int{0, 1, 1},
|
|
},
|
|
},
|
|
{
|
|
`A.B?.C.D`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
&runtime.Field{
|
|
Index: []int{0, 1},
|
|
Path: []string{"A", "B"},
|
|
},
|
|
&runtime.Field{
|
|
Index: []int{2, 3},
|
|
Path: []string{"C", "D"},
|
|
},
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpLoadField,
|
|
vm.OpJumpIfNil,
|
|
vm.OpFetchField,
|
|
},
|
|
Arguments: []int{0, 1, 1},
|
|
},
|
|
},
|
|
{
|
|
`A.Map["B"].C.D`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
&runtime.Field{
|
|
Index: []int{0, 2},
|
|
Path: []string{"A", "Map"},
|
|
},
|
|
"B",
|
|
&runtime.Field{
|
|
Index: []int{2, 3},
|
|
Path: []string{"C", "D"},
|
|
},
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpLoadField,
|
|
vm.OpPush,
|
|
vm.OpFetch,
|
|
vm.OpFetchField,
|
|
},
|
|
Arguments: []int{0, 1, 0, 2},
|
|
},
|
|
},
|
|
{
|
|
`A ?? 1`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
&runtime.Field{
|
|
Index: []int{0},
|
|
Path: []string{"A"},
|
|
},
|
|
1,
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpLoadField,
|
|
vm.OpJumpIfNotNil,
|
|
vm.OpPop,
|
|
vm.OpPush,
|
|
},
|
|
Arguments: []int{0, 2, 0, 1},
|
|
},
|
|
},
|
|
{
|
|
`A.Ptr + 1`,
|
|
vm.Program{
|
|
Constants: []any{
|
|
&runtime.Field{
|
|
Index: []int{0, 3},
|
|
Path: []string{"A", "Ptr"},
|
|
},
|
|
1,
|
|
},
|
|
Bytecode: []vm.Opcode{
|
|
vm.OpLoadField,
|
|
vm.OpDeref,
|
|
vm.OpPush,
|
|
vm.OpAdd,
|
|
},
|
|
Arguments: []int{0, 0, 1, 0},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
program, err := expr.Compile(test.input, expr.Env(Env{}), expr.Optimize(false))
|
|
require.NoError(t, err, test.input)
|
|
|
|
assert.Equal(t, test.program.Disassemble(), program.Disassemble(), test.input)
|
|
}
|
|
}
|
|
|
|
func TestCompile_panic(t *testing.T) {
|
|
tests := []string{
|
|
`(TotalPosts.Profile[Authors > TotalPosts == get(nil, TotalLikes)] > Authors) ^ (TotalLikes / (Posts?.PublishDate[TotalPosts] < Posts))`,
|
|
`one(Posts, nil)`,
|
|
`trim(TotalViews, Posts) <= get(Authors, nil)`,
|
|
`Authors.IsZero(nil * Authors) - (TotalViews && Posts ? nil : nil)[TotalViews.IsZero(false, " ").IsZero(Authors)]`,
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test, func(t *testing.T) {
|
|
_, err := expr.Compile(test, expr.Env(playground.Blog{}))
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
}
|