diff --git a/SPEC.md b/SPEC.md index 2689bd3..48ece35 100644 --- a/SPEC.md +++ b/SPEC.md @@ -376,15 +376,21 @@ Items are popped in reverse order (item1 is array[0]). Index is coerced to number and floored. #### ARRAY_SET -**Operand**: None -**Effect**: Set array element at index (mutates array) -**Stack**: [array, index, value] → [] +**Operand**: None +**Effect**: Set array element at index (mutates array) +**Stack**: [array, index, value] → [] **Errors**: Throws if not array or index out of bounds +#### ARRAY_PUSH +**Operand**: None +**Effect**: Append value to end of array (mutates array, grows by 1) +**Stack**: [array, value] → [] +**Errors**: Throws if not array + #### ARRAY_LEN -**Operand**: None -**Effect**: Get array length -**Stack**: [array] → [length] +**Operand**: None +**Effect**: Get array length +**Stack**: [array] → [length] **Errors**: Throws if not array ### Dictionary Operations diff --git a/src/opcode.ts b/src/opcode.ts index 8d9f645..a48f436 100644 --- a/src/opcode.ts +++ b/src/opcode.ts @@ -48,6 +48,7 @@ export enum OpCode { MAKE_ARRAY, ARRAY_GET, ARRAY_SET, + ARRAY_PUSH, ARRAY_LEN, // dicts diff --git a/src/vm.ts b/src/vm.ts index 1c256fb..bbc12ff 100644 --- a/src/vm.ts +++ b/src/vm.ts @@ -212,6 +212,16 @@ export class VM { setArray.value[setIdx] = setValue break + case OpCode.ARRAY_PUSH: + const pushValue = this.stack.pop()! + const pushArray = this.stack.pop()! + + if (pushArray.type !== 'array') + throw new Error('ARRAY_PUSH: not an array') + + pushArray.value.push(pushValue) + break + case OpCode.ARRAY_LEN: const lenArray = this.stack.pop()! if (lenArray.type !== 'array') diff --git a/tests/basic.test.ts b/tests/basic.test.ts index 26bb477..2ab8144 100644 --- a/tests/basic.test.ts +++ b/tests/basic.test.ts @@ -418,6 +418,33 @@ test("ARRAY_SET - sets element", async () => { expect(await run(toBytecode(str))).toEqual({ type: 'number', value: 99 }) }) +test("ARRAY_PUSH - appends to array", async () => { + const str = ` + PUSH 10 + PUSH 20 + MAKE_ARRAY 2 + DUP + PUSH 30 + ARRAY_PUSH + ARRAY_LEN +` + expect(await run(toBytecode(str))).toEqual({ type: 'number', value: 3 }) +}) + +test("ARRAY_PUSH - mutates original array", async () => { + const str = ` + PUSH 10 + PUSH 20 + MAKE_ARRAY 2 + DUP + PUSH 30 + ARRAY_PUSH + PUSH 2 + ARRAY_GET +` + expect(await run(toBytecode(str))).toEqual({ type: 'number', value: 30 }) +}) + test("ARRAY_LEN - gets length", async () => { const str = ` PUSH 10