123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745 |
- // -*- js2-basic-offset: 4 -*-
- class Char {
- constructor(codepoint) {
- this.codepoint = codepoint;
- }
- toString() {
- let ch = String.fromCodePoint(this.codepoint);
- if (ch.match(/[a-zA-Z0-9$[\]().]/)) return `#\\${ch}`;
- return `#\\x${this.codepoint.toString(16)}`;
- }
- }
- class Eof { toString() { return "#<eof>"; } }
- class Nil { toString() { return "#nil"; } }
- class Null { toString() { return "()"; } }
- class Unspecified { toString() { return "#<unspecified>"; } }
- class Complex {
- constructor(real, imag) {
- this.real = real;
- this.imag = imag;
- }
- toString() {
- const sign = this.imag >= 0 && Number.isFinite(this.imag) ? "+": "";
- return `${flonum_to_string(this.real)}${sign}${flonum_to_string(this.imag)}i`;
- }
- }
- class Fraction {
- constructor(num, denom) {
- this.num = num;
- this.denom = denom;
- }
- toString() {
- return `${this.num}/${this.denom}`;
- }
- }
- class HeapObject {
- constructor(reflector, obj) {
- this.reflector = reflector;
- this.obj = obj;
- }
- repr() { return this.toString(); } // Default implementation.
- }
- class Pair extends HeapObject {
- toString() { return "#<pair>"; }
- repr() {
- let car_repr = repr(this.reflector.car(this));
- let cdr_repr = repr(this.reflector.cdr(this));
- if (cdr_repr == '()')
- return `(${car_repr})`;
- if (cdr_repr.charAt(0) == '(')
- return `(${car_repr} ${cdr_repr.substring(1)}`;
- return `(${car_repr} . ${cdr_repr})`;
- }
- }
- class MutablePair extends Pair { toString() { return "#<mutable-pair>"; } }
- class Vector extends HeapObject {
- toString() { return "#<vector>"; }
- repr() {
- let len = this.reflector.vector_length(this);
- let out = '#(';
- for (let i = 0; i < len; i++) {
- if (i) out += ' ';
- out += repr(this.reflector.vector_ref(this, i));
- }
- out += ')';
- return out;
- }
- }
- class MutableVector extends Vector {
- toString() { return "#<mutable-vector>"; }
- }
- class Bytevector extends HeapObject {
- toString() { return "#<bytevector>"; }
- repr() {
- let len = this.reflector.bytevector_length(this);
- let out = '#vu8(';
- for (let i = 0; i < len; i++) {
- if (i) out += ' ';
- out += this.reflector.bytevector_ref(this, i);
- }
- out += ')';
- return out;
- }
- }
- class MutableBytevector extends Bytevector {
- toString() { return "#<mutable-bytevector>"; }
- }
- class Bitvector extends HeapObject {
- toString() { return "#<bitvector>"; }
- repr() {
- let len = this.reflector.bitvector_length(this);
- let out = '#*';
- for (let i = 0; i < len; i++) {
- out += this.reflector.bitvector_ref(this, i) ? '1' : '0';
- }
- return out;
- }
- }
- class MutableBitvector extends Bitvector {
- toString() { return "#<mutable-bitvector>"; }
- }
- class MutableString extends HeapObject {
- toString() { return "#<mutable-string>"; }
- repr() { return string_repr(this.reflector.string_value(this)); }
- }
- class Procedure extends HeapObject {
- toString() { return "#<procedure>"; }
- call(...arg) {
- return this.reflector.call(this, ...arg);
- }
- async call_async(...arg) {
- return await this.reflector.call_async(this, ...arg);
- }
- }
- class Sym extends HeapObject {
- toString() { return "#<symbol>"; }
- repr() { return this.reflector.symbol_name(this); }
- }
- class Keyword extends HeapObject {
- toString() { return "#<keyword>"; }
- repr() { return `#:${this.reflector.keyword_name(this)}`; }
- }
- class Variable extends HeapObject { toString() { return "#<variable>"; } }
- class AtomicBox extends HeapObject { toString() { return "#<atomic-box>"; } }
- class HashTable extends HeapObject { toString() { return "#<hash-table>"; } }
- class WeakTable extends HeapObject { toString() { return "#<weak-table>"; } }
- class Fluid extends HeapObject { toString() { return "#<fluid>"; } }
- class DynamicState extends HeapObject { toString() { return "#<dynamic-state>"; } }
- class Syntax extends HeapObject { toString() { return "#<syntax>"; } }
- class Port extends HeapObject { toString() { return "#<port>"; } }
- class Struct extends HeapObject { toString() { return "#<struct>"; } }
- function instantiate_streaming(path, imports) {
- if (typeof fetch !== 'undefined' && typeof window !== 'undefined')
- return WebAssembly.instantiateStreaming(fetch(path), imports);
- let bytes;
- if (typeof read !== 'undefined') {
- bytes = read(path, 'binary');
- } else if (typeof readFile !== 'undefined') {
- bytes = readFile(path);
- } else {
- let fs = require('fs');
- bytes = fs.readFileSync(path);
- }
- return WebAssembly.instantiate(bytes, imports);
- }
- class Scheme {
- #instance;
- #abi;
- constructor(instance, abi) {
- this.#instance = instance;
- this.#abi = abi;
- }
- static async reflect(abi, {reflect_wasm_dir = '.'}) {
- let debug = {
- debug_str(x) { console.log(`reflect debug: ${x}`); },
- debug_str_i32(x, y) { console.log(`reflect debug: ${x}: ${y}`); },
- debug_str_scm: (x, y) => {
- console.log(`reflect debug: ${x}: #<scm>`);
- },
- };
- let reflect_wasm = reflect_wasm_dir + '/reflect.wasm';
- let rt = {
- die(tag, data) { throw new SchemeTrapError(tag, data); },
- wtf8_to_string(wtf8) { return wtf8_to_string(wtf8); },
- string_to_wtf8(str) { return string_to_wtf8(str); },
- };
- let { module, instance } =
- await instantiate_streaming(reflect_wasm, { abi, debug, rt });
- return new Scheme(instance, abi);
- }
- #init_module(mod) {
- mod.set_debug_handler({
- debug_str(x) { console.log(`debug: ${x}`); },
- debug_str_i32(x, y) { console.log(`debug: ${x}: ${y}`); },
- debug_str_scm: (x, y) => {
- console.log(`debug: ${x}: ${repr(this.#to_js(y))}`);
- },
- });
- mod.set_ffi_handler({
- procedure_to_extern: (obj) => {
- const proc = this.#to_js(obj);
- return (...args) => {
- return proc.call(...args);
- };
- }
- });
- let proc = new Procedure(this, mod.get_export('$load').value);
- return proc.call();
- }
- static async load_main(path, opts = {}) {
- let mod = await SchemeModule.fetch_and_instantiate(path, opts);
- let reflect = await mod.reflect(opts);
- return reflect.#init_module(mod);
- }
- async load_extension(path, opts = {}) {
- opts = Object.assign({ abi: this.#abi }, opts);
- let mod = await SchemeModule.fetch_and_instantiate(path, opts);
- return this.#init_module(mod);
- }
- #to_scm(js) {
- let api = this.#instance.exports;
- if (typeof(js) == 'number') {
- return api.scm_from_f64(js);
- } else if (typeof(js) == 'bigint') {
- if (BigInt(api.scm_most_negative_fixnum()) <= js
- && js <= BigInt(api.scm_most_positive_fixnum()))
- return api.scm_from_fixnum(Number(js));
- return api.scm_from_bignum(js);
- } else if (typeof(js) == 'boolean') {
- return js ? api.scm_true() : api.scm_false();
- } else if (typeof(js) == 'string') {
- return api.scm_from_string(js);
- } else if (typeof(js) == 'object') {
- if (js instanceof Eof) return api.scm_eof();
- if (js instanceof Nil) return api.scm_nil();
- if (js instanceof Null) return api.scm_null();
- if (js instanceof Unspecified) return api.scm_unspecified();
- if (js instanceof Char) return api.scm_from_char(js.codepoint);
- if (js instanceof HeapObject) return js.obj;
- if (js instanceof Fraction)
- return api.scm_from_fraction(this.#to_scm(js.num),
- this.#to_scm(js.denom));
- if (js instanceof Complex)
- return api.scm_from_complex(js.real, js.imag);
- return api.scm_from_extern(js);
- } else if (typeof(js) == 'function') {
- return api.scm_from_extern(js);
- } else {
- throw new Error(`unexpected; ${typeof(js)}`);
- }
- }
- #to_js(scm) {
- let api = this.#instance.exports;
- let descr = api.describe(scm);
- let handlers = {
- fixnum: () => BigInt(api.fixnum_value(scm)),
- char: () => new Char(api.char_value(scm)),
- true: () => true,
- false: () => false,
- eof: () => new Eof,
- nil: () => new Nil,
- null: () => new Null,
- unspecified: () => new Unspecified,
- flonum: () => api.flonum_value(scm),
- bignum: () => api.bignum_value(scm),
- complex: () => new Complex(api.complex_real(scm),
- api.complex_imag(scm)),
- fraction: () => new Fraction(this.#to_js(api.fraction_num(scm)),
- this.#to_js(api.fraction_denom(scm))),
- pair: () => new Pair(this, scm),
- 'mutable-pair': () => new MutablePair(this, scm),
- vector: () => new Vector(this, scm),
- 'mutable-vector': () => new MutableVector(this, scm),
- bytevector: () => new Bytevector(this, scm),
- 'mutable-bytevector': () => new MutableBytevector(this, scm),
- bitvector: () => new Bitvector(this, scm),
- 'mutable-bitvector': () => new MutableBitvector(this, scm),
- string: () => api.string_value(scm),
- 'mutable-string': () => new MutableString(this, scm),
- procedure: () => new Procedure(this, scm),
- symbol: () => new Sym(this, scm),
- keyword: () => new Keyword(this, scm),
- variable: () => new Variable(this, scm),
- 'atomic-box': () => new AtomicBox(this, scm),
- 'hash-table': () => new HashTable(this, scm),
- 'weak-table': () => new WeakTable(this, scm),
- fluid: () => new Fluid(this, scm),
- 'dynamic-state': () => new DynamicState(this, scm),
- syntax: () => new Syntax(this, scm),
- port: () => new Port(this, scm),
- struct: () => new Struct(this, scm),
- 'extern-ref': () => api.extern_value(scm)
- };
- let handler = handlers[descr];
- return handler ? handler() : scm;
- }
- call(func, ...args) {
- let api = this.#instance.exports;
- let argv = api.make_vector(args.length + 1, api.scm_false());
- func = this.#to_scm(func);
- api.vector_set(argv, 0, func);
- for (let [idx, arg] of args.entries())
- api.vector_set(argv, idx + 1, this.#to_scm(arg));
- argv = api.call(func, argv);
- let results = [];
- for (let idx = 0; idx < api.vector_length(argv); idx++)
- results.push(this.#to_js(api.vector_ref(argv, idx)))
- return results;
- }
- call_async(func, ...args) {
- return new Promise((resolve, reject) => {
- this.call(func,
- val => resolve(this.#to_js(val)),
- err => reject(this.#to_js(err)),
- ...args);
- })
- }
- car(x) { return this.#to_js(this.#instance.exports.car(x.obj)); }
- cdr(x) { return this.#to_js(this.#instance.exports.cdr(x.obj)); }
- vector_length(x) { return this.#instance.exports.vector_length(x.obj); }
- vector_ref(x, i) {
- return this.#to_js(this.#instance.exports.vector_ref(x.obj, i));
- }
- bytevector_length(x) {
- return this.#instance.exports.bytevector_length(x.obj);
- }
- bytevector_ref(x, i) {
- return this.#instance.exports.bytevector_ref(x.obj, i);
- }
- bitvector_length(x) {
- return this.#instance.exports.bitvector_length(x.obj);
- }
- bitvector_ref(x, i) {
- return this.#instance.exports.bitvector_ref(x.obj, i) == 1;
- }
- string_value(x) { return this.#instance.exports.string_value(x.obj); }
- symbol_name(x) { return this.#instance.exports.symbol_name(x.obj); }
- keyword_name(x) { return this.#instance.exports.keyword_name(x.obj); }
- }
- class SchemeTrapError extends Error {
- constructor(tag, data) { super(); this.tag = tag; this.data = data; }
- // FIXME: data is raw Scheme object; would need to be reflected to
- // have a toString.
- toString() { return `SchemeTrap(${this.tag}, <data>)`; }
- }
- function string_repr(str) {
- // FIXME: Improve to match Scheme.
- return '"' + str.replace(/(["\\])/g, '\\$1').replace(/\n/g, '\\n') + '"';
- }
- function flonum_to_string(f64) {
- if (Object.is(f64, -0)) {
- return '-0.0';
- } else if (Number.isFinite(f64)) {
- let repr = f64 + '';
- return /^-?[0-9]+$/.test(repr) ? repr + '.0' : repr;
- } else if (Number.isNaN(f64)) {
- return '+nan.0';
- } else {
- return f64 < 0 ? '-inf.0' : '+inf.0';
- }
- }
- let async_invoke = typeof queueMicrotask !== 'undefined'
- ? queueMicrotask
- : thunk => setTimeout(thunk, 0);
- function async_invoke_later(thunk, jiffies) {
- setTimeout(thunk, jiffies / 1000);
- }
- let wtf8_helper;
- function wtf8_to_string(wtf8) {
- let { as_iter, iter_next } = wtf8_helper.exports;
- let codepoints = [];
- let iter = as_iter(wtf8);
- for (let cp = iter_next(iter); cp != -1; cp = iter_next(iter))
- codepoints.push(cp);
- // Passing too many codepoints can overflow the stack.
- let maxcp = 100000;
- if (codepoints.length <= maxcp) {
- return String.fromCodePoint(...codepoints);
- }
- // For converting large strings, concatenate several smaller
- // strings.
- let substrings = [];
- let end = 0;
- for (let start = 0; start != codepoints.length; start = end) {
- end = Math.min(start + maxcp, codepoints.length);
- substrings.push(String.fromCodePoint(...codepoints.slice(start, end)));
- }
- return substrings.join('');
- }
- function string_to_wtf8(str) {
- let { string_builder, builder_push_codepoint, finish_builder } =
- wtf8_helper.exports;
- let builder = string_builder()
- for (let cp of str)
- builder_push_codepoint(builder, cp.codePointAt(0));
- return finish_builder(builder);
- }
- async function load_wtf8_helper_module(reflect_wasm_dir = '') {
- if (wtf8_helper) return;
- let wtf8_wasm = reflect_wasm_dir + "/wtf8.wasm";
- let { module, instance } = await instantiate_streaming(wtf8_wasm);
- wtf8_helper = instance;
- }
- class SchemeModule {
- #instance;
- #io_handler;
- #debug_handler;
- #ffi_handler;
- static #rt = {
- bignum_from_string(str) { return BigInt(str); },
- bignum_from_i32(n) { return BigInt(n); },
- bignum_from_i64(n) { return n; },
- bignum_from_u64(n) { return n < 0n ? 0xffff_ffff_ffff_ffffn + (n + 1n) : n; },
- bignum_is_i64(n) {
- return -0x8000_0000_0000_0000n <= n && n <= 0x7FFF_FFFF_FFFF_FFFFn;
- },
- bignum_is_u64(n) {
- return 0n <= n && n <= 0xFFFF_FFFF_FFFF_FFFFn;
- },
- // This truncates; see https://tc39.es/ecma262/#sec-tobigint64.
- bignum_get_i64(n) { return n; },
- bignum_add(a, b) { return BigInt(a) + BigInt(b) },
- bignum_sub(a, b) { return BigInt(a) - BigInt(b) },
- bignum_mul(a, b) { return BigInt(a) * BigInt(b) },
- bignum_lsh(a, b) { return BigInt(a) << BigInt(b) },
- bignum_rsh(a, b) { return BigInt(a) >> BigInt(b) },
- bignum_quo(a, b) { return BigInt(a) / BigInt(b) },
- bignum_rem(a, b) { return BigInt(a) % BigInt(b) },
- bignum_mod(a, b) {
- let r = BigInt(a) % BigInt(b);
- if ((b > 0n && r < 0n) || (b < 0n && r > 0n)) {
- return b + r;
- } else {
- return r;
- }
- },
- bignum_gcd(a, b) {
- a = BigInt(a);
- b = BigInt(b);
- if (a < 0n) { a = -a; }
- if (b < 0n) { b = -b; }
- if (a == 0n) { return b; }
- if (b == 0n) { return a; }
- let r;
- while (b != 0n) {
- r = a % b;
- a = b;
- b = r;
- }
- return a;
- },
- bignum_logand(a, b) { return BigInt(a) & BigInt(b); },
- bignum_logior(a, b) { return BigInt(a) | BigInt(b); },
- bignum_logxor(a, b) { return BigInt(a) ^ BigInt(b); },
- bignum_logsub(a, b) { return BigInt(a) & (~ BigInt(b)); },
- bignum_lt(a, b) { return a < b; },
- bignum_le(a, b) { return a <= b; },
- bignum_eq(a, b) { return a == b; },
- bignum_to_f64(n) { return Number(n); },
- f64_is_nan(n) { return Number.isNaN(n); },
- f64_is_infinite(n) { return !Number.isFinite(n); },
- flonum_to_string,
- string_upcase: Function.call.bind(String.prototype.toUpperCase),
- string_downcase: Function.call.bind(String.prototype.toLowerCase),
- make_weak_map() { return new WeakMap; },
- weak_map_get(map, k, fail) {
- const val = map.get(k);
- return val === undefined ? fail: val;
- },
- weak_map_set(map, k, v) { return map.set(k, v); },
- weak_map_delete(map, k) { return map.delete(k); },
- fsqrt: Math.sqrt,
- fsin: Math.sin,
- fcos: Math.cos,
- ftan: Math.tan,
- fasin: Math.asin,
- facos: Math.acos,
- fatan: Math.atan,
- fatan2: Math.atan2,
- flog: Math.log,
- fexp: Math.exp,
- jiffies_per_second() { return 1000000; },
- current_jiffy() { return performance.now() * 1000; },
- current_second() { return Date.now() / 1000; },
- async_invoke,
- async_invoke_later,
- promise_on_completed(p, kt, kf) { p.then(kt, kf); },
- promise_complete(callback, val) { callback(val); },
- // Wrap in functions to allow for lazy loading of the wtf8
- // module.
- wtf8_to_string(wtf8) { return wtf8_to_string(wtf8); },
- string_to_wtf8(str) { return string_to_wtf8(str); },
- die(tag, data) { throw new SchemeTrapError(tag, data); }
- };
- constructor(instance) {
- this.#instance = instance;
- let open_file_error = (filename) => {
- throw new Error('No file system access');
- };
- if (typeof printErr === 'function') { // v8/sm dev console
- // On the console, try to use 'write' (v8) or 'putstr' (sm),
- // as these don't add an extraneous newline. Unfortunately
- // JSC doesn't have a printer that doesn't add a newline.
- let write_no_newline =
- typeof write === 'function' ? write
- : typeof putstr === 'function' ? putstr : print;
- // Use readline when available. v8 strips newlines so
- // we need to add them back.
- let read_stdin =
- typeof readline == 'function' ? () => {
- let line = readline();
- if (line) {
- return `${line}\n`;
- } else {
- return '\n';
- }
- }: () => '';
- this.#io_handler = {
- write_stdout: write_no_newline,
- write_stderr: printErr,
- read_stdin,
- file_exists: (filename) => false,
- open_input_file: open_file_error,
- open_output_file: open_file_error,
- close_file: () => undefined,
- read_file: (handle, length) => 0,
- write_file: (handle, length) => 0,
- seek_file: (handle, offset, whence) => -1,
- file_random_access: (handle) => false,
- file_buffer_size: (handle) => 0,
- file_buffer_ref: (handle, i) => 0,
- file_buffer_set: (handle, i, x) => undefined,
- delete_file: (filename) => undefined
- };
- } else if (typeof window !== 'undefined') { // web browser
- this.#io_handler = {
- write_stdout: console.log,
- write_stderr: console.error,
- read_stdin: () => '',
- file_exists: (filename) => false,
- open_input_file: open_file_error,
- open_output_file: open_file_error,
- close_file: () => undefined,
- read_file: (handle, length) => 0,
- write_file: (handle, length) => 0,
- seek_file: (handle, offset, whence) => -1,
- file_random_access: (handle) => false,
- file_buffer_size: (handle) => 0,
- file_buffer_ref: (handle, i) => 0,
- file_buffer_set: (handle, i, x) => undefined,
- delete_file: (filename) => undefined
- };
- } else { // nodejs
- const fs = require('fs');
- const process = require('process');
- const bufLength = 1024;
- const stdinBuf = Buffer.alloc(bufLength);
- const SEEK_SET = 0, SEEK_CUR = 1, SEEK_END = 2;
- this.#io_handler = {
- write_stdout: process.stdout.write.bind(process.stdout),
- write_stderr: process.stderr.write.bind(process.stderr),
- read_stdin: () => {
- let n = fs.readSync(process.stdin.fd, stdinBuf, 0, stdinBuf.length);
- return stdinBuf.toString('utf8', 0, n);
- },
- file_exists: fs.existsSync.bind(fs),
- open_input_file: (filename) => {
- let fd = fs.openSync(filename, 'r');
- return {
- fd,
- buf: Buffer.alloc(bufLength),
- pos: 0
- };
- },
- open_output_file: (filename) => {
- let fd = fs.openSync(filename, 'w');
- return {
- fd,
- buf: Buffer.alloc(bufLength),
- pos: 0
- };
- },
- close_file: (handle) => {
- fs.closeSync(handle.fd);
- },
- read_file: (handle, count) => {
- const n = fs.readSync(handle.fd, handle.buf, 0, count, handle.pos);
- handle.pos += n;
- return n;
- },
- write_file: (handle, count) => {
- const n = fs.writeSync(handle.fd, handle.buf, 0, count, handle.pos);
- handle.pos += n;
- return n;
- },
- seek_file: (handle, offset, whence) => {
- // There doesn't seem to be a way to ask NodeJS if
- // a position is valid or not.
- if (whence == SEEK_SET) {
- handle.pos = offset;
- return handle.pos;
- } else if (whence == SEEK_CUR) {
- handle.pos += offset;
- return handle.pos;
- }
- // SEEK_END not supported.
- return -1;
- },
- file_random_access: (handle) => {
- return true;
- },
- file_buffer_size: (handle) => {
- return handle.buf.length;
- },
- file_buffer_ref: (handle, i) => {
- return handle.buf[i];
- },
- file_buffer_set: (handle, i, x) => {
- handle.buf[i] = x;
- },
- delete_file: fs.rmSync.bind(fs)
- };
- }
- this.#debug_handler = {
- debug_str(x) { console.log(`debug: ${x}`); },
- debug_str_i32(x, y) { console.log(`debug: ${x}: ${y}`); },
- debug_str_scm(x, y) { console.log(`debug: ${x}: #<scm>`); },
- };
- }
- static async fetch_and_instantiate(path, { abi, reflect_wasm_dir = '.',
- user_imports = {} }) {
- await load_wtf8_helper_module(reflect_wasm_dir);
- let io = {
- write_stdout(str) { mod.#io_handler.write_stdout(str); },
- write_stderr(str) { mod.#io_handler.write_stderr(str); },
- read_stdin() { return mod.#io_handler.read_stdin(); },
- file_exists(filename) { return mod.#io_handler.file_exists(filename); },
- open_input_file(filename) { return mod.#io_handler.open_input_file(filename); },
- open_output_file(filename) { return mod.#io_handler.open_output_file(filename); },
- close_file(handle) { mod.#io_handler.close_file(handle); },
- read_file(handle, length) { return mod.#io_handler.read_file(handle, length); },
- write_file(handle, length) { return mod.#io_handler.write_file(handle, length); },
- seek_file(handle, offset, whence) { return mod.#io_handler.seek_file(handle, offset, whence); },
- file_random_access(handle) { return mod.#io_handler.file_random_access(handle); },
- file_buffer_size(handle) { return mod.#io_handler.file_buffer_size(handle); },
- file_buffer_ref(handle, i) { return mod.#io_handler.file_buffer_ref(handle, i); },
- file_buffer_set(handle, i, x) { return mod.#io_handler.file_buffer_set(handle, i, x); },
- delete_file(filename) { mod.#io_handler.delete_file(filename); }
- };
- let debug = {
- debug_str(x) { mod.#debug_handler.debug_str(x); },
- debug_str_i32(x, y) { mod.#debug_handler.debug_str_i32(x, y); },
- debug_str_scm(x, y) { mod.#debug_handler.debug_str_scm(x, y); },
- }
- let ffi = {
- procedure_to_extern(proc) {
- return mod.#ffi_handler.procedure_to_extern(proc);
- }
- };
- let imports = {
- rt: SchemeModule.#rt,
- abi, debug, io, ffi, ...user_imports
- };
- let { module, instance } = await instantiate_streaming(path, imports);
- let mod = new SchemeModule(instance);
- return mod;
- }
- set_io_handler(h) { this.#io_handler = h; }
- set_debug_handler(h) { this.#debug_handler = h; }
- set_ffi_handler(h) { this.#ffi_handler = h; }
- all_exports() { return this.#instance.exports; }
- exported_abi() {
- let abi = {}
- for (let [k, v] of Object.entries(this.all_exports())) {
- if (k.startsWith("$"))
- abi[k] = v;
- }
- return abi;
- }
- exports() {
- let ret = {}
- for (let [k, v] of Object.entries(this.all_exports())) {
- if (!k.startsWith("$"))
- ret[k] = v;
- }
- return ret;
- }
- get_export(name) {
- if (name in this.all_exports())
- return this.all_exports()[name];
- throw new Error(`unknown export: ${name}`)
- }
- async reflect(opts = {}) {
- return await Scheme.reflect(this.exported_abi(), opts);
- }
- }
- function repr(obj) {
- if (obj instanceof HeapObject)
- return obj.repr();
- if (typeof obj === 'boolean')
- return obj ? '#t' : '#f';
- if (typeof obj === 'number')
- return flonum_to_string(obj);
- if (typeof obj === 'string')
- return string_repr(obj);
- return obj + '';
- }
- // Modulize when possible.
- if (typeof exports !== 'undefined') {
- exports.Scheme = Scheme;
- exports.repr = repr;
- }
|