Compare commits
5 Commits
e60e3184fa
...
211fa11a65
| Author | SHA1 | Date | |
|---|---|---|---|
| 211fa11a65 | |||
| a8fd79a990 | |||
| 2abf3558d5 | |||
| cc8d64b3ec | |||
| 83fad9a68f |
26
bin/repl
26
bin/repl
|
|
@ -144,19 +144,33 @@ async function repl() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
codeHistory.push(trimmed)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const compiler = new Compiler(trimmed, Object.keys(globals))
|
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) {
|
} catch (error: any) {
|
||||||
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
|
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
|
||||||
codeHistory.pop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.prompt()
|
rl.prompt()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"dev": "bun generate-parser && bun --hot src/server/server.tsx",
|
"dev": "bun generate-parser && bun --hot src/server/server.tsx",
|
||||||
"generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts",
|
"generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts",
|
||||||
"repl": "bun generate-parser && bun bin/repl",
|
"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": {
|
"dependencies": {
|
||||||
"@codemirror/view": "^6.38.3",
|
"@codemirror/view": "^6.38.3",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { type Value, toValue, toNull } from 'reefvm'
|
||||||
|
|
||||||
export const list = {
|
export const list = {
|
||||||
slice: (list: any[], start: number, end?: number) => list.slice(start, end),
|
slice: (list: any[], start: number, end?: number) => list.slice(start, end),
|
||||||
map: async (list: any[], cb: Function) => {
|
map: async (list: any[], cb: Function) => {
|
||||||
|
|
@ -40,9 +42,41 @@ export const list = {
|
||||||
return true
|
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
|
// sequence operations
|
||||||
reverse: (list: any[]) => list.slice().reverse(),
|
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),
|
concat: (...lists: any[][]) => lists.flat(1),
|
||||||
flatten: (list: any[], depth: number = 1) => list.flat(depth),
|
flatten: (list: any[], depth: number = 1) => list.flat(depth),
|
||||||
unique: (list: any[]) => Array.from(new Set(list)),
|
unique: (list: any[]) => Array.from(new Set(list)),
|
||||||
|
|
@ -92,4 +126,13 @@ export const list = {
|
||||||
}
|
}
|
||||||
return groups
|
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
|
||||||
|
|
@ -344,6 +344,84 @@ describe('collections', () => {
|
||||||
await expect(`list.index-of [1 2 3] 5`).toEvaluateTo(-1, globals)
|
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 () => {
|
test('list.any? checks if any element matches', async () => {
|
||||||
await expect(`
|
await expect(`
|
||||||
gt-three = do x: x > 3 end
|
gt-three = do x: x > 3 end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user