MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

lisp demo

+441 -7
+21
examples/demo/whisp/index.ts
··· 1 + import whisp from './whisp'; 2 + 3 + whisp`(write "running Whisp\n")`; 4 + 5 + whisp` 6 + (do 7 + (let n 30) 8 + (if (<= n 1) n 9 + (do 10 + (let a 0) 11 + (let b 1) 12 + (let i 2) 13 + (let temp 0) 14 + (loop (<= i n) 15 + (do 16 + (let temp (+ a b)) 17 + (let a b) 18 + (let b temp) 19 + (let i (+ i 1)))) 20 + (write "fib(30): " b "\n")))) 21 + `;
+76
examples/demo/whisp/parser.ts
··· 1 + import * as Types from './types'; 2 + 3 + type TokenHandler = (ctx: ParseContext) => void; 4 + 5 + interface ParseContext { 6 + source: string; 7 + i: number; 8 + head: Types.Expr[]; 9 + stack: Types.Expr[][]; 10 + acc: string; 11 + } 12 + 13 + const openParen = (ctx: ParseContext): void => { 14 + const temp: Types.Expr[] = []; 15 + ctx.head.push(temp); 16 + ctx.stack.push(ctx.head); 17 + ctx.head = temp; 18 + }; 19 + 20 + const closeParen = (ctx: ParseContext): void => { 21 + flushToken(ctx); 22 + ctx.head = ctx.stack.pop()!; 23 + }; 24 + 25 + const flushToken = (ctx: ParseContext): void => { 26 + const token = ctx.acc; 27 + ctx.acc = ''; 28 + if (!token) return; 29 + 30 + if (!ctx.head.length) ctx.head.push(Types.Leaf.apply(token)); 31 + else if (/^-?\d+(\.\d+)?$/.test(token)) ctx.head.push(Types.Leaf.atom(Number(token))); 32 + else ctx.head.push(Types.Leaf.word(token)); 33 + }; 34 + 35 + const parseString = (ctx: ParseContext): void => { 36 + let str = ''; 37 + const escapes: Record<string, string> = { n: '\n', t: '\t', '\\': '\\', '"': '"' }; 38 + 39 + while (++ctx.i < ctx.source.length && ctx.source[ctx.i] !== '"') { 40 + if (ctx.source[ctx.i] === '\\' && ctx.i + 1 < ctx.source.length) { 41 + const next = ctx.source[++ctx.i]; 42 + str += escapes[next] ?? '\\' + next; 43 + } else str += ctx.source[ctx.i]; 44 + } 45 + ctx.head.push(Types.Leaf.string(str)); 46 + }; 47 + 48 + const tokenTable: Record<string, TokenHandler> = { 49 + '"': parseString, 50 + '(': openParen, 51 + ')': closeParen, 52 + ' ': flushToken, 53 + '\n': flushToken, 54 + '\r': flushToken, 55 + '\t': flushToken 56 + }; 57 + 58 + export function parse(source: string): Types.Expr { 59 + const tree: Types.Expr[] = []; 60 + 61 + const ctx: ParseContext = { 62 + source, 63 + i: 0, 64 + head: tree, 65 + stack: [tree], 66 + acc: '' 67 + }; 68 + 69 + for (; ctx.i < source.length; ctx.i++) { 70 + const handler = tokenTable[source[ctx.i]]; 71 + if (handler) handler(ctx); 72 + else ctx.acc += source[ctx.i]; 73 + } 74 + 75 + return tree[0]; 76 + }
+20
examples/demo/whisp/types.ts
··· 1 + export enum LispNode { 2 + APPLY, 3 + WORD, 4 + ATOM, 5 + STRING 6 + } 7 + 8 + export type Env = Record<string, LispValue>; 9 + export type Leaf = [LispNode, string | number]; 10 + export type Expr = Leaf | Expr[]; 11 + 12 + export type LispValue = number | string | LispValue[] | LispFn; 13 + export type LispFn = (args: Expr[], env: Env) => LispValue; 14 + 15 + export const Leaf = { 16 + apply: (value: string): Leaf => [LispNode.APPLY, value], 17 + word: (value: string): Leaf => [LispNode.WORD, value], 18 + atom: (value: number): Leaf => [LispNode.ATOM, value], 19 + string: (value: string): Leaf => [LispNode.STRING, value] 20 + };
+223
examples/demo/whisp/whisp.ts
··· 1 + import { parse } from './parser'; 2 + import * as Types from './types'; 3 + 4 + const TRUE = 1; 5 + const FALSE = 0; 6 + 7 + const handlers: Record<Types.LispNode, (value: any, args: Types.Expr[], env: Types.Env) => Types.LispValue> = { 8 + [Types.LispNode.WORD]: (value, _, env) => env[value], 9 + [Types.LispNode.APPLY]: (value, args, env) => (env[value] as Types.LispFn)(args, env), 10 + [Types.LispNode.ATOM]: value => value, 11 + [Types.LispNode.STRING]: value => value 12 + }; 13 + 14 + const Eval = { 15 + name: (exp: Types.Expr) => { const [, value] = exp as Types.Leaf; return value; }, 16 + num: (exp: Types.Expr, env: Types.Env): number => evaluate(exp, env) as number, 17 + str: (exp: Types.Expr, env: Types.Env): string => evaluate(exp, env) as string, 18 + arr: (exp: Types.Expr, env: Types.Env): Types.LispValue[] => evaluate(exp, env) as Types.LispValue[], 19 + fn: (exp: Types.Expr, env: Types.Env): Types.LispFn => evaluate(exp, env) as Types.LispFn 20 + }; 21 + 22 + function isLeaf(exp: Types.Expr): exp is Types.Leaf { 23 + if (!Array.isArray(exp)) return false; 24 + return typeof exp[0] === 'number' && exp[0] >= Types.LispNode.APPLY && exp[0] <= Types.LispNode.STRING; 25 + } 26 + 27 + function l_mod(args: Types.Expr[], env: Types.Env): number { 28 + return Eval.num(args[0], env) % Eval.num(args[1], env); 29 + } 30 + 31 + function l_div(args: Types.Expr[], env: Types.Env): number { 32 + return Eval.num(args[0], env) / Eval.num(args[1], env); 33 + } 34 + 35 + function l_length(args: Types.Expr[], env: Types.Env): number { 36 + return (evaluate(args[0], env) as string | Types.LispValue[]).length; 37 + } 38 + 39 + function l_add(args: Types.Expr[], env: Types.Env): number { 40 + return Eval.num(args[0], env) + Eval.num(args[1], env); 41 + } 42 + 43 + function l_mul(args: Types.Expr[], env: Types.Env): number { 44 + return Eval.num(args[0], env) * Eval.num(args[1], env); 45 + } 46 + 47 + function l_sub(args: Types.Expr[], env: Types.Env): number { 48 + return Eval.num(args[0], env) - Eval.num(args[1], env); 49 + } 50 + 51 + function l_array(args: Types.Expr[], env: Types.Env): Types.LispValue[] { 52 + return args.length ? args.map(x => evaluate(x, env)) : []; 53 + } 54 + 55 + function l_get(args: Types.Expr[], env: Types.Env): Types.LispValue { 56 + return Eval.arr(args[0], env)[Eval.num(args[1], env)]; 57 + } 58 + 59 + function l_do(args: Types.Expr[], env: Types.Env): Types.LispValue { 60 + return args.reduce<Types.LispValue>((_, x) => evaluate(x, env), FALSE); 61 + } 62 + 63 + function l_not(args: Types.Expr[], env: Types.Env): number { 64 + return +!evaluate(args[0], env); 65 + } 66 + 67 + function l_eq(args: Types.Expr[], env: Types.Env): number { 68 + return +(evaluate(args[0], env) === evaluate(args[1], env)); 69 + } 70 + 71 + function l_lt(args: Types.Expr[], env: Types.Env): number { 72 + return +(evaluate(args[0], env) < evaluate(args[1], env)); 73 + } 74 + 75 + function l_gt(args: Types.Expr[], env: Types.Env): number { 76 + return +(evaluate(args[0], env) > evaluate(args[1], env)); 77 + } 78 + 79 + function l_gte(args: Types.Expr[], env: Types.Env): number { 80 + return +(evaluate(args[0], env) >= evaluate(args[1], env)); 81 + } 82 + 83 + function l_lte(args: Types.Expr[], env: Types.Env): number { 84 + return +(evaluate(args[0], env) <= evaluate(args[1], env)); 85 + } 86 + 87 + function l_and(args: Types.Expr[], env: Types.Env): Types.LispValue { 88 + return !evaluate(args[0], env) ? FALSE : evaluate(args[1], env); 89 + } 90 + 91 + function l_or(args: Types.Expr[], env: Types.Env): Types.LispValue { 92 + return evaluate(args[0], env) ? TRUE : evaluate(args[1], env); 93 + } 94 + 95 + function l_apply(args: Types.Expr[], env: Types.Env): Types.LispValue { 96 + return Eval.fn(args.pop()!, env)(args, env); 97 + } 98 + 99 + function l_let(args: Types.Expr[], env: Types.Env): Types.LispValue { 100 + return (env[Eval.name(args[0])] = evaluate(args[1], env)); 101 + } 102 + 103 + function l_bitand(args: Types.Expr[], env: Types.Env): number { 104 + return Eval.num(args[0], env) & Eval.num(args[1], env); 105 + } 106 + 107 + function l_bitnot(args: Types.Expr[], env: Types.Env): number { 108 + return ~Eval.num(args[0], env); 109 + } 110 + 111 + function l_bitor(args: Types.Expr[], env: Types.Env): number { 112 + return Eval.num(args[0], env) | Eval.num(args[1], env); 113 + } 114 + 115 + function l_bitxor(args: Types.Expr[], env: Types.Env): number { 116 + return Eval.num(args[0], env) ^ Eval.num(args[1], env); 117 + } 118 + 119 + function l_shl(args: Types.Expr[], env: Types.Env): number { 120 + return Eval.num(args[0], env) << Eval.num(args[1], env); 121 + } 122 + 123 + function l_shr(args: Types.Expr[], env: Types.Env): number { 124 + return Eval.num(args[0], env) >> Eval.num(args[1], env); 125 + } 126 + 127 + function l_if(args: Types.Expr[], env: Types.Env): Types.LispValue { 128 + return evaluate(args[0], env) ? evaluate(args[1], env) : evaluate(args[2], env); 129 + } 130 + 131 + function l_is_atom(args: Types.Expr[], env: Types.Env): number { 132 + return +(typeof evaluate(args[0], env) === 'number'); 133 + } 134 + 135 + function l_is_lambda(args: Types.Expr[], env: Types.Env): number { 136 + return +(typeof evaluate(args[0], env) === 'function'); 137 + } 138 + 139 + function l_loop(args: Types.Expr[], env: Types.Env): number { 140 + while (evaluate(args[0], env) === TRUE) evaluate(args[1], env); 141 + return -1; 142 + } 143 + 144 + function l_set(args: Types.Expr[], env: Types.Env): Types.LispValue[] { 145 + const array = Eval.arr(args[0], env); 146 + array[Eval.num(args[1], env)] = evaluate(args[2], env); 147 + return array; 148 + } 149 + 150 + function l_pop(args: Types.Expr[], env: Types.Env): Types.LispValue[] { 151 + const array = Eval.arr(args[0], env); 152 + array.pop(); 153 + return array; 154 + } 155 + 156 + function l_write(args: Types.Expr[], env: Types.Env): Types.LispValue { 157 + args.forEach(a => process.stdout.write(String(evaluate(a, env)))); 158 + return FALSE; 159 + } 160 + 161 + function l_lambda(args: Types.Expr[], env: Types.Env): Types.LispFn { 162 + const params = args.slice(0, -1); 163 + return (props: Types.Expr[] = [], scope: Types.Env) => { 164 + const localEnv: Types.Env = Object.create(env); 165 + props.forEach((prop, i) => (localEnv[Eval.name(params[i])] = evaluate(prop, scope))); 166 + return evaluate(args.at(-1)!, localEnv); 167 + }; 168 + } 169 + 170 + const keywords: Types.Env = { 171 + ['mod']: l_mod, 172 + ['length']: l_length, 173 + ['/']: l_div, 174 + ['+']: l_add, 175 + ['*']: l_mul, 176 + ['-']: l_sub, 177 + ['array']: l_array, 178 + ['get']: l_get, 179 + ['do']: l_do, 180 + ['not']: l_not, 181 + ['=']: l_eq, 182 + ['<']: l_lt, 183 + ['>']: l_gt, 184 + ['>=']: l_gte, 185 + ['<=']: l_lte, 186 + ['and']: l_and, 187 + ['or']: l_or, 188 + ['apply']: l_apply, 189 + ['let']: l_let, 190 + ['&']: l_bitand, 191 + ['~']: l_bitnot, 192 + ['|']: l_bitor, 193 + ['^']: l_bitxor, 194 + ['<<']: l_shl, 195 + ['>>']: l_shr, 196 + ['if']: l_if, 197 + ['atom?']: l_is_atom, 198 + ['lambda?']: l_is_lambda, 199 + ['loop']: l_loop, 200 + ['set!']: l_set, 201 + ['pop!']: l_pop, 202 + ['write']: l_write, 203 + ['lambda']: l_lambda 204 + }; 205 + 206 + function evaluate(exp: Types.Expr, env: Types.Env = keywords): Types.LispValue { 207 + if (!Array.isArray(exp) || exp.length === 0) return []; 208 + const [head, ...args] = isLeaf(exp) ? [exp] : (exp as [Types.Leaf, ...Types.Expr[]]); 209 + const [type, value] = head; 210 + return handlers[type]?.(value, args, env) ?? []; 211 + } 212 + 213 + export default function run(strings: TemplateStringsArray | string, ...values: unknown[]): Types.LispValue { 214 + if (typeof strings === 'string') { 215 + return evaluate(parse(`(apply (lambda (do ${strings})))`)); 216 + } 217 + 218 + const source = (strings as TemplateStringsArray).reduce((acc: string, str: string, i: number) => { 219 + return acc + str + (values[i] !== undefined ? values[i] : ''); 220 + }, ''); 221 + 222 + return evaluate(parse(`(apply (lambda (do ${source})))`)); 223 + }
+101 -6
src/types/process.d.ts
··· 1 1 type ProcessEnv = { 2 2 [key: string]: string | undefined; 3 - } & { toObject(): Record<string, string> }; 3 + } & { 4 + toObject(): Record<string, string>; 5 + toString(): string; 6 + }; 4 7 5 8 interface Features { 6 9 uv: boolean; ··· 9 12 } 10 13 11 14 interface Versions { 12 - node: string; 13 15 ant: string; 14 - v8: string; 15 16 uv: string; 16 - modules: string; 17 + } 18 + 19 + interface Release { 20 + name: string; 21 + } 22 + 23 + interface MemoryUsage { 24 + rss: number; 25 + heapTotal: number; 26 + heapUsed: number; 27 + external: number; 28 + arrayBuffers: number; 29 + } 30 + 31 + interface CpuUsage { 32 + user: number; 33 + system: number; 34 + } 35 + 36 + interface ReadStream { 37 + isTTY: boolean; 38 + setRawMode(enable?: boolean): boolean; 39 + resume(): this; 40 + pause(): this; 41 + on(event: string, listener: EventListener): this; 42 + off(event: string, listener: EventListener): this; 43 + removeListener(event: string, listener: EventListener): this; 44 + removeAllListeners(event?: string): this; 45 + } 46 + 47 + interface WriteStream { 48 + isTTY: boolean; 49 + rows: number; 50 + columns: number; 51 + write(data: string): boolean; 52 + on(event: string, listener: EventListener): this; 53 + once(event: string, listener: EventListener): this; 54 + off(event: string, listener: EventListener): this; 55 + removeListener(event: string, listener: EventListener): this; 56 + removeAllListeners(event?: string): this; 57 + getWindowSize(): [number, number]; 58 + } 59 + 60 + interface StderrStream extends Omit<WriteStream, 'rows' | 'columns' | 'getWindowSize'> {} 61 + 62 + interface HrTime { 63 + (time?: [number, number]): [number, number]; 64 + bigint(): bigint; 65 + } 66 + 67 + interface MemoryUsageFn { 68 + (): MemoryUsage; 69 + rss(): number; 17 70 } 18 71 19 72 interface Process { 20 73 env: ProcessEnv; 21 74 argv: string[]; 75 + execArgv: string[]; 76 + argv0: string; 77 + execPath: string; 22 78 pid: number; 79 + ppid: number; 23 80 platform: string; 24 81 arch: string; 82 + version: string; 83 + versions: Versions; 84 + release: Release; 85 + features: Features; 86 + 87 + stdin: ReadStream; 88 + stdout: WriteStream; 89 + stderr: StderrStream; 90 + 25 91 exit(code?: number): never; 92 + abort(): never; 26 93 cwd(): string; 94 + chdir(directory: string): void; 95 + uptime(): number; 96 + hrtime: HrTime; 97 + memoryUsage: MemoryUsageFn; 98 + cpuUsage(previousValue?: CpuUsage): CpuUsage; 99 + kill(pid: number, signal?: number | string): true; 100 + umask(mask?: number): number; 27 101 dlopen(module: { exports?: unknown }, filename: string): void; 28 - features: Features; 29 - versions: Versions; 102 + 103 + on(event: string, listener: EventListener): this; 104 + addListener(event: string, listener: EventListener): this; 105 + once(event: string, listener: EventListener): this; 106 + off(event: string, listener: EventListener): this; 107 + removeListener(event: string, listener: EventListener): this; 108 + removeAllListeners(event?: string): this; 109 + emit(event: string, ...args: unknown[]): boolean; 110 + listenerCount(event: string): number; 111 + setMaxListeners(n: number): this; 112 + getMaxListeners(): number; 113 + 114 + getuid(): number; 115 + geteuid(): number; 116 + getgid(): number; 117 + getegid(): number; 118 + getgroups(): number[]; 119 + setuid(id: number | string): void; 120 + setgid(id: number | string): void; 121 + seteuid(id: number | string): void; 122 + setegid(id: number | string): void; 123 + setgroups(groups: Array<number | string>): void; 124 + initgroups(user: string, extraGroup: number | string): void; 30 125 } 31 126 32 127 declare const process: Process;
-1
tsconfig.json
··· 3 3 "noEmit": true, 4 4 "strict": true, 5 5 "noImplicitAny": true, 6 - 7 6 "target": "ES2024", 8 7 "module": "ESNext", 9 8 "moduleResolution": "bundler",