SWAP opcode

This commit is contained in:
Chris Wanstrath 2025-10-29 20:37:09 -07:00
parent c69b172c78
commit 4b2fd61554
5 changed files with 68 additions and 0 deletions

View File

@ -185,6 +185,7 @@ CALL
- `PUSH <const>` - Push constant
- `POP` - Remove top
- `DUP` - Duplicate top
- `SWAP` - Swap top two values
### Variables
- `LOAD <name>` - Push variable value (throws if not found)
@ -360,6 +361,30 @@ POP
.end: ; Result on stack
```
### Reversing Operand Order
Use SWAP to reverse operand order for non-commutative operations:
```
; Compute 10 / 2 when values are in reverse order
PUSH 2
PUSH 10
SWAP ; Now: [10, 2]
DIV ; 10 / 2 = 5
```
```
; Compute "hello" - "world" (subtraction with strings coerced to numbers)
PUSH "world"
PUSH "hello"
SWAP ; Now: ["hello", "world"]
SUB ; Result based on operand order
```
**Common Use Cases**:
- Division and subtraction when operands are in wrong order
- String concatenation with specific order
- Preparing arguments for functions that care about position
### Try-Catch
```
PUSH_TRY .catch

View File

@ -138,6 +138,11 @@ type ExceptionHandler = {
**Effect**: Duplicate top of stack
**Stack**: [value] → [value, value]
#### SWAP
**Operand**: None
**Effect**: Swap the top two values on the stack
**Stack**: [value1, value2] → [value2, value1]
### Variable Operations
#### LOAD

View File

@ -3,6 +3,7 @@ export enum OpCode {
PUSH, // operand: constant index (number) | stack: [] → [value]
POP, // operand: none | stack: [value] → []
DUP, // operand: none | stack: [value] → [value, value]
SWAP, // operand: none | stack: [value1, value2] → [value2, value1]
// variables
LOAD, // operand: variable name (identifier) | stack: [] → [value]

View File

@ -144,6 +144,13 @@ export class VM {
this.stack.push(this.stack[this.stack.length - 1]!)
break
case OpCode.SWAP:
const first = this.stack.pop()!
const second = this.stack.pop()!
this.stack.push(first)
this.stack.push(second)
break
case OpCode.ADD:
const b = this.stack.pop()!
const a = this.stack.pop()!

View File

@ -538,6 +538,36 @@ describe("DUP", () => {
})
})
describe("SWAP", () => {
test("swaps two numbers", async () => {
const str = `
PUSH 10
PUSH 20
SWAP
`
expect(await run(toBytecode(str))).toEqual({ type: 'number', value: 10 })
})
test("swap and use in subtraction", async () => {
const str = `
PUSH 5
PUSH 10
SWAP
SUB
`
expect(await run(toBytecode(str))).toEqual({ type: 'number', value: 5 })
})
test("swap different types", async () => {
const str = `
PUSH "hello"
PUSH 42
SWAP
`
expect(await run(toBytecode(str))).toEqual({ type: 'string', value: 'hello' })
})
})
describe("EQ", () => {
test("equality comparison", async () => {
const str = `