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.

whisp(lisp) demo

+462 -23
+1 -5
examples/demo/whisp/index.ts examples/whisp/tests/fib.wsp
··· 1 - import whisp from './whisp'; 2 - 3 - whisp` 4 - (fn fib n a b 1 + (fn fib (n a b) 5 2 (if (= n 0) a 6 3 (fib (- n 1) b (+ a b)))) 7 4 ··· 12 9 13 10 (write "fibonacci(30) = " result "\n") 14 11 (write "Time: " (. elapsed toFixed 4) " ms (" (. (* elapsed 1000) toFixed 2) " ยตs)\n")) 15 - `;
examples/demo/whisp/meta.ts

This is a binary file and will not be displayed.

+14
examples/demo/whisp/parser.ts examples/whisp/parser.ts
··· 11 11 } 12 12 13 13 const openParen = (ctx: ParseContext): void => { 14 + if (!ctx.head) { 15 + const around = ctx.source.substring(Math.max(0, ctx.i - 20), ctx.i + 20); 16 + throw new Error(`Unexpected '(' at position ${ctx.i} (stack empty): ...${around}...`); 17 + } 14 18 const temp: Types.Expr[] = []; 15 19 ctx.head.push(temp); 16 20 ctx.stack.push(ctx.head); ··· 19 23 20 24 const closeParen = (ctx: ParseContext): void => { 21 25 flushToken(ctx); 26 + if (ctx.stack.length === 0) { 27 + const around = ctx.source.substring(Math.max(0, ctx.i - 20), ctx.i + 20); 28 + throw new Error(`Unmatched ')' at position ${ctx.i}: ...${around}...`); 29 + } 22 30 ctx.head = ctx.stack.pop()!; 23 31 }; 24 32 ··· 45 53 ctx.head.push(Types.Leaf.string(str)); 46 54 }; 47 55 56 + const skipComment = (ctx: ParseContext): void => { 57 + flushToken(ctx); 58 + while (ctx.i < ctx.source.length && ctx.source[ctx.i] !== '\n') ctx.i++; 59 + }; 60 + 48 61 const tokenTable: Record<string, TokenHandler> = { 49 62 '"': parseString, 50 63 '(': openParen, 51 64 ')': closeParen, 65 + ';': skipComment, 52 66 ' ': flushToken, 53 67 '\n': flushToken, 54 68 '\r': flushToken,
examples/demo/whisp/types.ts examples/whisp/types.ts
+56 -8
examples/demo/whisp/whisp.ts examples/whisp/whisp.ts
··· 141 141 } 142 142 143 143 function l_loop(args: Types.Expr[], env: Types.Env): number { 144 - while (evaluate(args[0], env) === TRUE) evaluate(args[1], env); 144 + while (evaluate(args[0], env)) evaluate(args[1], env); 145 145 return -1; 146 146 } 147 147 148 148 function l_set(args: Types.Expr[], env: Types.Env): Types.LispValue[] { 149 149 const array = Eval.arr(args[0], env); 150 150 array[Eval.num(args[1], env)] = evaluate(args[2], env); 151 + return array; 152 + } 153 + 154 + function l_push(args: Types.Expr[], env: Types.Env): Types.LispValue[] { 155 + const array = Eval.arr(args[0], env); 156 + array.push(evaluate(args[1], env)); 151 157 return array; 152 158 } 153 159 ··· 162 168 return FALSE; 163 169 } 164 170 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))); 171 + function l_neq(args: Types.Expr[], env: Types.Env): number { 172 + return +(evaluate(args[0], env) !== evaluate(args[1], env)); 169 173 } 170 174 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)); 175 + function l_concat(args: Types.Expr[], env: Types.Env): string { 176 + return args.map(a => String(evaluate(a, env))).join(''); 174 177 } 175 178 176 179 function l_resolve(name: string, env: Types.Env) { ··· 179 182 return { target: env, value: env[name] }; 180 183 } 181 184 185 + function l_dot(args: Types.Expr[], env: Types.Env): Types.LispValue { 186 + const obj: any = evaluate(args[0], env); 187 + const member = String(Eval.name(args[1])); 188 + const val = obj[member]; 189 + if (typeof val === 'function') return val.call(obj, ...args.slice(2).map(a => evaluate(a, env))); 190 + return val as Types.LispValue; 191 + } 192 + 193 + function l_mut(args: Types.Expr[], env: Types.Env): Types.LispValue { 194 + const name = String(Eval.name(args[0])); 195 + const val = evaluate(args[1], env); 196 + let scope: any = env; 197 + while (scope && !Object.prototype.hasOwnProperty.call(scope, name)) { 198 + scope = Object.getPrototypeOf(scope); 199 + } 200 + if (scope) scope[name] = val; 201 + else env[name] = val; 202 + return val; 203 + } 204 + 205 + function l_cond(args: Types.Expr[], env: Types.Env): Types.LispValue { 206 + for (let i = 0; i < args.length - 1; i += 2) { 207 + if (evaluate(args[i], env)) return evaluate(args[i + 1], env); 208 + } 209 + if (args.length % 2 === 1) return evaluate(args.at(-1)!, env); 210 + return FALSE; 211 + } 212 + 182 213 function l_lambda(args: Types.Expr[], env: Types.Env): Types.LispFn { 183 214 const params = args.slice(0, -1); 184 215 return (props: Types.Expr[] = [], scope: Types.Env) => { ··· 188 219 }; 189 220 } 190 221 222 + function l_fn(args: Types.Expr[], env: Types.Env): Types.LispValue { 223 + const name = Eval.name(args[0]); 224 + const paramList = args[1] as Types.Expr[]; 225 + const bodyExprs = args.slice(2); 226 + const fn: Types.LispFn = (props: Types.Expr[] = [], scope: Types.Env) => { 227 + const localEnv: Types.Env = Object.create(env); 228 + paramList.forEach((param, i) => (localEnv[Eval.name(param)] = evaluate(props[i], scope))); 229 + return bodyExprs.reduce<Types.LispValue>((_, x) => evaluate(x, localEnv), 0); 230 + }; 231 + return (env[name] = fn); 232 + } 233 + 191 234 function jsResolve(path: string): { target: any; value: any } { 192 235 const parts = path.split('.'); 193 236 let target: any = globalThis; ··· 225 268 ['atom?']: l_is_atom, 226 269 ['lambda?']: l_is_lambda, 227 270 ['loop']: l_loop, 271 + ['!=']: l_neq, 228 272 ['set!']: l_set, 273 + ['push!']: l_push, 229 274 ['pop!']: l_pop, 275 + ['mut!']: l_mut, 230 276 ['write']: l_write, 277 + ['concat']: l_concat, 278 + ['cond']: l_cond, 231 279 ['.']: l_dot, 232 280 ['lambda']: l_lambda, 233 281 ['fn']: l_fn
+8
examples/whisp/index.ts
··· 1 + import whisp from './whisp'; 2 + import { readFile } from 'fs/promises'; 3 + 4 + const file = process.argv[2]; 5 + whisp`(write "running ${file}:\n\n"`; 6 + 7 + const source = await readFile(file, 'utf8'); 8 + whisp(source);
+339
examples/whisp/tests/bunny.wsp
··· 1 + ;; === opcodes === 2 + (let CONST 0) 3 + (let LOAD 1) 4 + (let ADD 2) 5 + (let CALL 3) 6 + (let RETURN 4) 7 + (let EXTERN 5) 8 + (let HALT 6) 9 + 10 + ;; === token types === 11 + (let T_EOF 0) 12 + (let T_FN 1) 13 + (let T_RET 2) 14 + (let T_IDENT 3) 15 + (let T_NUM 4) 16 + (let T_STR 5) 17 + (let T_LPAREN 6) 18 + (let T_RPAREN 7) 19 + (let T_LBRACE 8) 20 + (let T_RBRACE 9) 21 + (let T_COMMA 10) 22 + (let T_SEMI 11) 23 + (let T_PLUS 12) 24 + (let T_DOT 13) 25 + 26 + ;; === vm state === 27 + (let code (array)) 28 + (let stack (array)) 29 + (let callstack (array)) 30 + (let functions (array)) 31 + (let output (array)) 32 + (let entry 0) 33 + 34 + ;; === extern table === 35 + (let externs (array 36 + (array "std.io.println" 1 0) 37 + (array "bunny.squeak" 0 1))) 38 + 39 + ;; === lexer state === 40 + (let src "") 41 + (let pos 0) 42 + (let cur_type 0) 43 + (let cur_val "") 44 + (let cur_num 0) 45 + 46 + (fn is_alpha (c) 47 + (let cc (. c charCodeAt 0)) 48 + (or (and (>= cc 65) (<= cc 90)) 49 + (and (>= cc 97) (<= cc 122)))) 50 + 51 + (fn is_digit (c) 52 + (let cc (. c charCodeAt 0)) 53 + (and (>= cc 48) (<= cc 57))) 54 + 55 + (fn is_alnum (c) 56 + (or (is_alpha c) (is_digit c))) 57 + 58 + (fn is_space (c) 59 + (or (= c " ") (or (= c "\n") (or (= c "\r") (= c "\t"))))) 60 + 61 + (fn char_at (i) 62 + (if (< i (. src length)) (. src charAt i) "")) 63 + 64 + (fn skip_ws () 65 + (loop (and (< pos (. src length)) (is_space (char_at pos))) 66 + (mut! pos (+ pos 1)))) 67 + 68 + (fn next_token () 69 + (skip_ws) 70 + (if (>= pos (. src length)) 71 + (do (mut! cur_type T_EOF) (mut! cur_val "") 0) 72 + (do 73 + (let c (char_at pos)) 74 + (cond 75 + (is_alpha c) (do 76 + (let start pos) 77 + (loop (and (< pos (. src length)) (or (is_alnum (char_at pos)) (= (char_at pos) "_"))) 78 + (mut! pos (+ pos 1))) 79 + (let word (. src slice start pos)) 80 + (cond 81 + (= word "fn") (do (mut! cur_type T_FN) (mut! cur_val word)) 82 + (= word "return") (do (mut! cur_type T_RET) (mut! cur_val word)) 83 + (do (mut! cur_type T_IDENT) (mut! cur_val word)))) 84 + 85 + (is_digit c) (do 86 + (let start pos) 87 + (loop (and (< pos (. src length)) (is_digit (char_at pos))) 88 + (mut! pos (+ pos 1))) 89 + (mut! cur_type T_NUM) 90 + (mut! cur_num (Number.parseInt (. src slice start pos))) 91 + (mut! cur_val "")) 92 + 93 + (= c "\"") (do 94 + (mut! pos (+ pos 1)) 95 + (let start pos) 96 + (loop (and (< pos (. src length)) (!= (char_at pos) "\"")) 97 + (mut! pos (+ pos 1))) 98 + (mut! cur_type T_STR) 99 + (mut! cur_val (. src slice start pos)) 100 + (mut! pos (+ pos 1))) 101 + 102 + (do 103 + (mut! pos (+ pos 1)) 104 + (cond 105 + (= c "(") (mut! cur_type T_LPAREN) 106 + (= c ")") (mut! cur_type T_RPAREN) 107 + (= c "{") (mut! cur_type T_LBRACE) 108 + (= c "}") (mut! cur_type T_RBRACE) 109 + (= c ",") (mut! cur_type T_COMMA) 110 + (= c ";") (mut! cur_type T_SEMI) 111 + (= c "+") (mut! cur_type T_PLUS) 112 + (= c ".") (mut! cur_type T_DOT) 113 + (mut! cur_type T_EOF)) 114 + (mut! cur_val "")))))) 115 + 116 + (fn advance () (next_token)) 117 + 118 + (fn emit (op operand) 119 + (push! code (array op operand))) 120 + 121 + (fn find_function (name) 122 + (let i 0) 123 + (let result -1) 124 + (loop (and (< i (. functions length)) (= result -1)) 125 + (do 126 + (if (= (get (get functions i) 0) name) 127 + (mut! result i) 0) 128 + (mut! i (+ i 1)))) 129 + result) 130 + 131 + (fn find_extern (name) 132 + (let i 0) 133 + (let result -1) 134 + (loop (and (< i (. externs length)) (= result -1)) 135 + (do 136 + (if (= (get (get externs i) 0) name) 137 + (mut! result i) 0) 138 + (mut! i (+ i 1)))) 139 + result) 140 + 141 + (fn find_param (func name) 142 + (if (= func -1) -1 143 + (do 144 + (let f (get functions func)) 145 + (let params (get f 2)) 146 + (let nparams (get f 1)) 147 + (let i 0) 148 + (let result -1) 149 + (loop (and (< i nparams) (= result -1)) 150 + (do 151 + (if (= (get params i) name) 152 + (mut! result (- nparams (+ i 1))) 0) 153 + (mut! i (+ i 1)))) 154 + result))) 155 + 156 + (fn parse_qname () 157 + (if (!= cur_type T_IDENT) "" 158 + (do 159 + (let name cur_val) 160 + (advance) 161 + (loop (= cur_type T_DOT) 162 + (do 163 + (advance) 164 + (if (= cur_type T_IDENT) 165 + (do (mut! name (concat name "." cur_val)) 166 + (advance)) 0))) 167 + name))) 168 + 169 + (fn parse_expr (func) 170 + (cond 171 + (= cur_type T_NUM) (do 172 + (emit CONST cur_num) 173 + (advance)) 174 + 175 + (= cur_type T_STR) (do 176 + (emit CONST cur_val) 177 + (advance)) 178 + 179 + (= cur_type T_IDENT) (do 180 + (let name (parse_qname)) 181 + (if (= cur_type T_LPAREN) 182 + (parse_call func name) 183 + (do 184 + (let offset (find_param func name)) 185 + (emit LOAD offset))))) 186 + 187 + (if (= cur_type T_PLUS) 188 + (do (advance) (parse_expr func) (emit ADD 0)) 0)) 189 + 190 + (fn parse_call (func name) 191 + (advance) 192 + (if (!= cur_type T_RPAREN) 193 + (do 194 + (parse_expr func) 195 + (loop (= cur_type T_COMMA) 196 + (do (advance) (parse_expr func)))) 0) 197 + (advance) 198 + 199 + (let ext (find_extern name)) 200 + (if (!= ext -1) 201 + (emit EXTERN ext) 202 + (emit CALL (find_function name)))) 203 + 204 + (fn parse_function () 205 + (advance) 206 + (let fname cur_val) 207 + (advance) 208 + (advance) 209 + 210 + (let params (array)) 211 + (if (= cur_type T_IDENT) 212 + (do 213 + (push! params cur_val) 214 + (advance) 215 + (loop (= cur_type T_COMMA) 216 + (do (advance) 217 + (push! params cur_val) 218 + (advance)))) 0) 219 + (advance) 220 + (advance) 221 + 222 + (let fidx (. functions length)) 223 + (push! functions (array fname (. params length) params (. code length))) 224 + 225 + (loop (!= cur_type T_RBRACE) 226 + (if (= cur_type T_RET) 227 + (do 228 + (advance) 229 + (parse_expr fidx) 230 + (emit RETURN 0) 231 + (advance)) 0)) 232 + (advance)) 233 + 234 + (fn parse_program () 235 + (loop (= cur_type T_FN) 236 + (parse_function)) 237 + 238 + (mut! entry (. code length)) 239 + 240 + (loop (!= cur_type T_EOF) 241 + (if (= cur_type T_IDENT) 242 + (do 243 + (let name (parse_qname)) 244 + (if (= cur_type T_LPAREN) 245 + (do (parse_call -1 name) 246 + (if (= cur_type T_SEMI) (advance) 0)) 0)) 247 + (advance))) 248 + 249 + (emit HALT 0)) 250 + 251 + (fn vm_run () 252 + (let pc entry) 253 + (let fp 0) 254 + 255 + (loop (< pc (. code length)) 256 + (do 257 + (let instr (get code pc)) 258 + (let op (get instr 0)) 259 + (let operand (get instr 1)) 260 + 261 + (cond 262 + (= op CONST) (do 263 + (push! stack operand) 264 + (mut! pc (+ pc 1))) 265 + 266 + (= op LOAD) (do 267 + (push! stack (get stack (+ fp operand))) 268 + (mut! pc (+ pc 1))) 269 + 270 + (= op ADD) (do 271 + (let b (. stack pop)) 272 + (let a (. stack pop)) 273 + (push! stack (+ a b)) 274 + (mut! pc (+ pc 1))) 275 + 276 + (= op CALL) (do 277 + (let f (get functions operand)) 278 + (push! callstack (+ pc 1)) 279 + (push! callstack fp) 280 + (mut! fp (- (. stack length) (get f 1))) 281 + (mut! pc (get f 3))) 282 + 283 + (= op RETURN) (do 284 + (let ret (. stack pop)) 285 + (mut! stack (. stack slice 0 fp)) 286 + (mut! fp (. callstack pop)) 287 + (mut! pc (. callstack pop)) 288 + (push! stack ret)) 289 + 290 + (= op EXTERN) (do 291 + (let ext (get externs operand)) 292 + (let eid (get ext 2)) 293 + (cond 294 + (= eid 0) (do 295 + (let val (. stack pop)) 296 + (push! output (concat val)) 297 + (push! stack 0)) 298 + (= eid 1) (do 299 + (push! output "squeak") 300 + (push! stack 0))) 301 + (mut! pc (+ pc 1))) 302 + 303 + (= op HALT) 304 + (mut! pc (. code length)))))) 305 + 306 + (fn print_bytecode () 307 + (let names (array "CONST" "LOAD" "ADD" "CALL" "RETURN" "EXTERN" "HALT")) 308 + (write "bytecode:\n") 309 + (let i 0) 310 + (loop (< i (. code length)) 311 + (do 312 + (let instr (get code i)) 313 + (write " " i ": " (get names (get instr 0)) " " (get instr 1) "\n") 314 + (mut! i (+ i 1))))) 315 + 316 + (let source "fn add(a, b) { 317 + return a + b; 318 + } 319 + bunny.squeak(); 320 + std.io.println(add(5, 10)); 321 + std.io.println(\"hello world\");") 322 + 323 + (write "Source:\n\n" source "\n\n") 324 + (write (concat (. "=" repeat 40) "\n\n")) 325 + 326 + (mut! src source) 327 + (mut! pos 0) 328 + (next_token) 329 + (parse_program) 330 + (print_bytecode) 331 + 332 + (write "\nOutput:\n") 333 + (vm_run) 334 + 335 + (let i 0) 336 + (loop (< i (. output length)) 337 + (do 338 + (write (get output i) "\n") 339 + (mut! i (+ i 1))))
+1 -1
include/silver/ast.h
··· 78 78 79 79 typedef enum { 80 80 SV_VAR_VAR, 81 - SV_VAR_LET_ASN, 81 + SV_VAR_LET, 82 82 SV_VAR_CONST, 83 83 } sv_var_kind_t; 84 84
+13
src/ant.c
··· 12664 12664 *out = proxy_get(js, obj, key, key_len); 12665 12665 return true; 12666 12666 } 12667 + 12668 + if (t == T_STR || t == T_NUM || t == T_BOOL) { 12669 + if (t == T_STR && key_len == 6 && memcmp(key, "length", 6) == 0) { 12670 + jsoff_t byte_len = 0; jsoff_t str_off = vstr(js, obj, &byte_len); 12671 + const char *str_data = (const char *)&js->mem[str_off]; 12672 + *out = tov((double)utf16_strlen(str_data, byte_len)); 12673 + return true; 12674 + } 12675 + jsval_t boxed = mkobj(js, 0); 12676 + js_set_slot(js, js_as_obj(boxed), SLOT_PRIMITIVE, obj); 12677 + obj = boxed; t = T_OBJ; 12678 + } 12679 + 12667 12680 if (is_promise) obj = js_as_obj(obj); 12668 12681 else if (t != T_OBJ) return false; 12669 12682 jsoff_t off = lkp(js, obj, key, key_len);
+30 -9
src/types/modules/fs.d.ts
··· 9 9 isSymbolicLink(): boolean; 10 10 } 11 11 12 + type Encoding = 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'latin1' | 'binary' | 'base64' | 'base64url' | 'hex' | 'ascii'; 13 + 12 14 const constants: { 13 15 F_OK: number; 14 16 R_OK: number; 15 17 W_OK: number; 16 18 X_OK: number; 19 + O_RDONLY: number; 20 + O_WRONLY: number; 21 + O_RDWR: number; 22 + O_CREAT: number; 23 + O_EXCL: number; 24 + O_TRUNC: number; 25 + O_APPEND: number; 17 26 }; 18 27 const promises: typeof import('fs/promises'); 19 28 20 - function readFile(path: string): Promise<string>; 21 - function readFileSync(path: string): string; 29 + function readFile(path: string, encoding: Encoding): Promise<string>; 30 + function readFile(path: string): Promise<Uint8Array>; 31 + function readFileSync(path: string, encoding: Encoding | { encoding: Encoding }): string; 32 + function readFileSync(path: string): Uint8Array; 22 33 function readSync(fd: number, buffer: ArrayBufferView, offset?: number, length?: number, position?: number | null): number; 23 34 function stream(path: string): Promise<string>; 24 35 function open(path: string, flags?: string, mode?: number): Promise<number>; 25 36 function openSync(path: string, flags?: string, mode?: number): number; 26 37 function close(fd: number): Promise<void>; 27 38 function closeSync(fd: number): void; 28 - function writeFile(path: string, data: string): Promise<void>; 29 - function writeFileSync(path: string, data: string): void; 39 + function writeFile(path: string, data: string | ArrayBufferView): Promise<void>; 40 + function writeFileSync(path: string, data: string | ArrayBufferView): void; 30 41 function write(fd: number, data: string | ArrayBufferView, offset?: number, length?: number, position?: number | null): Promise<number>; 31 42 function writeSync(fd: number, data: string | ArrayBufferView, offset?: number, length?: number, position?: number | null): number; 32 43 function writev(fd: number, buffers: ArrayBufferView[], position?: number): Promise<number>; ··· 36 47 function renameSync(oldPath: string, newPath: string): void; 37 48 function unlink(path: string): Promise<void>; 38 49 function unlinkSync(path: string): void; 39 - function mkdir(path: string, options?: { recursive?: boolean }): Promise<void>; 40 - function mkdirSync(path: string, options?: { recursive?: boolean }): void; 50 + function mkdir(path: string, options?: { recursive?: boolean; mode?: number }): Promise<void>; 51 + function mkdirSync(path: string, options?: number | { recursive?: boolean; mode?: number }): void; 41 52 function rmdir(path: string): Promise<void>; 42 53 function rmdirSync(path: string): void; 43 54 function stat(path: string): Promise<Stats>; ··· 69 80 isSymbolicLink(): boolean; 70 81 } 71 82 83 + type Encoding = 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'latin1' | 'binary' | 'base64' | 'base64url' | 'hex' | 'ascii'; 84 + 72 85 const constants: { 73 86 F_OK: number; 74 87 R_OK: number; 75 88 W_OK: number; 76 89 X_OK: number; 90 + O_RDONLY: number; 91 + O_WRONLY: number; 92 + O_RDWR: number; 93 + O_CREAT: number; 94 + O_EXCL: number; 95 + O_TRUNC: number; 96 + O_APPEND: number; 77 97 }; 78 98 79 - function readFile(path: string): Promise<string>; 99 + function readFile(path: string, encoding: Encoding): Promise<string>; 100 + function readFile(path: string): Promise<Uint8Array>; 80 101 function open(path: string, flags?: string, mode?: number): Promise<number>; 81 102 function close(fd: number): Promise<void>; 82 - function writeFile(path: string, data: string): Promise<void>; 103 + function writeFile(path: string, data: string | ArrayBufferView): Promise<void>; 83 104 function write(fd: number, data: string | ArrayBufferView, offset?: number, length?: number, position?: number | null): Promise<number>; 84 105 function writev(fd: number, buffers: ArrayBufferView[], position?: number): Promise<number>; 85 106 function unlink(path: string): Promise<void>; 86 - function mkdir(path: string, options?: { recursive?: boolean }): Promise<void>; 107 + function mkdir(path: string, options?: { recursive?: boolean; mode?: number }): Promise<void>; 87 108 function rmdir(path: string): Promise<void>; 88 109 function stat(path: string): Promise<Stats>; 89 110 function exists(path: string): Promise<boolean>;