forked from defunkt/ReefVM
ADD concats arrays too
This commit is contained in:
parent
9618dd6414
commit
956fd576f8
5
SPEC.md
5
SPEC.md
|
|
@ -180,8 +180,9 @@ All arithmetic operations pop two values, perform operation, push result as numb
|
|||
#### ADD
|
||||
**Stack**: [a, b] → [a + b]
|
||||
|
||||
Performs addition or string concatenation depending on operand types:
|
||||
Performs different operations depending on operand types:
|
||||
- If either operand is a string, converts both to strings and concatenates
|
||||
- Else if both operands are arrays, concatenates the arrays
|
||||
- Otherwise, converts both to numbers and performs numeric addition
|
||||
|
||||
**Examples**:
|
||||
|
|
@ -189,6 +190,8 @@ Performs addition or string concatenation depending on operand types:
|
|||
- `"hello" + " world"` → `"hello world"` (string concatenation)
|
||||
- `"count: " + 42` → `"count: 42"` (string concatenation)
|
||||
- `100 + " items"` → `"100 items"` (string concatenation)
|
||||
- `[1, 2, 3] + [4]` → `[1, 2, 3, 4]` (array concatenation)
|
||||
- `[1, 2] + [3, 4]` → `[1, 2, 3, 4]` (array concatenation)
|
||||
|
||||
#### SUB
|
||||
**Stack**: [a, b] → [a - b]
|
||||
|
|
|
|||
116
examples/add-with-arrays.ts
Normal file
116
examples/add-with-arrays.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* Demonstrates the ADD opcode working with arrays
|
||||
*
|
||||
* ADD now handles array concatenation:
|
||||
* - [1, 2, 3] + [4] === [1, 2, 3, 4]
|
||||
* - If both operands are arrays, they are concatenated
|
||||
*/
|
||||
|
||||
import { toBytecode, run } from "#reef"
|
||||
|
||||
// Basic array concatenation
|
||||
const basicConcat = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["PUSH", 3],
|
||||
["MAKE_ARRAY", 3],
|
||||
["PUSH", 4],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
console.log('Basic array concatenation ([1, 2, 3] + [4]):')
|
||||
const result1 = await run(basicConcat)
|
||||
console.log(result1)
|
||||
// Output: { type: 'array', value: [1, 2, 3, 4] }
|
||||
|
||||
// Concatenate two multi-element arrays
|
||||
const multiConcat = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["MAKE_ARRAY", 2],
|
||||
["PUSH", 3],
|
||||
["PUSH", 4],
|
||||
["MAKE_ARRAY", 2],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
console.log('\nConcatenate two arrays ([1, 2] + [3, 4]):')
|
||||
const result2 = await run(multiConcat)
|
||||
console.log(result2)
|
||||
// Output: { type: 'array', value: [1, 2, 3, 4] }
|
||||
|
||||
// Concatenate multiple arrays in sequence
|
||||
const multipleConcat = toBytecode([
|
||||
["PUSH", 1],
|
||||
["MAKE_ARRAY", 1],
|
||||
["PUSH", 2],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["PUSH", 3],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["PUSH", 4],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
console.log('\nMultiple concatenations ([1] + [2] + [3] + [4]):')
|
||||
const result3 = await run(multipleConcat)
|
||||
console.log(result3)
|
||||
// Output: { type: 'array', value: [1, 2, 3, 4] }
|
||||
|
||||
// Concatenate arrays with mixed types
|
||||
const mixedTypes = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", "hello"],
|
||||
["MAKE_ARRAY", 2],
|
||||
["PUSH", true],
|
||||
["PUSH", null],
|
||||
["MAKE_ARRAY", 2],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
console.log('\nConcatenate arrays with mixed types ([1, "hello"] + [true, null]):')
|
||||
const result4 = await run(mixedTypes)
|
||||
console.log(result4)
|
||||
// Output: { type: 'array', value: [1, "hello", true, null] }
|
||||
|
||||
// Concatenate empty array with non-empty
|
||||
const emptyConcat = toBytecode([
|
||||
["MAKE_ARRAY", 0],
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["PUSH", 3],
|
||||
["MAKE_ARRAY", 3],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
console.log('\nConcatenate empty array with [1, 2, 3] ([] + [1, 2, 3]):')
|
||||
const result5 = await run(emptyConcat)
|
||||
console.log(result5)
|
||||
// Output: { type: 'array', value: [1, 2, 3] }
|
||||
|
||||
// Nested arrays
|
||||
const nestedConcat = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["MAKE_ARRAY", 2],
|
||||
["MAKE_ARRAY", 1],
|
||||
["PUSH", 3],
|
||||
["PUSH", 4],
|
||||
["MAKE_ARRAY", 2],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
console.log('\nConcatenate nested arrays ([[1, 2]] + [[3, 4]]):')
|
||||
const result6 = await run(nestedConcat)
|
||||
console.log(result6)
|
||||
// Output: { type: 'array', value: [[1, 2], [3, 4]] }
|
||||
|
|
@ -151,6 +151,9 @@ export class VM {
|
|||
// If either operand is a string, do string concatenation
|
||||
if (a.type === 'string' || b.type === 'string') {
|
||||
this.stack.push(toValue(toString(a) + toString(b)))
|
||||
} else if (a.type === 'array' && b.type === 'array') {
|
||||
// two arrays, concatenate them
|
||||
this.stack.push({ type: 'array', value: [...a.value, ...b.value] })
|
||||
} else {
|
||||
// Otherwise do numeric addition
|
||||
this.stack.push(toValue(toNumber(a) + toNumber(b)))
|
||||
|
|
|
|||
|
|
@ -99,6 +99,122 @@ describe("ADD", () => {
|
|||
`
|
||||
expect(await run(toBytecode(str))).toEqual({ type: 'string', value: 'Result: 15' })
|
||||
})
|
||||
|
||||
test("concatenate two arrays", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["PUSH", 3],
|
||||
["MAKE_ARRAY", 3],
|
||||
["PUSH", 4],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
const result = await run(bytecode)
|
||||
expect(result.type).toBe('array')
|
||||
if (result.type === 'array') {
|
||||
expect(result.value.length).toBe(4)
|
||||
expect(result.value[0]).toEqual({ type: 'number', value: 1 })
|
||||
expect(result.value[1]).toEqual({ type: 'number', value: 2 })
|
||||
expect(result.value[2]).toEqual({ type: 'number', value: 3 })
|
||||
expect(result.value[3]).toEqual({ type: 'number', value: 4 })
|
||||
}
|
||||
})
|
||||
|
||||
test("concatenate empty arrays", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["MAKE_ARRAY", 0],
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["MAKE_ARRAY", 2],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
const result = await run(bytecode)
|
||||
expect(result.type).toBe('array')
|
||||
if (result.type === 'array') {
|
||||
expect(result.value.length).toBe(2)
|
||||
expect(result.value[0]).toEqual({ type: 'number', value: 1 })
|
||||
expect(result.value[1]).toEqual({ type: 'number', value: 2 })
|
||||
}
|
||||
})
|
||||
|
||||
test("concatenate multiple arrays in sequence", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", 1],
|
||||
["MAKE_ARRAY", 1],
|
||||
["PUSH", 2],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["PUSH", 3],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
const result = await run(bytecode)
|
||||
expect(result.type).toBe('array')
|
||||
if (result.type === 'array') {
|
||||
expect(result.value.length).toBe(3)
|
||||
expect(result.value[0]).toEqual({ type: 'number', value: 1 })
|
||||
expect(result.value[1]).toEqual({ type: 'number', value: 2 })
|
||||
expect(result.value[2]).toEqual({ type: 'number', value: 3 })
|
||||
}
|
||||
})
|
||||
|
||||
test("concatenate arrays with different types", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", "hello"],
|
||||
["MAKE_ARRAY", 2],
|
||||
["PUSH", true],
|
||||
["PUSH", null],
|
||||
["MAKE_ARRAY", 2],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
const result = await run(bytecode)
|
||||
expect(result.type).toBe('array')
|
||||
if (result.type === 'array') {
|
||||
expect(result.value.length).toBe(4)
|
||||
expect(result.value[0]).toEqual({ type: 'number', value: 1 })
|
||||
expect(result.value[1]).toEqual({ type: 'string', value: 'hello' })
|
||||
expect(result.value[2]).toEqual({ type: 'boolean', value: true })
|
||||
expect(result.value[3]).toEqual({ type: 'null', value: null })
|
||||
}
|
||||
})
|
||||
|
||||
test("concatenate arrays containing nested arrays", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", 1],
|
||||
["PUSH", 2],
|
||||
["MAKE_ARRAY", 2],
|
||||
["MAKE_ARRAY", 1],
|
||||
["PUSH", 3],
|
||||
["MAKE_ARRAY", 1],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
const result = await run(bytecode)
|
||||
expect(result.type).toBe('array')
|
||||
if (result.type === 'array') {
|
||||
expect(result.value.length).toBe(2)
|
||||
// First element is nested array [1, 2]
|
||||
expect(result.value[0]?.type).toBe('array')
|
||||
if (result.value[0]?.type === 'array') {
|
||||
expect(result.value[0].value.length).toBe(2)
|
||||
expect(result.value[0].value[0]).toEqual({ type: 'number', value: 1 })
|
||||
expect(result.value[0].value[1]).toEqual({ type: 'number', value: 2 })
|
||||
}
|
||||
// Second element is 3
|
||||
expect(result.value[1]).toEqual({ type: 'number', value: 3 })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("SUB", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user