import { readFileSync } from 'fs' import { VM, fromValue, toValue, isValue, type Bytecode } from 'reefvm' import { type Tree } from '@lezer/common' import { Compiler } from '#compiler/compiler' import { parser } from '#parser/shrimp' import { globals as parserGlobals, setGlobals as setParserGlobals } from '#parser/tokenizer' import { globals as prelude } from '#prelude' export { Compiler } from '#compiler/compiler' export { parser } from '#parser/shrimp' export { globals as prelude } from '#prelude' export type { Tree } from '@lezer/common' export { type Value, type Bytecode } from 'reefvm' export { toValue, fromValue, isValue, Scope, VM, bytecodeToString } from 'reefvm' export class Shrimp { vm: VM private globals?: Record constructor(globals?: Record) { const emptyBytecode = { instructions: [], constants: [], labels: new Map() } this.vm = new VM(emptyBytecode, Object.assign({}, prelude, globals ?? {})) this.globals = globals } get(name: string): any { const value = this.vm.scope.get(name) return value ? fromValue(value, this.vm) : null } set(name: string, value: any) { this.vm.scope.set(name, toValue(value, this.vm)) } has(name: string): boolean { return this.vm.scope.has(name) } async call(name: string, ...args: any[]): Promise { const result = await this.vm.call(name, ...args) return isValue(result) ? fromValue(result, this.vm) : result } parse(code: string): Tree { return parseCode(code, this.globals) } compile(code: string): Bytecode { return compileCode(code, this.globals) } async run(code: string | Bytecode, locals?: Record): Promise { let bytecode if (typeof code === 'string') { const compiler = new Compiler(code, Object.keys(Object.assign({}, prelude, this.globals ?? {}, locals ?? {}))) bytecode = compiler.bytecode } else { bytecode = code } if (locals) this.vm.pushScope(locals) this.vm.appendBytecode(bytecode) await this.vm.continue() if (locals) this.vm.popScope() return this.vm.stack.length ? fromValue(this.vm.stack.at(-1)!, this.vm) : null } } export async function runFile(path: string, globals?: Record): Promise { const code = readFileSync(path, 'utf-8') return await runCode(code, globals) } export async function runCode(code: string, globals?: Record): Promise { return await runBytecode(compileCode(code, globals), globals) } export async function runBytecode(bytecode: Bytecode, globals?: Record): Promise { const vm = new VM(bytecode, Object.assign({}, prelude, globals)) await vm.run() return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!, vm) : null } export function compileFile(path: string, globals?: Record): Bytecode { const code = readFileSync(path, 'utf-8') return compileCode(code, globals) } export function compileCode(code: string, globals?: Record): Bytecode { const globalNames = [...Object.keys(prelude), ...(globals ? Object.keys(globals) : [])] const compiler = new Compiler(code, globalNames) return compiler.bytecode } export function parseFile(path: string, globals?: Record): Tree { const code = readFileSync(path, 'utf-8') return parseCode(code, globals) } export function parseCode(code: string, globals?: Record): Tree { const oldGlobals = [...parserGlobals] const globalNames = [...Object.keys(prelude), ...(globals ? Object.keys(globals) : [])] setParserGlobals(globalNames) const result = parser.parse(code) setParserGlobals(oldGlobals) return result }