Merge branch 'main' into try-catch-throw-finally

This commit is contained in:
probablycorey 2025-10-31 16:53:01 +00:00
commit c883854187
5 changed files with 145 additions and 10 deletions

View File

@ -144,19 +144,33 @@ async function repl() {
return
}
codeHistory.push(trimmed)
try {
const compiler = new Compiler(trimmed, Object.keys(globals))
vm.appendBytecode(compiler.bytecode)
// Save VM state before appending bytecode, in case execution fails
const savedInstructions = [...vm.instructions]
const savedConstants = [...vm.constants]
const savedPc = vm.pc
const savedScope = vm.scope
const savedStopped = vm.stopped
const result = await vm.continue()
try {
vm.appendBytecode(compiler.bytecode)
const result = await vm.continue()
console.log(`${colors.dim}=>${colors.reset} ${formatValue(result)}`)
codeHistory.push(trimmed)
console.log(`${colors.dim}=>${colors.reset} ${formatValue(result)}`)
} catch (error: any) {
vm.instructions = savedInstructions
vm.constants = savedConstants
vm.pc = savedPc
vm.scope = savedScope
vm.stopped = savedStopped
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
}
} catch (error: any) {
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
codeHistory.pop()
}
rl.prompt()

View File

@ -62,7 +62,7 @@
"hono": ["hono@4.9.8", "", {}, "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg=="],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#030eb7487165b3ba502965a8b7fa09c4b5fdb0da", { "peerDependencies": { "typescript": "^5" } }, "030eb7487165b3ba502965a8b7fa09c4b5fdb0da"],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#c69b172c78853756ec8acba5bc33d93eb6a571c6", { "peerDependencies": { "typescript": "^5" } }, "c69b172c78853756ec8acba5bc33d93eb6a571c6"],
"style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="],

View File

@ -7,7 +7,7 @@
"dev": "bun generate-parser && bun --hot src/server/server.tsx",
"generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts",
"repl": "bun generate-parser && bun bin/repl",
"update-reef": "cd packages/ReefVM && git pull origin main"
"update-reef": "rm -rf ~/.bun/install/cache/ && bun update reefvm"
},
"dependencies": {
"@codemirror/view": "^6.38.3",

View File

@ -1,3 +1,5 @@
import { type Value, toValue, toNull } from 'reefvm'
export const list = {
slice: (list: any[], start: number, end?: number) => list.slice(start, end),
map: async (list: any[], cb: Function) => {
@ -40,9 +42,41 @@ export const list = {
return true
},
// mutating
push: (list: Value, item: Value) => {
if (list.type !== 'array') return toNull()
return toValue(list.value.push(item))
},
pop: (list: Value) => {
if (list.type !== 'array') return toNull()
return toValue(list.value.pop())
},
shift: (list: Value) => {
if (list.type !== 'array') return toNull()
return toValue(list.value.shift())
},
unshift: (list: Value, item: Value) => {
if (list.type !== 'array') return toNull()
return toValue(list.value.unshift(item))
},
splice: (list: Value, start: Value, deleteCount: Value, ...items: Value[]) => {
const realList = list.value as any[]
const realStart = start.value as number
const realDeleteCount = deleteCount.value as number
const realItems = items.map(item => item.value)
return toValue(realList.splice(realStart, realDeleteCount, ...realItems))
},
// sequence operations
reverse: (list: any[]) => list.slice().reverse(),
sort: (list: any[], cb?: (a: any, b: any) => number) => list.slice().sort(cb),
sort: async (list: any[], cb?: (a: any, b: any) => number) => {
const arr = [...list]
if (!cb) return arr.sort()
for (let i = 0; i < arr.length; i++)
for (let j = i + 1; j < arr.length; j++)
if ((await cb(arr[i], arr[j])) > 0) [arr[i], arr[j]] = [arr[j], arr[i]]
return arr
},
concat: (...lists: any[][]) => lists.flat(1),
flatten: (list: any[], depth: number = 1) => list.flat(depth),
unique: (list: any[]) => Array.from(new Set(list)),
@ -86,4 +120,13 @@ export const list = {
}
return groups
},
}
}
// raw functions deal directly in Value types, meaning we can modify collection
// careful - they MUST return a Value!
; (list.splice as any).raw = true
; (list.push as any).raw = true
; (list.pop as any).raw = true
; (list.shift as any).raw = true
; (list.unshift as any).raw = true

View File

@ -341,6 +341,84 @@ describe('collections', () => {
await expect(`list.index-of [1 2 3] 5`).toEvaluateTo(-1, globals)
})
test('list.push adds to end and mutates array', async () => {
await expect(`arr = [1 2]; list.push arr 3; arr`).toEvaluateTo([1, 2, 3], globals)
})
test('list.push returns the size of the array', async () => {
await expect(`arr = [1 2]; arr | list.push 3`).toEvaluateTo(3, globals)
})
test('list.pop removes from end and mutates array', async () => {
await expect(`arr = [1 2 3]; list.pop arr; arr`).toEvaluateTo([1, 2], globals)
})
test('list.pop returns removed element', async () => {
await expect(`list.pop [1 2 3]`).toEvaluateTo(3, globals)
})
test('list.pop returns null for empty array', async () => {
await expect(`list.pop []`).toEvaluateTo(null, globals)
})
test('list.shift removes from start and mutates array', async () => {
await expect(`arr = [1 2 3]; list.shift arr; arr`).toEvaluateTo([2, 3], globals)
})
test('list.shift returns removed element', async () => {
await expect(`list.shift [1 2 3]`).toEvaluateTo(1, globals)
})
test('list.shift returns null for empty array', async () => {
await expect(`list.shift []`).toEvaluateTo(null, globals)
})
test('list.unshift adds to start and mutates array', async () => {
await expect(`arr = [2 3]; list.unshift arr 1; arr`).toEvaluateTo([1, 2, 3], globals)
})
test('list.unshift returns the length of the array', async () => {
await expect(`arr = [2 3]; arr | list.unshift 1`).toEvaluateTo(3, globals)
})
test('list.splice removes elements and mutates array', async () => {
await expect(`arr = [1 2 3 4 5]; list.splice arr 1 2; arr`).toEvaluateTo([1, 4, 5], globals)
})
test('list.splice returns removed elements', async () => {
await expect(`list.splice [1 2 3 4 5] 1 2`).toEvaluateTo([2, 3], globals)
})
test('list.splice from start', async () => {
await expect(`list.splice [1 2 3 4 5] 0 2`).toEvaluateTo([1, 2], globals)
})
test('list.splice to end', async () => {
await expect(`arr = [1 2 3 4 5]; list.splice arr 3 2; arr`).toEvaluateTo([1, 2, 3], globals)
})
test('list.sort with no callback sorts ascending', async () => {
await expect(`list.sort [3 1 4 1 5] null`).toEvaluateTo([1, 1, 3, 4, 5], globals)
})
test('list.sort with callback sorts using comparator', async () => {
await expect(`
desc = do a b:
b - a
end
list.sort [3 1 4 1 5] desc
`).toEvaluateTo([5, 4, 3, 1, 1], globals)
})
test('list.sort with callback for strings by length', async () => {
await expect(`
by-length = do a b:
(length a) - (length b)
end
list.sort ['cat' 'a' 'dog' 'elephant'] by-length
`).toEvaluateTo(['a', 'cat', 'dog', 'elephant'], globals)
})
test('list.any? checks if any element matches', async () => {
await expect(`
gt-three = do x: x > 3 end