xgo/compiler_test.go
wlxwlxwlx 18b953c7be
increase size of jump operands (#438)
* [OpJumpFalse, OpAndJump, OpOrJump, OpJump], these four instructions have been changed to use 4 bytes to avoid precision loss and panic when the number of instructions exceeds the maximum of 16 bits (65535)

* update test cases

* update test cases

---------

Co-authored-by: 王录祥 <wanglx@smartsteps.com>
2023-12-20 13:13:23 -05:00

1480 lines
45 KiB
Go

package tengo_test
import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"github.com/d5/tengo/v2"
"github.com/d5/tengo/v2/parser"
"github.com/d5/tengo/v2/require"
"github.com/d5/tengo/v2/stdlib"
)
func TestCompiler_Compile(t *testing.T) {
expectCompile(t, `1 + 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1; 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 - 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 12),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 * 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 13),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `2 / 1`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 14),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(2),
intObject(1))))
expectCompile(t, `true`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `false`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpFalse),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `1 > 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 39),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 < 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 38),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 >= 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 44),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 <= 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 43),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 == 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpEqual),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `1 != 2`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpNotEqual),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `true == false`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpFalse),
tengo.MakeInstruction(parser.OpEqual),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `true != false`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpFalse),
tengo.MakeInstruction(parser.OpNotEqual),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `-1`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpMinus),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1))))
expectCompile(t, `!true`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpLNot),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `if true { 10 }; 3333`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpTrue), // 0000
tengo.MakeInstruction(parser.OpJumpFalsy, 10), // 0001
tengo.MakeInstruction(parser.OpConstant, 0), // 0004
tengo.MakeInstruction(parser.OpPop), // 0007
tengo.MakeInstruction(parser.OpConstant, 1), // 0008
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)), // 0011
objectsArray(
intObject(10),
intObject(3333))))
expectCompile(t, `if (true) { 10 } else { 20 }; 3333;`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpTrue), // 0000
tengo.MakeInstruction(parser.OpJumpFalsy, 15), // 0001
tengo.MakeInstruction(parser.OpConstant, 0), // 0004
tengo.MakeInstruction(parser.OpPop), // 0007
tengo.MakeInstruction(parser.OpJump, 19), // 0008
tengo.MakeInstruction(parser.OpConstant, 1), // 0011
tengo.MakeInstruction(parser.OpPop), // 0014
tengo.MakeInstruction(parser.OpConstant, 2), // 0015
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)), // 0018
objectsArray(
intObject(10),
intObject(20),
intObject(3333))))
expectCompile(t, `"kami"`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
stringObject("kami"))))
expectCompile(t, `"ka" + "mi"`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
stringObject("ka"),
stringObject("mi"))))
expectCompile(t, `a := 1; b := 2; a += b`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetGlobal, 1),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `a := 1; b := 2; a /= b`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetGlobal, 1),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 14),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2))))
expectCompile(t, `[]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpArray, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `[1, 2, 3]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3))))
expectCompile(t, `[1 + 2, 3 - 4, 5 * 6]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpBinaryOp, 12),
tengo.MakeInstruction(parser.OpConstant, 4),
tengo.MakeInstruction(parser.OpConstant, 5),
tengo.MakeInstruction(parser.OpBinaryOp, 13),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3),
intObject(4),
intObject(5),
intObject(6))))
expectCompile(t, `{}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpMap, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `{a: 2, b: 4, c: 6}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpConstant, 4),
tengo.MakeInstruction(parser.OpConstant, 5),
tengo.MakeInstruction(parser.OpMap, 6),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
stringObject("a"),
intObject(2),
stringObject("b"),
intObject(4),
stringObject("c"),
intObject(6))))
expectCompile(t, `{a: 2 + 3, b: 5 * 6}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpConstant, 4),
tengo.MakeInstruction(parser.OpConstant, 5),
tengo.MakeInstruction(parser.OpBinaryOp, 13),
tengo.MakeInstruction(parser.OpMap, 4),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
stringObject("a"),
intObject(2),
intObject(3),
stringObject("b"),
intObject(5),
intObject(6))))
expectCompile(t, `[1, 2, 3][1 + 1]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpIndex),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3))))
expectCompile(t, `{a: 2}[2 - 1]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpMap, 2),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpBinaryOp, 12),
tengo.MakeInstruction(parser.OpIndex),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
stringObject("a"),
intObject(2),
intObject(1))))
expectCompile(t, `[1, 2, 3][:]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpNull),
tengo.MakeInstruction(parser.OpNull),
tengo.MakeInstruction(parser.OpSliceIndex),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3))))
expectCompile(t, `[1, 2, 3][0 : 2]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSliceIndex),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3),
intObject(0))))
expectCompile(t, `[1, 2, 3][:2]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpNull),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSliceIndex),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3))))
expectCompile(t, `[1, 2, 3][0:]`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 3),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpNull),
tengo.MakeInstruction(parser.OpSliceIndex),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3),
intObject(0))))
expectCompile(t, `f1 := func(a) { return a }; f1([1, 2]...);`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpArray, 2),
tengo.MakeInstruction(parser.OpCall, 1, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpReturn, 1)),
intObject(1),
intObject(2))))
expectCompile(t, `func() { return 5 + 10 }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(5),
intObject(10),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `func() { 5 + 10 }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(5),
intObject(10),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `func() { 1; 2 }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `func() { 1; return 2 }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `func() { if(true) { return 1 } else { return 2 } }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpTrue), // 0000
tengo.MakeInstruction(parser.OpJumpFalsy, 11), // 0001
tengo.MakeInstruction(parser.OpConstant, 0), // 0004
tengo.MakeInstruction(parser.OpReturn, 1), // 0007
tengo.MakeInstruction(parser.OpConstant, 1), // 0009
tengo.MakeInstruction(parser.OpReturn, 1))))) // 0012
expectCompile(t, `func() { 1; if(true) { 2 } else { 3 }; 4 }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 4),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3),
intObject(4),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0), // 0000
tengo.MakeInstruction(parser.OpPop), // 0003
tengo.MakeInstruction(parser.OpTrue), // 0004
tengo.MakeInstruction(parser.OpJumpFalsy, 19), // 0005
tengo.MakeInstruction(parser.OpConstant, 1), // 0008
tengo.MakeInstruction(parser.OpPop), // 0011
tengo.MakeInstruction(parser.OpJump, 23), // 0012
tengo.MakeInstruction(parser.OpConstant, 2), // 0015
tengo.MakeInstruction(parser.OpPop), // 0018
tengo.MakeInstruction(parser.OpConstant, 3), // 0019
tengo.MakeInstruction(parser.OpPop), // 0022
tengo.MakeInstruction(parser.OpReturn, 0))))) // 0023
expectCompile(t, `func() { }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `func() { 24 }()`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpCall, 0, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(24),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `func() { return 24 }()`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpCall, 0, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(24),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `noArg := func() { 24 }; noArg();`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpCall, 0, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(24),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `noArg := func() { return 24 }; noArg();`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpCall, 0, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(24),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `n := 55; func() { n };`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(55),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `func() { n := 55; return n }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(55),
compiledFunction(1, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `func() { a := 55; b := 77; return a + b }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(55),
intObject(77),
compiledFunction(2, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpDefineLocal, 1),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `f1 := func(a) { return a }; f1(24);`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpCall, 1, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpReturn, 1)),
intObject(24))))
expectCompile(t, `varTest := func(...a) { return a }; varTest(1,2,3);`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpCall, 3, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpReturn, 1)),
intObject(1), intObject(2), intObject(3))))
expectCompile(t, `f1 := func(a, b, c) { a; b; return c; }; f1(24, 25, 26);`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpCall, 3, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(3, 3,
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpGetLocal, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpGetLocal, 2),
tengo.MakeInstruction(parser.OpReturn, 1)),
intObject(24),
intObject(25),
intObject(26))))
expectCompile(t, `func() { n := 55; n = 23; return n }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(55),
intObject(23),
compiledFunction(1, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `len([]);`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpGetBuiltin, 0),
tengo.MakeInstruction(parser.OpArray, 0),
tengo.MakeInstruction(parser.OpCall, 1, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `func() { return len([]) }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpGetBuiltin, 0),
tengo.MakeInstruction(parser.OpArray, 0),
tengo.MakeInstruction(parser.OpCall, 1, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `func(a) { func(b) { return a + b } }`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetFree, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpReturn, 1)),
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetLocalPtr, 0),
tengo.MakeInstruction(parser.OpClosure, 0, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpReturn, 0)))))
expectCompile(t, `
func(a) {
return func(b) {
return func(c) {
return a + b + c
}
}
}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetFree, 0),
tengo.MakeInstruction(parser.OpGetFree, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpReturn, 1)),
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetFreePtr, 0),
tengo.MakeInstruction(parser.OpGetLocalPtr, 0),
tengo.MakeInstruction(parser.OpClosure, 0, 2),
tengo.MakeInstruction(parser.OpReturn, 1)),
compiledFunction(1, 1,
tengo.MakeInstruction(parser.OpGetLocalPtr, 0),
tengo.MakeInstruction(parser.OpClosure, 1, 1),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `
g := 55;
func() {
a := 66;
return func() {
b := 77;
return func() {
c := 88;
return g + a + b + c;
}
}
}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 6),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(55),
intObject(66),
intObject(77),
intObject(88),
compiledFunction(1, 0,
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpGetFree, 0),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpGetFree, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpReturn, 1)),
compiledFunction(1, 0,
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetFreePtr, 0),
tengo.MakeInstruction(parser.OpGetLocalPtr, 0),
tengo.MakeInstruction(parser.OpClosure, 4, 2),
tengo.MakeInstruction(parser.OpReturn, 1)),
compiledFunction(1, 0,
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetLocalPtr, 0),
tengo.MakeInstruction(parser.OpClosure, 5, 1),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `for i:=0; i<10; i++ {}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 38),
tengo.MakeInstruction(parser.OpJumpFalsy, 35),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpJump, 6),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(0),
intObject(10),
intObject(1))))
expectCompile(t, `m := {}; for k, v in m {}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpMap, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpIteratorInit),
tengo.MakeInstruction(parser.OpSetGlobal, 1),
tengo.MakeInstruction(parser.OpGetGlobal, 1),
tengo.MakeInstruction(parser.OpIteratorNext),
tengo.MakeInstruction(parser.OpJumpFalsy, 41),
tengo.MakeInstruction(parser.OpGetGlobal, 1),
tengo.MakeInstruction(parser.OpIteratorKey),
tengo.MakeInstruction(parser.OpSetGlobal, 2),
tengo.MakeInstruction(parser.OpGetGlobal, 1),
tengo.MakeInstruction(parser.OpIteratorValue),
tengo.MakeInstruction(parser.OpSetGlobal, 3),
tengo.MakeInstruction(parser.OpJump, 13),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray()))
expectCompile(t, `a := 0; a == 0 && a != 1 || a < 1`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpEqual),
tengo.MakeInstruction(parser.OpAndJump, 25),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpNotEqual),
tengo.MakeInstruction(parser.OpOrJump, 38),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 38),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(0),
intObject(1))))
// unknown module name
expectCompileError(t, `import("user1")`, "module 'user1' not found")
// too many errors
expectCompileError(t, `
r["x"] = {
@a:1,
@b:1,
@c:1,
@d:1,
@e:1,
@f:1,
@g:1,
@h:1,
@i:1,
@j:1,
@k:1
}
`, "Parse Error: illegal character U+0040 '@'\n\tat test:3:5 (and 10 more errors)")
expectCompileError(t, `import("")`, "empty module name")
// https://github.com/d5/tengo/issues/314
expectCompileError(t, `
(func() {
fn := fn()
})()
`, "unresolved reference 'fn")
}
func TestCompilerErrorReport(t *testing.T) {
expectCompileError(t, `import("user1")`,
"Compile Error: module 'user1' not found\n\tat test:1:1")
expectCompileError(t, `a = 1`,
"Compile Error: unresolved reference 'a'\n\tat test:1:1")
expectCompileError(t, `a := a`,
"Compile Error: unresolved reference 'a'\n\tat test:1:6")
expectCompileError(t, `a, b := 1, 2`,
"Compile Error: tuple assignment not allowed\n\tat test:1:1")
expectCompileError(t, `a.b := 1`,
"not allowed with selector")
expectCompileError(t, `a:=1; a:=3`,
"Compile Error: 'a' redeclared in this block\n\tat test:1:7")
expectCompileError(t, `return 5`,
"Compile Error: return not allowed outside function\n\tat test:1:1")
expectCompileError(t, `func() { break }`,
"Compile Error: break not allowed outside loop\n\tat test:1:10")
expectCompileError(t, `func() { continue }`,
"Compile Error: continue not allowed outside loop\n\tat test:1:10")
expectCompileError(t, `func() { export 5 }`,
"Compile Error: export not allowed inside function\n\tat test:1:10")
}
func TestCompilerDeadCode(t *testing.T) {
expectCompile(t, `
func() {
a := 4
return a
b := 5 // dead code from here
c := a
return b
}`,
bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(4),
intObject(5),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `
func() {
if true {
return 5
a := 4 // dead code from here
b := a
return b
} else {
return 4
c := 5 // dead code from here
d := c
return d
}
}`, bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(5),
intObject(4),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpJumpFalsy, 11),
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpReturn, 1),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `
func() {
a := 1
for {
if a == 5 {
return 10
}
5 + 5
return 20
b := a
return b
}
}`, bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 4),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(5),
intObject(10),
intObject(20),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpEqual),
tengo.MakeInstruction(parser.OpJumpFalsy, 21),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpReturn, 1),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpBinaryOp, 11),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `
func() {
if true {
return 5
a := 4 // dead code from here
b := a
return b
} else {
return 4
c := 5 // dead code from here
d := c
return d
}
}`, bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(5),
intObject(4),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpJumpFalsy, 11),
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpReturn, 1),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpReturn, 1)))))
expectCompile(t, `
func() {
if true {
return
}
return
return 123
}`, bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(123),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpTrue),
tengo.MakeInstruction(parser.OpJumpFalsy, 8),
tengo.MakeInstruction(parser.OpReturn, 0),
tengo.MakeInstruction(parser.OpReturn, 0),
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpReturn, 1)))))
}
func TestCompilerScopes(t *testing.T) {
expectCompile(t, `
if a := 1; a {
a = 2
b := a
} else {
a = 3
b := a
}`, bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpJumpFalsy, 31),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 1),
tengo.MakeInstruction(parser.OpJump, 43),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpSetGlobal, 0),
tengo.MakeInstruction(parser.OpGetGlobal, 0),
tengo.MakeInstruction(parser.OpSetGlobal, 2),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3))))
expectCompile(t, `
func() {
if a := 1; a {
a = 2
b := a
} else {
a = 3
b := a
}
}`, bytecode(
concatInsts(
tengo.MakeInstruction(parser.OpConstant, 3),
tengo.MakeInstruction(parser.OpPop),
tengo.MakeInstruction(parser.OpSuspend)),
objectsArray(
intObject(1),
intObject(2),
intObject(3),
compiledFunction(0, 0,
tengo.MakeInstruction(parser.OpConstant, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpJumpFalsy, 26),
tengo.MakeInstruction(parser.OpConstant, 1),
tengo.MakeInstruction(parser.OpSetLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 1),
tengo.MakeInstruction(parser.OpJump, 35),
tengo.MakeInstruction(parser.OpConstant, 2),
tengo.MakeInstruction(parser.OpSetLocal, 0),
tengo.MakeInstruction(parser.OpGetLocal, 0),
tengo.MakeInstruction(parser.OpDefineLocal, 1),
tengo.MakeInstruction(parser.OpReturn, 0)))))
}
func TestCompiler_custom_extension(t *testing.T) {
pathFileSource := "./testdata/issue286/test.mshk"
modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...)
src, err := ioutil.ReadFile(pathFileSource)
require.NoError(t, err)
// Escape shegang
if len(src) > 1 && string(src[:2]) == "#!" {
copy(src, "//")
}
fileSet := parser.NewFileSet()
srcFile := fileSet.AddFile(filepath.Base(pathFileSource), -1, len(src))
p := parser.NewParser(srcFile, src, nil)
file, err := p.ParseFile()
require.NoError(t, err)
c := tengo.NewCompiler(srcFile, nil, nil, modules, nil)
c.EnableFileImport(true)
c.SetImportDir(filepath.Dir(pathFileSource))
// Search for "*.tengo" and ".mshk"(custom extension)
c.SetImportFileExt(".tengo", ".mshk")
err = c.Compile(file)
require.NoError(t, err)
}
func TestCompilerNewCompiler_default_file_extension(t *testing.T) {
modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...)
input := "{}"
fileSet := parser.NewFileSet()
file := fileSet.AddFile("test", -1, len(input))
c := tengo.NewCompiler(file, nil, nil, modules, nil)
c.EnableFileImport(true)
require.Equal(t, []string{".tengo"}, c.GetImportFileExt(),
"newly created compiler object must contain the default extension")
}
func TestCompilerSetImportExt_extension_name_validation(t *testing.T) {
c := new(tengo.Compiler) // Instantiate a new compiler object with no initialization
// Test of empty arg
err := c.SetImportFileExt()
require.Error(t, err, "empty arg should return an error")
// Test of various arg types
for _, test := range []struct {
extensions []string
expect []string
requireErr bool
msgFail string
}{
{[]string{".tengo"}, []string{".tengo"}, false,
"well-formed extension should not return an error"},
{[]string{""}, []string{".tengo"}, true,
"empty extension name should return an error"},
{[]string{"foo"}, []string{".tengo"}, true,
"name without dot prefix should return an error"},
{[]string{"foo.bar"}, []string{".tengo"}, true,
"malformed extension should return an error"},
{[]string{"foo."}, []string{".tengo"}, true,
"malformed extension should return an error"},
{[]string{".mshk"}, []string{".mshk"}, false,
"name with dot prefix should be added"},
{[]string{".foo", ".bar"}, []string{".foo", ".bar"}, false,
"it should replace instead of appending"},
} {
err := c.SetImportFileExt(test.extensions...)
if test.requireErr {
require.Error(t, err, test.msgFail)
}
expect := test.expect
actual := c.GetImportFileExt()
require.Equal(t, expect, actual, test.msgFail)
}
}
func concatInsts(instructions ...[]byte) []byte {
var concat []byte
for _, i := range instructions {
concat = append(concat, i...)
}
return concat
}
func bytecode(
instructions []byte,
constants []tengo.Object,
) *tengo.Bytecode {
return &tengo.Bytecode{
FileSet: parser.NewFileSet(),
MainFunction: &tengo.CompiledFunction{Instructions: instructions},
Constants: constants,
}
}
func expectCompile(
t *testing.T,
input string,
expected *tengo.Bytecode,
) {
actual, trace, err := traceCompile(input, nil)
var ok bool
defer func() {
if !ok {
for _, tr := range trace {
t.Log(tr)
}
}
}()
require.NoError(t, err)
equalBytecode(t, expected, actual)
ok = true
}
func expectCompileError(t *testing.T, input, expected string) {
_, trace, err := traceCompile(input, nil)
var ok bool
defer func() {
if !ok {
for _, tr := range trace {
t.Log(tr)
}
}
}()
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), expected),
"expected error string: %s, got: %s", expected, err.Error())
ok = true
}
func equalBytecode(t *testing.T, expected, actual *tengo.Bytecode) {
require.Equal(t, expected.MainFunction, actual.MainFunction)
equalConstants(t, expected.Constants, actual.Constants)
}
func equalConstants(t *testing.T, expected, actual []tengo.Object) {
require.Equal(t, len(expected), len(actual))
for i := 0; i < len(expected); i++ {
require.Equal(t, expected[i], actual[i])
}
}
type compileTracer struct {
Out []string
}
func (o *compileTracer) Write(p []byte) (n int, err error) {
o.Out = append(o.Out, string(p))
return len(p), nil
}
func traceCompile(
input string,
symbols map[string]tengo.Object,
) (res *tengo.Bytecode, trace []string, err error) {
fileSet := parser.NewFileSet()
file := fileSet.AddFile("test", -1, len(input))
p := parser.NewParser(file, []byte(input), nil)
symTable := tengo.NewSymbolTable()
for name := range symbols {
symTable.Define(name)
}
for idx, fn := range tengo.GetAllBuiltinFunctions() {
symTable.DefineBuiltin(idx, fn.Name)
}
tr := &compileTracer{}
c := tengo.NewCompiler(file, symTable, nil, nil, tr)
parsed, err := p.ParseFile()
if err != nil {
return
}
err = c.Compile(parsed)
res = c.Bytecode()
res.RemoveDuplicates()
{
trace = append(trace, fmt.Sprintf("Compiler Trace:\n%s",
strings.Join(tr.Out, "")))
trace = append(trace, fmt.Sprintf("Compiled Constants:\n%s",
strings.Join(res.FormatConstants(), "\n")))
trace = append(trace, fmt.Sprintf("Compiled Instructions:\n%s\n",
strings.Join(res.FormatInstructions(), "\n")))
}
if err != nil {
return
}
return
}
func objectsArray(o ...tengo.Object) []tengo.Object {
return o
}
func intObject(v int64) *tengo.Int {
return &tengo.Int{Value: v}
}
func stringObject(v string) *tengo.String {
return &tengo.String{Value: v}
}
func compiledFunction(
numLocals, numParams int,
insts ...[]byte,
) *tengo.CompiledFunction {
return &tengo.CompiledFunction{
Instructions: concatInsts(insts...),
NumLocals: numLocals,
NumParameters: numParams,
}
}