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.

add meta functions

+50 -31
+11 -17
examples/demo/whisp/index.ts
··· 1 1 import whisp from './whisp'; 2 2 3 - whisp`(write "running Whisp\n")`; 4 - 5 3 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")))) 4 + (fn fib n a b 5 + (if (= n 0) a 6 + (fib (- n 1) b (+ a b)))) 7 + 8 + (let start (performance.now)) 9 + (let result (fib 30 0 1)) 10 + (let end (performance.now)) 11 + (let elapsed (- end start)) 12 + 13 + (write "fibonacci(30) = " result "\n") 14 + (write "Time: " (. elapsed toFixed 4) " ms (" (. (* elapsed 1000) toFixed 2) " ยตs)\n")) 21 15 `;
examples/demo/whisp/meta.ts

This is a binary file and will not be displayed.

+39 -14
examples/demo/whisp/whisp.ts
··· 5 5 const FALSE = 0; 6 6 7 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 8 [Types.LispNode.ATOM]: value => value, 11 - [Types.LispNode.STRING]: value => value 9 + [Types.LispNode.STRING]: value => value, 10 + [Types.LispNode.WORD]: (value, _, env) => l_resolve(value, env).value as Types.LispValue, 11 + 12 + [Types.LispNode.APPLY]: (value, args, env) => { 13 + const { target, value: fn } = l_resolve(value, env); 14 + return target === env ? (fn as Types.LispFn)(args, env) : fn.call(target, ...args.map(a => evaluate(a, env))); 15 + } 12 16 }; 13 17 14 18 const Eval = { 15 - name: (exp: Types.Expr) => { const [, value] = exp as Types.Leaf; return value; }, 19 + name: (exp: Types.Expr) => (exp as Types.Leaf)[1], 16 20 num: (exp: Types.Expr, env: Types.Env): number => evaluate(exp, env) as number, 17 21 str: (exp: Types.Expr, env: Types.Env): string => evaluate(exp, env) as string, 18 22 arr: (exp: Types.Expr, env: Types.Env): Types.LispValue[] => evaluate(exp, env) as Types.LispValue[], ··· 158 162 return FALSE; 159 163 } 160 164 165 + function l_dot(args: Types.Expr[], env: Types.Env): Types.LispValue { 166 + const obj: any = evaluate(args[0], env); 167 + const method = String(Eval.name(args[1])); 168 + return obj[method](...args.slice(2).map(a => evaluate(a, env))); 169 + } 170 + 171 + function l_fn(args: Types.Expr[], env: Types.Env): Types.LispValue { 172 + const name = Eval.name(args[0]); 173 + return (env[name] = l_lambda(args.slice(1), env)); 174 + } 175 + 176 + function l_resolve(name: string, env: Types.Env) { 177 + if (name in env) return { target: env, value: env[name] }; 178 + if (name.includes('.')) return jsResolve(name); 179 + return { target: env, value: env[name] }; 180 + } 181 + 161 182 function l_lambda(args: Types.Expr[], env: Types.Env): Types.LispFn { 162 183 const params = args.slice(0, -1); 163 184 return (props: Types.Expr[] = [], scope: Types.Env) => { ··· 167 188 }; 168 189 } 169 190 191 + function jsResolve(path: string): { target: any; value: any } { 192 + const parts = path.split('.'); 193 + let target: any = globalThis; 194 + parts.slice(0, -1).forEach(p => (target = target[p])); 195 + return { target, value: target[parts.at(-1)!] }; 196 + } 197 + 170 198 const keywords: Types.Env = { 171 199 ['mod']: l_mod, 172 200 ['length']: l_length, ··· 200 228 ['set!']: l_set, 201 229 ['pop!']: l_pop, 202 230 ['write']: l_write, 203 - ['lambda']: l_lambda 231 + ['.']: l_dot, 232 + ['lambda']: l_lambda, 233 + ['fn']: l_fn 204 234 }; 205 235 206 236 function evaluate(exp: Types.Expr, env: Types.Env = keywords): Types.LispValue { ··· 211 241 } 212 242 213 243 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})))`)); 244 + let source: string; 245 + if (typeof strings === 'string') source = strings; 246 + else source = (strings as TemplateStringsArray).reduce((acc, str, i) => acc + str + (values[i] ?? ''), ''); 247 + return evaluate(parse(`(do ${source})`), Object.create(keywords)); 223 248 }