forked from defunkt/ReefVM
ADD: error checking
This commit is contained in:
parent
0b5d3e634c
commit
c69b172c78
9
SPEC.md
9
SPEC.md
|
|
@ -184,7 +184,8 @@ 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
|
||||
- Else if both operands are dicts, merges them (b's keys overwrite a's keys on conflict)
|
||||
- Otherwise, converts both to numbers and performs numeric addition
|
||||
- Else if both operands are numbers, performs numeric addition
|
||||
- Otherwise, throws an error
|
||||
|
||||
**Examples**:
|
||||
- `5 + 3` → `8` (numeric addition)
|
||||
|
|
@ -196,6 +197,12 @@ Performs different operations depending on operand types:
|
|||
- `{a: 1} + {b: 2}` → `{a: 1, b: 2}` (dict merge)
|
||||
- `{a: 1, b: 2} + {b: 99}` → `{a: 1, b: 99}` (dict merge, b overwrites)
|
||||
|
||||
**Invalid operations** (throw errors):
|
||||
- `true + false` → Error
|
||||
- `null + 5` → Error
|
||||
- `[1] + 5` → Error
|
||||
- `{a: 1} + 5` → Error
|
||||
|
||||
#### SUB
|
||||
**Stack**: [a, b] → [a - b]
|
||||
|
||||
|
|
|
|||
|
|
@ -148,22 +148,20 @@ export class VM {
|
|||
const b = this.stack.pop()!
|
||||
const a = this.stack.pop()!
|
||||
|
||||
// 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 if (a.type === 'dict' && b.type === 'dict') {
|
||||
// two dicts, merge them (b's keys overwrite a's keys)
|
||||
const merged = new Map(a.value)
|
||||
for (const [key, value] of b.value) {
|
||||
merged.set(key, value)
|
||||
}
|
||||
this.stack.push({ type: 'dict', value: merged })
|
||||
} else if (a.type === 'number' && b.type === 'number') {
|
||||
this.stack.push(toValue(a.value + b.value))
|
||||
} else {
|
||||
// Otherwise do numeric addition
|
||||
this.stack.push(toValue(toNumber(a) + toNumber(b)))
|
||||
throw new Error(`ADD: Cannot add ${a.type} and ${b.type}`)
|
||||
}
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ test("TRY_CALL - handles null values", async () => {
|
|||
|
||||
test("TRY_CALL - function can access its parameters", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["MAKE_FUNCTION", ["x"], ".body"],
|
||||
["MAKE_FUNCTION", ["x=0"], ".body"],
|
||||
["STORE", "addFive"],
|
||||
["PUSH", 10],
|
||||
["STORE", "x"],
|
||||
|
|
@ -501,8 +501,8 @@ test("TRY_CALL - function can access its parameters", async () => {
|
|||
])
|
||||
|
||||
const result = await run(bytecode)
|
||||
// Function is called with 0 args, so x inside function should be null
|
||||
// Then we add 5 to null (which coerces to 0)
|
||||
// Function is called with 0 args, so x defaults to 0
|
||||
// Then we add 5 to 0
|
||||
expect(result).toEqual({ type: 'number', value: 5 })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -373,6 +373,87 @@ describe("ADD", () => {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("cannot add boolean + boolean", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", true],
|
||||
["PUSH", false],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add boolean and boolean')
|
||||
})
|
||||
|
||||
test("cannot add null + number", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", null],
|
||||
["PUSH", 5],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add null and number')
|
||||
})
|
||||
|
||||
test("cannot add array + dict", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", 1],
|
||||
["MAKE_ARRAY", 1],
|
||||
["MAKE_DICT", 0],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add array and dict')
|
||||
})
|
||||
|
||||
test("cannot add array + number", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", 1],
|
||||
["MAKE_ARRAY", 1],
|
||||
["PUSH", 5],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add array and number')
|
||||
})
|
||||
|
||||
test("cannot add dict + number", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["MAKE_DICT", 0],
|
||||
["PUSH", 5],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add dict and number')
|
||||
})
|
||||
|
||||
test("cannot add function + number", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["MAKE_FUNCTION", [], ".body"],
|
||||
["PUSH", 5],
|
||||
["ADD"],
|
||||
["HALT"],
|
||||
[".body:"],
|
||||
["RETURN"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add function and number')
|
||||
})
|
||||
|
||||
test("cannot add boolean + null", async () => {
|
||||
const bytecode = toBytecode([
|
||||
["PUSH", true],
|
||||
["PUSH", null],
|
||||
["ADD"],
|
||||
["HALT"]
|
||||
])
|
||||
|
||||
await expect(run(bytecode)).rejects.toThrow('ADD: Cannot add boolean and null')
|
||||
})
|
||||
})
|
||||
|
||||
describe("SUB", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user