|
1 | 1 | import { parseWithoutExpand } from './parser.js'; |
2 | 2 |
|
3 | | -import { readFile, readFileSync } from 'fs'; |
| 3 | +import { readFileSync, openSync, closeSync, writeSync } from 'fs'; |
4 | 4 | import URL from 'url'; |
5 | 5 |
|
6 | 6 | // Use JavaScript native Symbol instead of custom implementation |
@@ -241,6 +241,8 @@ macro_table.set(_let, letMacro); |
241 | 241 | // Global environment setup |
242 | 242 | export const globalEnv = new Env(); |
243 | 243 |
|
| 244 | +const EOF_OBJECT = Symbol.for('#<eof-object>'); |
| 245 | + |
244 | 246 | function addGlobals(env) { |
245 | 247 | // Add basic operations |
246 | 248 | env.set(Sym('+'), (a, b) => a + b); |
@@ -273,6 +275,30 @@ function addGlobals(env) { |
273 | 275 | env.set(Sym('hash-table-delete!'), (table, key) => { table.delete(key); }); |
274 | 276 | env.set(Sym('hash-table-keys'), table => Array.from(table.keys())); |
275 | 277 | env.set(Sym('hash-table-values'), table => Array.from(table.values())); |
| 278 | + |
| 279 | + // File operations |
| 280 | + function isIteratorLike(obj) { |
| 281 | + // Copied from https://github.com/ayonli/check-iterable/ |
| 282 | + return typeof obj === "object" |
| 283 | + && obj !== null |
| 284 | + && typeof obj.next === "function"; |
| 285 | + } |
| 286 | + env.set(Sym('open-output-file'), filename => openSync(filename, 'w')); |
| 287 | + env.set(Sym('open-input-file'), filename => readFileSync(filename, "utf-8")[Symbol.iterator]()); |
| 288 | + env.set(Sym('close-port'), (port) => { |
| 289 | + if (typeof port === "number") { |
| 290 | + closeSync(port); |
| 291 | + } |
| 292 | + }); |
| 293 | + env.set(Sym('read-char'), (port) => { |
| 294 | + const value = port.next(); |
| 295 | + return value.done ? EOF_OBJECT : value.value; |
| 296 | + }); |
| 297 | + env.set(Sym('write-char'), (char, fd) => { writeSync(fd, char); }); |
| 298 | + env.set(Sym('eof-object?'), object => object === EOF_OBJECT); |
| 299 | + env.set(Sym('port?'), port => isIteratorLike(port) || typeof port === "number"); |
| 300 | + env.set(Sym('input-port?'), port => isIteratorLike(port)); |
| 301 | + env.set(Sym('output-port?'), port => typeof port === "number"); |
276 | 302 |
|
277 | 303 | // Error handling |
278 | 304 | env.set(Sym('error'), msg => { throw new Error(msg); }); |
|
0 commit comments