forked from defunkt/ReefVM
update tests
This commit is contained in:
parent
8198c555ac
commit
4b3c9e8bfc
|
|
@ -17,12 +17,15 @@ export type Constant =
|
||||||
|
|
||||||
//
|
//
|
||||||
// Parse bytecode from human-readable string format.
|
// Parse bytecode from human-readable string format.
|
||||||
// Operand types are determined by prefix:
|
// Operand types are determined by prefix/literal:
|
||||||
// #42 -> immediate number (e.g., JUMP #5, MAKE_ARRAY #3)
|
// #42 -> immediate number (e.g., JUMP #5, MAKE_ARRAY #3)
|
||||||
// name -> variable/function name (e.g., LOAD x, CALL_NATIVE add)
|
// name -> variable/function name (e.g., LOAD x, CALL_NATIVE add)
|
||||||
// 42 -> number constant (e.g., PUSH 42)
|
// 42 -> number constant (e.g., PUSH 42)
|
||||||
// "str" -> string constant (e.g., PUSH "hello")
|
// "str" -> string constant (e.g., PUSH "hello")
|
||||||
// 'str' -> string constant (e.g., PUSH 'hello')
|
// 'str' -> string constant (e.g., PUSH 'hello')
|
||||||
|
// true -> boolean constant (e.g., PUSH true)
|
||||||
|
// false -> boolean constant (e.g., PUSH false)
|
||||||
|
// null -> null constant (e.g., PUSH null)
|
||||||
//
|
//
|
||||||
// Function definitions:
|
// Function definitions:
|
||||||
// MAKE_FUNCTION (x y) #7 -> basic function
|
// MAKE_FUNCTION (x y) #7 -> basic function
|
||||||
|
|
@ -156,6 +159,16 @@ export function toBytecode(str: string): Bytecode /* throws */ {
|
||||||
bytecode.constants.push(toValue(parseFloat(operand)))
|
bytecode.constants.push(toValue(parseFloat(operand)))
|
||||||
operandValue = bytecode.constants.length - 1
|
operandValue = bytecode.constants.length - 1
|
||||||
|
|
||||||
|
} else if (operand === 'true' || operand === 'false') {
|
||||||
|
// boolean
|
||||||
|
bytecode.constants.push(toValue(operand === 'true'))
|
||||||
|
operandValue = bytecode.constants.length - 1
|
||||||
|
|
||||||
|
} else if (operand === 'null') {
|
||||||
|
// null
|
||||||
|
bytecode.constants.push(toValue(null))
|
||||||
|
operandValue = bytecode.constants.length - 1
|
||||||
|
|
||||||
} else if (/^[a-zA-Z_].*$/.test(operand)) {
|
} else if (/^[a-zA-Z_].*$/.test(operand)) {
|
||||||
// variable
|
// variable
|
||||||
operandValue = operand
|
operandValue = operand
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,13 @@
|
||||||
import { test, expect } from "bun:test"
|
import { test, expect } from "bun:test"
|
||||||
|
import { toBytecode } from "#bytecode"
|
||||||
import { VM } from "#vm"
|
import { VM } from "#vm"
|
||||||
import { OpCode } from "#opcode"
|
|
||||||
|
|
||||||
test("MAKE_FUNCTION - creates function with captured scope", async () => {
|
test("MAKE_FUNCTION - creates function with captured scope", async () => {
|
||||||
const vm = new VM({
|
const bytecode = toBytecode(`
|
||||||
instructions: [
|
MAKE_FUNCTION () #999
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 }
|
`)
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: [],
|
|
||||||
defaults: {},
|
|
||||||
body: 999,
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
const result = await new VM(bytecode).run()
|
||||||
expect(result.type).toBe('function')
|
expect(result.type).toBe('function')
|
||||||
if (result.type === 'function') {
|
if (result.type === 'function') {
|
||||||
expect(result.body).toBe(999)
|
expect(result.body).toBe(999)
|
||||||
|
|
@ -28,97 +16,45 @@ test("MAKE_FUNCTION - creates function with captured scope", async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL and RETURN - basic function call", async () => {
|
test("CALL and RETURN - basic function call", async () => {
|
||||||
// Function that returns 42
|
const bytecode = toBytecode(`
|
||||||
const vm = new VM({
|
MAKE_FUNCTION () #3
|
||||||
instructions: [
|
CALL #0
|
||||||
// 0: Create and call the function
|
HALT
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 },
|
PUSH 42
|
||||||
{ op: OpCode.CALL, operand: 0 }, // call with 0 args
|
RETURN
|
||||||
{ op: OpCode.HALT },
|
`)
|
||||||
|
|
||||||
// 3: Function body (starts here, address = 3)
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.PUSH, operand: 1 }, // push 42
|
|
||||||
{ op: OpCode.RETURN }
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: [],
|
|
||||||
defaults: {},
|
|
||||||
body: 3, // function body starts at instruction 3
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
{ type: 'number', value: 42 }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'number', value: 42 })
|
expect(result).toEqual({ type: 'number', value: 42 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL and RETURN - function with one parameter", async () => {
|
test("CALL and RETURN - function with one parameter", async () => {
|
||||||
// Function that returns its parameter
|
const bytecode = toBytecode(`
|
||||||
const vm = new VM({
|
MAKE_FUNCTION (x) #4
|
||||||
instructions: [
|
PUSH 100
|
||||||
// 0: Push argument and call function
|
CALL #1
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 },
|
HALT
|
||||||
{ op: OpCode.PUSH, operand: 1 }, // argument: 100
|
LOAD x
|
||||||
{ op: OpCode.CALL, operand: 1 }, // call with 1 arg
|
RETURN
|
||||||
{ op: OpCode.HALT },
|
`)
|
||||||
|
|
||||||
// 4: Function body
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.LOAD, operand: 'x' }, // load parameter x
|
|
||||||
{ op: OpCode.RETURN }
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['x'],
|
|
||||||
defaults: {},
|
|
||||||
body: 4,
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
{ type: 'number', value: 100 }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'number', value: 100 })
|
expect(result).toEqual({ type: 'number', value: 100 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL and RETURN - function with two parameters", async () => {
|
test("CALL and RETURN - function with two parameters", async () => {
|
||||||
// Function that adds two parameters
|
const bytecode = toBytecode(`
|
||||||
const vm = new VM({
|
MAKE_FUNCTION (a b) #5
|
||||||
instructions: [
|
PUSH 10
|
||||||
// 0: Push arguments and call function
|
PUSH 20
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 },
|
CALL #2
|
||||||
{ op: OpCode.PUSH, operand: 1 }, // arg1: 10
|
HALT
|
||||||
{ op: OpCode.PUSH, operand: 2 }, // arg2: 20
|
LOAD a
|
||||||
{ op: OpCode.CALL, operand: 2 }, // call with 2 args
|
LOAD b
|
||||||
{ op: OpCode.HALT },
|
ADD
|
||||||
|
RETURN
|
||||||
|
`)
|
||||||
|
|
||||||
// 5: Function body
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.LOAD, operand: 'a' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'b' },
|
|
||||||
{ op: OpCode.ADD },
|
|
||||||
{ op: OpCode.RETURN }
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['a', 'b'],
|
|
||||||
defaults: {},
|
|
||||||
body: 5,
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
{ type: 'number', value: 10 },
|
|
||||||
{ type: 'number', value: 20 }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'number', value: 30 })
|
expect(result).toEqual({ type: 'number', value: 30 })
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { test, expect } from "bun:test"
|
import { test, expect } from "bun:test"
|
||||||
|
import { toBytecode } from "#bytecode"
|
||||||
import { VM } from "#vm"
|
import { VM } from "#vm"
|
||||||
import { OpCode } from "#opcode"
|
|
||||||
import { toValue } from "#value"
|
|
||||||
|
|
||||||
test("TAIL_CALL - basic tail recursive countdown", async () => {
|
test("TAIL_CALL - basic tail recursive countdown", async () => {
|
||||||
// Tail recursive function that counts down to 0
|
// Tail recursive function that counts down to 0
|
||||||
|
|
@ -9,48 +8,27 @@ test("TAIL_CALL - basic tail recursive countdown", async () => {
|
||||||
// if (n === 0) return "done"
|
// if (n === 0) return "done"
|
||||||
// return countdown(n - 1) // tail call
|
// return countdown(n - 1) // tail call
|
||||||
// }
|
// }
|
||||||
const vm = new VM({
|
const bytecode = toBytecode(`
|
||||||
instructions: [
|
MAKE_FUNCTION (n) #6
|
||||||
// 0: Setup - create function and call it
|
STORE countdown
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 },
|
LOAD countdown
|
||||||
{ op: OpCode.STORE, operand: 'countdown' },
|
PUSH 5
|
||||||
{ op: OpCode.LOAD, operand: 'countdown' },
|
CALL #1
|
||||||
{ op: OpCode.PUSH, operand: 1 }, // start with 5
|
HALT
|
||||||
{ op: OpCode.CALL, operand: 1 },
|
LOAD n
|
||||||
{ op: OpCode.HALT },
|
PUSH 0
|
||||||
|
EQ
|
||||||
|
JUMP_IF_FALSE #2
|
||||||
|
PUSH "done"
|
||||||
|
RETURN
|
||||||
|
LOAD countdown
|
||||||
|
LOAD n
|
||||||
|
PUSH 1
|
||||||
|
SUB
|
||||||
|
TAIL_CALL #1
|
||||||
|
`)
|
||||||
|
|
||||||
// 6: Function body
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 2 }, // 0
|
|
||||||
{ op: OpCode.EQ },
|
|
||||||
{ op: OpCode.JUMP_IF_FALSE, operand: 2 }, // if n !== 0, jump to recursive case
|
|
||||||
{ op: OpCode.PUSH, operand: 3 }, // return "done"
|
|
||||||
{ op: OpCode.RETURN },
|
|
||||||
|
|
||||||
// 12: Recursive case (tail call)
|
|
||||||
{ op: OpCode.LOAD, operand: 'countdown' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 4 }, // 1
|
|
||||||
{ op: OpCode.SUB },
|
|
||||||
{ op: OpCode.TAIL_CALL, operand: 1 } // tail call with n-1
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['n'],
|
|
||||||
defaults: {},
|
|
||||||
body: 6,
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
toValue(5),
|
|
||||||
toValue(0),
|
|
||||||
toValue('done'),
|
|
||||||
toValue(1)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'string', value: 'done' })
|
expect(result).toEqual({ type: 'string', value: 'done' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -59,99 +37,58 @@ test("TAIL_CALL - tail recursive sum with accumulator", async () => {
|
||||||
// if (n === 0) return acc
|
// if (n === 0) return acc
|
||||||
// return sum(n - 1, acc + n) // tail call
|
// return sum(n - 1, acc + n) // tail call
|
||||||
// }
|
// }
|
||||||
const vm = new VM({
|
const bytecode = toBytecode(`
|
||||||
instructions: [
|
MAKE_FUNCTION (n acc) #7
|
||||||
// 0: Setup
|
STORE sum
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 },
|
LOAD sum
|
||||||
{ op: OpCode.STORE, operand: 'sum' },
|
PUSH 10
|
||||||
{ op: OpCode.LOAD, operand: 'sum' },
|
PUSH 0
|
||||||
{ op: OpCode.PUSH, operand: 1 }, // n = 10
|
CALL #2
|
||||||
{ op: OpCode.PUSH, operand: 2 }, // acc = 0
|
HALT
|
||||||
{ op: OpCode.CALL, operand: 2 },
|
LOAD n
|
||||||
{ op: OpCode.HALT },
|
PUSH 0
|
||||||
|
EQ
|
||||||
|
JUMP_IF_FALSE #2
|
||||||
|
LOAD acc
|
||||||
|
RETURN
|
||||||
|
LOAD sum
|
||||||
|
LOAD n
|
||||||
|
PUSH 1
|
||||||
|
SUB
|
||||||
|
LOAD acc
|
||||||
|
LOAD n
|
||||||
|
ADD
|
||||||
|
TAIL_CALL #2
|
||||||
|
`)
|
||||||
|
|
||||||
// 7: Function body
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 2 }, // 0
|
|
||||||
{ op: OpCode.EQ },
|
|
||||||
{ op: OpCode.JUMP_IF_FALSE, operand: 2 },
|
|
||||||
{ op: OpCode.LOAD, operand: 'acc' },
|
|
||||||
{ op: OpCode.RETURN },
|
|
||||||
|
|
||||||
// 13: Recursive case
|
|
||||||
{ op: OpCode.LOAD, operand: 'sum' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 3 }, // 1
|
|
||||||
{ op: OpCode.SUB },
|
|
||||||
{ op: OpCode.LOAD, operand: 'acc' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.ADD },
|
|
||||||
{ op: OpCode.TAIL_CALL, operand: 2 }
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['n', 'acc'],
|
|
||||||
defaults: {},
|
|
||||||
body: 7,
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
toValue(10),
|
|
||||||
toValue(0),
|
|
||||||
toValue(1)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'number', value: 55 }) // sum of 1..10
|
expect(result).toEqual({ type: 'number', value: 55 }) // sum of 1..10
|
||||||
})
|
})
|
||||||
|
|
||||||
test("TAIL_CALL - doesn't overflow stack with deep recursion", async () => {
|
test("TAIL_CALL - doesn't overflow stack with deep recursion", async () => {
|
||||||
// This would overflow the stack with regular CALL
|
// This would overflow the stack with regular CALL
|
||||||
// but should work fine with TAIL_CALL
|
// but should work fine with TAIL_CALL
|
||||||
const vm = new VM({
|
const bytecode = toBytecode(`
|
||||||
instructions: [
|
MAKE_FUNCTION (n) #6
|
||||||
// 0: Setup
|
STORE deep
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 },
|
LOAD deep
|
||||||
{ op: OpCode.STORE, operand: 'deep' },
|
PUSH 10000
|
||||||
{ op: OpCode.LOAD, operand: 'deep' },
|
CALL #1
|
||||||
{ op: OpCode.PUSH, operand: 1 }, // 10000 iterations
|
HALT
|
||||||
{ op: OpCode.CALL, operand: 1 },
|
LOAD n
|
||||||
{ op: OpCode.HALT },
|
PUSH 0
|
||||||
|
LTE
|
||||||
|
JUMP_IF_FALSE #2
|
||||||
|
PUSH "success"
|
||||||
|
RETURN
|
||||||
|
LOAD deep
|
||||||
|
LOAD n
|
||||||
|
PUSH 1
|
||||||
|
SUB
|
||||||
|
TAIL_CALL #1
|
||||||
|
`)
|
||||||
|
|
||||||
// 6: Function body
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 2 }, // 0
|
|
||||||
{ op: OpCode.LTE },
|
|
||||||
{ op: OpCode.JUMP_IF_FALSE, operand: 2 },
|
|
||||||
{ op: OpCode.PUSH, operand: 3 }, // "success"
|
|
||||||
{ op: OpCode.RETURN },
|
|
||||||
|
|
||||||
// 12: Tail recursive case
|
|
||||||
{ op: OpCode.LOAD, operand: 'deep' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 4 }, // 1
|
|
||||||
{ op: OpCode.SUB },
|
|
||||||
{ op: OpCode.TAIL_CALL, operand: 1 }
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['n'],
|
|
||||||
defaults: {},
|
|
||||||
body: 6,
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
toValue(10000), // This would overflow with regular recursion
|
|
||||||
toValue(0),
|
|
||||||
toValue('success'),
|
|
||||||
toValue(1)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'string', value: 'success' })
|
expect(result).toEqual({ type: 'string', value: 'success' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -159,71 +96,39 @@ test("TAIL_CALL - tail call to different function", async () => {
|
||||||
// TAIL_CALL can call a different function (mutual recursion)
|
// TAIL_CALL can call a different function (mutual recursion)
|
||||||
// function even(n) { return n === 0 ? true : odd(n - 1) }
|
// function even(n) { return n === 0 ? true : odd(n - 1) }
|
||||||
// function odd(n) { return n === 0 ? false : even(n - 1) }
|
// function odd(n) { return n === 0 ? false : even(n - 1) }
|
||||||
const vm = new VM({
|
const bytecode = toBytecode(`
|
||||||
instructions: [
|
MAKE_FUNCTION (n) #8
|
||||||
// 0: Setup both functions
|
STORE even
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 0 }, // even
|
MAKE_FUNCTION (n) #19
|
||||||
{ op: OpCode.STORE, operand: 'even' },
|
STORE odd
|
||||||
{ op: OpCode.MAKE_FUNCTION, operand: 1 }, // odd
|
LOAD even
|
||||||
{ op: OpCode.STORE, operand: 'odd' },
|
PUSH 7
|
||||||
{ op: OpCode.LOAD, operand: 'even' },
|
CALL #1
|
||||||
{ op: OpCode.PUSH, operand: 2 }, // test with 7
|
HALT
|
||||||
{ op: OpCode.CALL, operand: 1 },
|
LOAD n
|
||||||
{ op: OpCode.HALT },
|
PUSH 0
|
||||||
|
EQ
|
||||||
|
JUMP_IF_FALSE #2
|
||||||
|
PUSH true
|
||||||
|
RETURN
|
||||||
|
LOAD odd
|
||||||
|
LOAD n
|
||||||
|
PUSH 1
|
||||||
|
SUB
|
||||||
|
TAIL_CALL #1
|
||||||
|
LOAD n
|
||||||
|
PUSH 0
|
||||||
|
EQ
|
||||||
|
JUMP_IF_FALSE #2
|
||||||
|
PUSH false
|
||||||
|
RETURN
|
||||||
|
LOAD even
|
||||||
|
LOAD n
|
||||||
|
PUSH 1
|
||||||
|
SUB
|
||||||
|
TAIL_CALL #1
|
||||||
|
`)
|
||||||
|
|
||||||
// 8: even function body
|
const result = await new VM(bytecode).run()
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 3 }, // 0
|
|
||||||
{ op: OpCode.EQ },
|
|
||||||
{ op: OpCode.JUMP_IF_FALSE, operand: 2 },
|
|
||||||
{ op: OpCode.PUSH, operand: 4 }, // true
|
|
||||||
{ op: OpCode.RETURN },
|
|
||||||
// Tail call to odd
|
|
||||||
{ op: OpCode.LOAD, operand: 'odd' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 5 }, // 1
|
|
||||||
{ op: OpCode.SUB },
|
|
||||||
{ op: OpCode.TAIL_CALL, operand: 1 },
|
|
||||||
|
|
||||||
// 19: odd function body
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 3 }, // 0
|
|
||||||
{ op: OpCode.EQ },
|
|
||||||
{ op: OpCode.JUMP_IF_FALSE, operand: 2 },
|
|
||||||
{ op: OpCode.PUSH, operand: 6 }, // false
|
|
||||||
{ op: OpCode.RETURN },
|
|
||||||
// Tail call to even
|
|
||||||
{ op: OpCode.LOAD, operand: 'even' },
|
|
||||||
{ op: OpCode.LOAD, operand: 'n' },
|
|
||||||
{ op: OpCode.PUSH, operand: 5 }, // 1
|
|
||||||
{ op: OpCode.SUB },
|
|
||||||
{ op: OpCode.TAIL_CALL, operand: 1 }
|
|
||||||
],
|
|
||||||
constants: [
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['n'],
|
|
||||||
defaults: {},
|
|
||||||
body: 8, // even body
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'function_def',
|
|
||||||
params: ['n'],
|
|
||||||
defaults: {},
|
|
||||||
body: 19, // odd body
|
|
||||||
variadic: false,
|
|
||||||
kwargs: false
|
|
||||||
},
|
|
||||||
toValue(7),
|
|
||||||
toValue(0),
|
|
||||||
toValue(true),
|
|
||||||
toValue(1),
|
|
||||||
toValue(false)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await vm.run()
|
|
||||||
expect(result).toEqual({ type: 'boolean', value: false }) // 7 is odd
|
expect(result).toEqual({ type: 'boolean', value: false }) // 7 is odd
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user