Compare commits
No commits in common. "0bd36438c8c6a4c95b157a4eb9d5d28449267662" and "298948ac449c0e09f31dc7572f31e83f8dd1dced" have entirely different histories.
0bd36438c8
...
298948ac44
6
bin/repl
6
bin/repl
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
import { Compiler } from '../src/compiler/compiler'
|
import { Compiler } from '../src/compiler/compiler'
|
||||||
import { colors, formatValue, globalFunctions } from '../src/prelude'
|
import { colors, formatValue, nativeFunctions, valueFunctions } from '../src/prelude'
|
||||||
import { VM, Scope, bytecodeToString } from 'reefvm'
|
import { VM, Scope, bytecodeToString } from 'reefvm'
|
||||||
import * as readline from 'readline'
|
import * as readline from 'readline'
|
||||||
import { readFileSync, writeFileSync } from 'fs'
|
import { readFileSync, writeFileSync } from 'fs'
|
||||||
|
|
@ -48,7 +48,7 @@ async function repl() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vm ||= new VM({ instructions: [], constants: [] }, globalFunctions)
|
vm ||= new VM({ instructions: [], constants: [] }, nativeFunctions, valueFunctions)
|
||||||
|
|
||||||
if (['/exit', 'exit', '/quit', 'quit'].includes(trimmed)) {
|
if (['/exit', 'exit', '/quit', 'quit'].includes(trimmed)) {
|
||||||
console.log(`\n${colors.yellow}Goodbye!${colors.reset}`)
|
console.log(`\n${colors.yellow}Goodbye!${colors.reset}`)
|
||||||
|
|
@ -211,7 +211,7 @@ async function loadFile(filePath: string): Promise<{ vm: VM; codeHistory: string
|
||||||
|
|
||||||
console.log(`${colors.dim}Loading ${basename(filePath)}...${colors.reset}`)
|
console.log(`${colors.dim}Loading ${basename(filePath)}...${colors.reset}`)
|
||||||
|
|
||||||
const vm = new VM({ instructions: [], constants: [] }, globalFunctions)
|
const vm = new VM({ instructions: [], constants: [] }, nativeFunctions, valueFunctions)
|
||||||
await vm.run()
|
await vm.run()
|
||||||
|
|
||||||
const codeHistory: string[] = []
|
const codeHistory: string[] = []
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
import { Compiler } from '../src/compiler/compiler'
|
import { Compiler } from '../src/compiler/compiler'
|
||||||
import { colors, globalFunctions } from '../src/prelude'
|
import { colors, nativeFunctions, valueFunctions } from '../src/prelude'
|
||||||
import { VM, fromValue, bytecodeToString } from 'reefvm'
|
import { VM, fromValue, bytecodeToString } from 'reefvm'
|
||||||
import { readFileSync, writeFileSync, mkdirSync } from 'fs'
|
import { readFileSync, writeFileSync, mkdirSync } from 'fs'
|
||||||
import { randomUUID } from "crypto"
|
import { randomUUID } from "crypto"
|
||||||
|
|
@ -12,7 +12,7 @@ async function runFile(filePath: string) {
|
||||||
try {
|
try {
|
||||||
const code = readFileSync(filePath, 'utf-8')
|
const code = readFileSync(filePath, 'utf-8')
|
||||||
const compiler = new Compiler(code)
|
const compiler = new Compiler(code)
|
||||||
const vm = new VM(compiler.bytecode, globalFunctions)
|
const vm = new VM(compiler.bytecode, nativeFunctions, valueFunctions)
|
||||||
await vm.run()
|
await vm.run()
|
||||||
return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]) : null
|
return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]) : null
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"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"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reefvm": "workspace:*",
|
"reefvm": "workspace:*",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,6 @@
|
||||||
// The prelude creates all the builtin Shrimp functions.
|
// The prelude creates all the builtin Shrimp functions.
|
||||||
|
|
||||||
import { resolve, parse } from 'path'
|
import { toValue, type Value, extractParamInfo, isWrapped, getOriginalFunction } from 'reefvm'
|
||||||
import { readFileSync } from 'fs'
|
|
||||||
import { Compiler } from '#compiler/compiler'
|
|
||||||
import {
|
|
||||||
VM, Scope, toValue, type Value,
|
|
||||||
extractParamInfo, isWrapped, getOriginalFunction,
|
|
||||||
} from 'reefvm'
|
|
||||||
|
|
||||||
export const colors = {
|
export const colors = {
|
||||||
reset: '\x1b[0m',
|
reset: '\x1b[0m',
|
||||||
|
|
@ -21,28 +15,40 @@ export const colors = {
|
||||||
pink: '\x1b[38;2;255;105;180m'
|
pink: '\x1b[38;2;255;105;180m'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const globalFunctions = {
|
export const valueFunctions = {
|
||||||
// hello
|
echo: (...args: Value[]) => {
|
||||||
echo: (...args: any[]) => {
|
console.log(...args.map(a => formatValue(a)))
|
||||||
console.log(...args.map(a => {
|
|
||||||
const v = toValue(a)
|
|
||||||
return ['array', 'dict'].includes(v.type) ? formatValue(v, true) : v.value
|
|
||||||
}))
|
|
||||||
return toValue(null)
|
return toValue(null)
|
||||||
},
|
},
|
||||||
|
|
||||||
// info
|
length: (value: Value) => {
|
||||||
type: (v: any) => toValue(v).type,
|
|
||||||
inspect: (v: any) => formatValue(toValue(v)),
|
|
||||||
length: (v: any) => {
|
|
||||||
const value = toValue(v)
|
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
case 'string': case 'array': return value.value.length
|
case 'string': case 'array':
|
||||||
case 'dict': return value.value.size
|
return toValue(value.value.length)
|
||||||
default: return 0
|
case 'dict':
|
||||||
|
return toValue(Object.keys(value.value).length)
|
||||||
|
default:
|
||||||
|
return toValue(0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
type: (value: Value) => toValue(value.type),
|
||||||
|
inspect: (value: Value) => toValue(formatValue(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const nativeFunctions = {
|
||||||
|
range: (start: number, end: number | null) => {
|
||||||
|
if (end === null) {
|
||||||
|
end = start
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
const result: number[] = []
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
|
||||||
// strings
|
// strings
|
||||||
join: (arr: any[], sep: string = ',') => arr.join(sep),
|
join: (arr: any[], sep: string = ',') => arr.join(sep),
|
||||||
split: (str: string, sep: string = ',') => str.split(sep),
|
split: (str: string, sep: string = ',') => str.split(sep),
|
||||||
|
|
@ -55,17 +61,6 @@ export const globalFunctions = {
|
||||||
list: (...args: any[]) => args,
|
list: (...args: any[]) => args,
|
||||||
dict: (atNamed = {}) => atNamed,
|
dict: (atNamed = {}) => atNamed,
|
||||||
slice: (list: any[], start: number, end?: number) => list.slice(start, end),
|
slice: (list: any[], start: number, end?: number) => list.slice(start, end),
|
||||||
range: (start: number, end: number | null) => {
|
|
||||||
if (end === null) {
|
|
||||||
end = start
|
|
||||||
start = 0
|
|
||||||
}
|
|
||||||
const result: number[] = []
|
|
||||||
for (let i = start; i <= end; i++) {
|
|
||||||
result.push(i)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
|
|
||||||
// enumerables
|
// enumerables
|
||||||
map: async (list: any[], cb: Function) => {
|
map: async (list: any[], cb: Function) => {
|
||||||
|
|
@ -76,32 +71,6 @@ export const globalFunctions = {
|
||||||
each: async (list: any[], cb: Function) => {
|
each: async (list: any[], cb: Function) => {
|
||||||
for (const value of list) await cb(value)
|
for (const value of list) await cb(value)
|
||||||
},
|
},
|
||||||
|
|
||||||
// modules
|
|
||||||
use: async function (this: VM, path: string) {
|
|
||||||
const scope = this.scope
|
|
||||||
const pc = this.pc
|
|
||||||
|
|
||||||
const fullPath = resolve(path) + '.sh'
|
|
||||||
const code = readFileSync(fullPath, 'utf-8')
|
|
||||||
|
|
||||||
this.pc = this.instructions.length
|
|
||||||
this.scope = new Scope(scope)
|
|
||||||
const compiled = new Compiler(code)
|
|
||||||
this.appendBytecode(compiled.bytecode)
|
|
||||||
|
|
||||||
await this.continue()
|
|
||||||
|
|
||||||
const module: Map<string, Value> = new Map
|
|
||||||
for (const [name, value] of this.scope.locals.entries())
|
|
||||||
module.set(name, value)
|
|
||||||
|
|
||||||
this.scope = scope
|
|
||||||
this.pc = pc
|
|
||||||
this.stopped = false
|
|
||||||
|
|
||||||
this.scope.set(parse(fullPath).name, { type: 'dict', value: module })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatValue(value: Value, inner = false): string {
|
export function formatValue(value: Value, inner = false): string {
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { expect, describe, test } from 'bun:test'
|
|
||||||
import { globalFunctions } from '#prelude'
|
|
||||||
|
|
||||||
describe('use', () => {
|
|
||||||
test(`imports all a file's functions`, async () => {
|
|
||||||
expect(`
|
|
||||||
use ./src/prelude/tests/math
|
|
||||||
dbl = math | at double
|
|
||||||
dbl 4
|
|
||||||
`).toEvaluateTo(8, globalFunctions)
|
|
||||||
|
|
||||||
expect(`
|
|
||||||
use ./src/prelude/tests/math
|
|
||||||
math | at pi
|
|
||||||
`).toEvaluateTo(3.14, globalFunctions)
|
|
||||||
|
|
||||||
expect(`
|
|
||||||
use ./src/prelude/tests/math
|
|
||||||
math | at 🥧
|
|
||||||
`).toEvaluateTo(3.14159265359, globalFunctions)
|
|
||||||
|
|
||||||
expect(`
|
|
||||||
use ./src/prelude/tests/math
|
|
||||||
call = do x y: x y end
|
|
||||||
call (math | at add1) 5
|
|
||||||
`).toEvaluateTo(6, globalFunctions)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { parser } from '#parser/shrimp'
|
||||||
import { $ } from 'bun'
|
import { $ } from 'bun'
|
||||||
import { assert, errorMessage } from '#utils/utils'
|
import { assert, errorMessage } from '#utils/utils'
|
||||||
import { Compiler } from '#compiler/compiler'
|
import { Compiler } from '#compiler/compiler'
|
||||||
import { run, VM, type TypeScriptFunction } from 'reefvm'
|
import { run, VM } from 'reefvm'
|
||||||
import { treeToString, VMResultToValue } from '#utils/tree'
|
import { treeToString, VMResultToValue } from '#utils/tree'
|
||||||
|
|
||||||
const regenerateParser = async () => {
|
const regenerateParser = async () => {
|
||||||
|
|
@ -33,7 +33,7 @@ declare module 'bun:test' {
|
||||||
toMatchTree(expected: string): T
|
toMatchTree(expected: string): T
|
||||||
toMatchExpression(expected: string): T
|
toMatchExpression(expected: string): T
|
||||||
toFailParse(): T
|
toFailParse(): T
|
||||||
toEvaluateTo(expected: unknown, globalFunctions?: Record<string, TypeScriptFunction>): Promise<T>
|
toEvaluateTo(expected: unknown, nativeFunctions?: Record<string, Function>): Promise<T>
|
||||||
toFailEvaluation(): Promise<T>
|
toFailEvaluation(): Promise<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,13 +96,13 @@ expect.extend({
|
||||||
async toEvaluateTo(
|
async toEvaluateTo(
|
||||||
received: unknown,
|
received: unknown,
|
||||||
expected: unknown,
|
expected: unknown,
|
||||||
globalFunctions: Record<string, TypeScriptFunction> = {}
|
nativeFunctions: Record<string, Function> = {}
|
||||||
) {
|
) {
|
||||||
assert(typeof received === 'string', 'toEvaluateTo can only be used with string values')
|
assert(typeof received === 'string', 'toEvaluateTo can only be used with string values')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const compiler = new Compiler(received)
|
const compiler = new Compiler(received)
|
||||||
const result = await run(compiler.bytecode, globalFunctions)
|
const result = await run(compiler.bytecode, nativeFunctions)
|
||||||
let value = VMResultToValue(result)
|
let value = VMResultToValue(result)
|
||||||
|
|
||||||
// Just treat regex as strings for comparison purposes
|
// Just treat regex as strings for comparison purposes
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user