• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jcubic / lips / 15562078490

10 Jun 2025 02:19PM UTC coverage: 86.31%. Remained the same
15562078490

push

github

jcubic
fix rounding handling

2776 of 3178 branches covered (87.35%)

Branch coverage included in aggregate %.

28 of 52 new or added lines in 1 file covered. (53.85%)

693 existing lines in 1 file now uncovered.

10710 of 12447 relevant lines covered (86.04%)

1097370.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.31
/src/lips.js
1
/**@license
1✔
2
 *   __ __                          __
1✔
3
 *  / / \ \       _    _  ___  ___  \ \
1✔
4
 * | |   \ \     | |  | || . \/ __>  | |
1✔
5
 * | |    > \    | |_ | ||  _/\__ \  | |
1✔
6
 * | |   / ^ \   |___||_||_|  <___/  | |
1✔
7
 *  \_\ /_/ \_\                     /_/
1✔
8
 *
1✔
9
 * <https://lips.js.org>
1✔
10
 *
1✔
11
 * LIPS is Pretty Simple - Scheme based powerful LISP in JavaScript
1✔
12
 *
1✔
13
 * Copyright (c) 2018-2024 Jakub T. Jankiewicz <https://jcubic.pl/me>
1✔
14
 * Released under the MIT license
1✔
15
 *
1✔
16
 * Includes:
1✔
17
 *
1✔
18
 * ucs2decode function from Punycode v 2.1.1 by Mathias Bynens MIT License
1✔
19
 *
1✔
20
 * Author: Diego Perini (diego.perini at gmail.com)
1✔
21
 * Summary: cross-browser wrapper for DOMContentLoaded
1✔
22
 * Updated: 20101020
1✔
23
 * License: MIT
1✔
24
 * Version: 1.2
1✔
25
 *
1✔
26
 * contentloaded.js
1✔
27
 *
1✔
28
 * URL:
1✔
29
 * http://javascript.nwbox.com/ContentLoaded/
1✔
30
 * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
1✔
31
 *
1✔
32
 * The rationalize algorithm is by Per M.A. Bothner, Alan Bawden and Marc Feeley.
1✔
33
 * source: Kawa, C-Gambit
1✔
34
 *
1✔
35
 * Build time: {{DATE}}
1✔
36
 */
1✔
37
/*
1✔
38
 * TODO: consider using exec in env.eval or use different maybe_async code
1✔
39
 */
1✔
40
/* global jQuery, BigInt, Map, WeakMap, Set, Symbol, importScripts, Uint8Array */
1✔
41
"use strict";
1✔
42

1✔
43
const root = typeof global !== 'undefined' ? global : self;
1!
44

1✔
45
import { addExtension, Encoder } from 'cbor-x';
1✔
46
import { pack, unpack } from 'lzjb-pack';
1✔
47
import unfetch from 'unfetch/dist/unfetch.mjs';
1✔
48

1✔
49
/* c8 ignore next 3 */
1✔
50
if (!root.fetch) {
1✔
51
    root.fetch = unfetch;
1✔
52
}
1✔
53

1✔
54
// -------------------------------------------------------------------------
1✔
55
// :: typechecking maps
1✔
56
// -------------------------------------------------------------------------
1✔
57
const type_mapping = {
1✔
58
    'pair': Pair,
1✔
59
    'symbol': LSymbol,
1✔
60
    'number': LNumber,
1✔
61
    'array': Array,
1✔
62
    'nil': Nil,
1✔
63
    'character': LCharacter,
1✔
64
    'values': Values,
1✔
65
    'input-port': InputPort,
1✔
66
    'output-port': OutputPort,
1✔
67
    'regex': RegExp,
1✔
68
    'syntax': Syntax,
1✔
69
    'eof': EOF,
1✔
70
    'macro': Macro,
1✔
71
    'string': LString,
1✔
72
    'native-symbol': Symbol
1✔
73
};
1✔
74
const type_constants = new Map([
1✔
75
    [NaN, 'NaN'],
1✔
76
    [null, 'null']
1✔
77
]);
1✔
78
// -------------------------------------------------------------------------
1✔
79

1✔
80
let fs, path, nodeRequire;
1✔
81

1✔
82
const BN = root.BN;
1✔
83

1✔
84
/* eslint-disable */
1✔
85
/* c8 ignore next 32 */
1✔
86
function contentLoaded(win, fn) {
1✔
87
    var done = false, top = true,
1✔
88

1✔
89
        doc = win.document,
1✔
90
        root = doc.documentElement,
1✔
91
        modern = doc.addEventListener,
1✔
92

1✔
93
        add = modern ? 'addEventListener' : 'attachEvent',
1✔
94
        rem = modern ? 'removeEventListener' : 'detachEvent',
1✔
95
        pre = modern ? '' : 'on',
1✔
96

1✔
97
        init = function(e) {
1✔
98
            if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
1✔
99
            (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
1✔
100
            if (!done && (done = true)) fn.call(win, e.type || e);
1✔
101
        },
1✔
102

1✔
103
        poll = function() {
1✔
104
            try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
1✔
105
            init('poll');
1✔
106
        };
1✔
107

1✔
108
    if (doc.readyState == 'complete') fn.call(win, 'lazy');
1✔
109
    else {
1✔
110
        if (!modern && root.doScroll) {
1✔
111
            try { top = !win.frameElement; } catch(e) { }
1✔
112
            if (top) poll();
1✔
113
        }
1✔
114
        doc[add](pre + 'DOMContentLoaded', init, false);
1✔
115
        doc[add](pre + 'readystatechange', init, false);
1✔
116
        win[add](pre + 'load', init, false);
1✔
117
    }
1✔
118
}
×
119
// -------------------------------------------------------------------------
1✔
120
/* c8 ignore next 13 */
1✔
121
function log(x, ...args) {
1✔
122
    if (is_plain_object(x) && is_debug(args[0])) {
1✔
123
        console.log(map_object(x, function(value) {
1✔
124
            return to_string(value, true);
1✔
125
        }));
1✔
126
    } else if (is_debug()) {
1✔
127
        console.log(to_string(x, true), ...args.map(item => {
1✔
128
            return to_string(item, true);
1✔
129
        }));
1✔
130
    }
1✔
131
}
1✔
132
/* eslint-enable */
1✔
133
// ----------------------------------------------------------------------
1✔
134
/* c8 ignore next */
1✔
135
function is_debug(n = null) {
1✔
136
    const debug = user_env && user_env.get('DEBUG', { throwError: false });
108,815✔
137
    if (n === null) {
108,815✔
138
        return debug === true;
108,815✔
139
    }
108,815✔
140
    return debug?.valueOf() === n.valueOf();
108,815!
141
}
108,815✔
142
/* eslint-enable */
1✔
143
/* eslint-disable max-len */
1✔
144
// functions generate regexes to match number rational, integer, complex, complex+rational
1✔
145
function num_mnemicic_re(mnemonic) {
18✔
146
    return mnemonic ? `(?:#${mnemonic}(?:#[ie])?|#[ie]#${mnemonic})` : '(?:#[ie])?';
18✔
147
}
18✔
148
function gen_rational_re(mnemonic, range) {
6✔
149
    return `${num_mnemicic_re(mnemonic)}[+-]?${range}+/${range}+`;
6✔
150
}
6✔
151
// TODO: float complex
1✔
152
function gen_complex_re(mnemonic, range) {
6✔
153
    // [+-]i have (?=..) so it don't match +i from +inf.0
6✔
154
    return `${num_mnemicic_re(mnemonic)}(?:[+-]?(?:${range}+/${range}+|nan.0|inf.0|${range}+))?(?:[+-]i|[+-]?(?:${range}+/${range}+|${range}+|nan.0|inf.0)i)(?=[()[\\]\\s]|$)`;
6✔
155
}
6✔
156
function gen_integer_re(mnemonic, range) {
6✔
157
    return `${num_mnemicic_re(mnemonic)}[+-]?${range}+`;
6✔
158
}
6✔
159
var re_re = /^#\/((?:\\\/|[^/]|\[[^\]]*\/[^\]]*\])+)\/([gimyus]*)$/;
1✔
160
var float_stre = '(?:[-+]?(?:[0-9]+(?:[eE][-+]?[0-9]+)|(?:\\.[0-9]+|[0-9]+\\.[0-9]+)(?:[eE][-+]?[0-9]+)?)|[0-9]+\\.)';
1✔
161
// TODO: extend to ([+-]1/2|float)([+-]1/2|float)
1✔
162
var complex_float_stre = `(?:#[ie])?(?:[+-]?(?:[0-9][0-9_]*/[0-9][0-9_]*|nan.0|inf.0|${float_stre}|[+-]?[0-9]+))?(?:${float_stre}|[+-](?:[0-9]+/[0-9]+|[0-9]+|nan.0|inf.0))i`;
1✔
163
var float_re = new RegExp(`^(#[ie])?${float_stre}$`, 'i');
1✔
164
function make_complex_match_re(mnemonic, range) {
5✔
165
    // complex need special treatment of 10e+1i when it's hex or decimal
5✔
166
    var neg = mnemonic === 'x' ? `(?!\\+|${range})` : `(?!\\.|${range})`;
5✔
167
    var fl = '';
5✔
168
    if (mnemonic === '') {
5✔
169
        fl = '(?:[-+]?(?:[0-9]+(?:[eE][-+]?[0-9]+)|(?:\\.[0-9]+|[0-9]+\\.[0-9]+(?![0-9]))(?:[eE][-+]?[0-9]+)?))';
2✔
170
    }
2✔
171
    return new RegExp(`^((?:(?:${fl}|[-+]?inf.0|[-+]?nan.0|[+-]?${range}+/${range}+(?!${range})|[+-]?${range}+)${neg})?)(${fl}|[-+]?inf.0|[-+]?nan.0|[+-]?${range}+/${range}+|[+-]?${range}+|[+-])i$`, 'i');
5✔
172
}
5✔
173
var complex_list_re = (function() {
1✔
174
    var result = {};
1✔
175
    [
1✔
176
        [10, '', '[0-9]'],
1✔
177
        [16, 'x', '[0-9a-fA-F]'],
1✔
178
        [8, 'o', '[0-7]'],
1✔
179
        [2, 'b', '[01]']
1✔
180
    ].forEach(([radix, mnemonic, range]) => {
1✔
181
        result[radix] = make_complex_match_re(mnemonic, range);
4✔
182
    });
1✔
183
    return result;
1✔
184
})();
1✔
185
const characters = {
1✔
186
    'alarm': '\x07',
1✔
187
    'backspace': '\x08',
1✔
188
    'delete': '\x7F',
1✔
189
    'escape': '\x1B',
1✔
190
    'newline': '\n',
1✔
191
    'null': '\x00',
1✔
192
    'return': '\r',
1✔
193
    'space': ' ',
1✔
194
    'tab': '\t',
1✔
195
    // new symbols from ASCII table in SRFI-175
1✔
196
    'dle': '\x10',
1✔
197
    'soh': '\x01',
1✔
198
    'dc1': '\x11',
1✔
199
    'stx': '\x02',
1✔
200
    'dc2': '\x12',
1✔
201
    'etx': '\x03',
1✔
202
    'dc3': '\x13',
1✔
203
    'eot': '\x04',
1✔
204
    'dc4': '\x14',
1✔
205
    'enq': '\x05',
1✔
206
    'nak': '\x15',
1✔
207
    'ack': '\x06',
1✔
208
    'syn': '\x16',
1✔
209
    'bel': '\x07',
1✔
210
    'etb': '\x17',
1✔
211
    'bs': '\x08',
1✔
212
    'can': '\x18',
1✔
213
    'ht': '\x09',
1✔
214
    'em': '\x19',
1✔
215
    'lf': '\x0a',
1✔
216
    'sub': '\x1a',
1✔
217
    'vt': '\x0b',
1✔
218
    'esc': '\x1b',
1✔
219
    'ff': '\x0c',
1✔
220
    'fs': '\x1c',
1✔
221
    'cr': '\x0d',
1✔
222
    'gs': '\x1d',
1✔
223
    'so': '\x0e',
1✔
224
    'rs': '\x1e',
1✔
225
    'si': '\x0f',
1✔
226
    'us': '\x1f',
1✔
227
    'del': '\x7f'
1✔
228
};
1✔
229
// -------------------------------------------------------------------------
1✔
230
// :: ref: https://github.com/bestiejs/punycode.js/blob/master/punycode.js
1✔
231
// -------------------------------------------------------------------------
1✔
232
function ucs2decode(string) {
102✔
233
    const output = [];
102✔
234
    let counter = 0;
102✔
235
    const length = string.length;
102✔
236
    while (counter < length) {
102✔
237
        const value = string.charCodeAt(counter++);
136✔
238
        if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
136✔
239
            // It's a high surrogate, and there is a next character.
1✔
240
            const extra = string.charCodeAt(counter++);
1✔
241
            if ((extra & 0xFC00) === 0xDC00) { // Low surrogate.
1✔
242
                output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
1✔
243
            } else {
1!
244
                // It's an unmatched surrogate; only append this code unit, in case the
×
245
                // next code unit is the high surrogate of a surrogate pair.
×
246
                output.push(value);
×
247
                counter--;
×
248
            }
×
249
        } else {
136✔
250
            output.push(value);
135✔
251
        }
135✔
252
    }
136✔
253
    return output;
102✔
254
}
102✔
255
// -------------------------------------------------------------------------
1✔
256
const character_symbols = Object.keys(characters).join('|');
1✔
257
const char_sre_re = `#\\\\(?:x[0-9a-f]+|${character_symbols}|[\\s\\S])`;
1✔
258
const char_re = new RegExp(`^${char_sre_re}$`, 'i');
1✔
259
// Complex with (int) (float) (rational)
1✔
260
function make_num_stre(fn) {
3✔
261
    const ranges = [
3✔
262
        ['o', '[0-7]'],
3✔
263
        ['x', '[0-9a-fA-F]'],
3✔
264
        ['b', '[01]'],
3✔
265
        ['d', '[0-9]'],
3✔
266
        ['', '[0-9]']
3✔
267
    ];
3✔
268
    // float exception that don't accept mnemonics
3✔
269
    let result = ranges.map(([m, range]) => fn(m, range)).join('|');
3✔
270
    if (fn === gen_complex_re) {
3✔
271
        result = complex_float_stre + '|' + result;
1✔
272
    }
1✔
273
    return result;
3✔
274
}
3✔
275
function make_type_re(fn) {
3✔
276
    return new RegExp('^(?:' + make_num_stre(fn) + ')$', 'i');
3✔
277
}
3✔
278
const complex_re = make_type_re(gen_complex_re);
1✔
279
const rational_re = make_type_re(gen_rational_re);
1✔
280
const int_re = make_type_re(gen_integer_re);
1✔
281

1✔
282
// regexes with full range but without mnemonics for string->number
1✔
283
const int_bare_re = new RegExp('^(?:' + gen_integer_re('', '[0-9a-f]') + ')$', 'i');
1✔
284
const rational_bare_re = new RegExp('^(?:' + gen_rational_re('', '[0-9a-f]') + ')$', 'i');
1✔
285
const complex_bare_re = new RegExp('^(?:' + gen_complex_re('', '[0-9a-f]') + ')$', 'i');
1✔
286

1✔
287
const complex_bare_match_re = make_complex_match_re('', '[0-9a-fA-F]');
1✔
288

1✔
289
const pre_num_parse_re = /((?:#[xodbie]){0,2})(.*)/i;
1✔
290
/* eslint-enable */
1✔
291
function num_pre_parse(arg) {
4,700✔
292
    var parts = arg.match(pre_num_parse_re);
4,700✔
293
    var options = {};
4,700✔
294
    if (parts[1]) {
4,700✔
295
        var type = parts[1].replace(/#/g, '').toLowerCase().split('');
217✔
296
        if (type.includes('x')) {
217✔
297
            options.radix = 16;
95✔
298
        } else if (type.includes('o')) {
217✔
299
            options.radix = 8;
27✔
300
        } else if (type.includes('b')) {
122✔
301
            options.radix = 2;
61✔
302
        } else if (type.includes('d')) {
95!
303
            options.radix = 10;
×
304
        }
×
305
        if (type.includes('i')) {
217✔
306
            options.inexact = true;
79✔
307
        }
79✔
308
        if (type.includes('e')) {
217✔
309
            options.exact = true;
31✔
310
        }
31✔
311
    }
217✔
312
    options.number = parts[2];
4,700✔
313
    return options;
4,700✔
314
}
4,700✔
315
// ----------------------------------------------------------------------
1✔
316
function parse_rational(arg, radix = 10) {
219✔
317
    var parse = num_pre_parse(arg);
219✔
318
    var parts = parse.number.split('/');
219✔
319
    var num = LRational({
219✔
320
        num: LNumber([parts[0], parse.radix || radix]),
219✔
321
        denom: LNumber([parts[1], parse.radix || radix])
219✔
322
    });
219✔
323
    if (parse.inexact) {
219✔
324
        return num.valueOf();
16✔
325
    } else {
219✔
326
        return num;
203✔
327
    }
203✔
328
}
219✔
329
// ----------------------------------------------------------------------
1✔
330
function parse_integer(arg, radix = 10) {
2,727✔
331
    var parse = num_pre_parse(arg);
2,727✔
332
    if (parse.inexact) {
2,727✔
333
        return LFloat(parseInt(parse.number, parse.radix || radix), true);
31✔
334
    }
31✔
335
    return LNumber([parse.number, parse.radix || radix]);
2,727✔
336
}
2,727✔
337
// ----------------------------------------------------------------------
1✔
338
function parse_character(arg) {
196✔
339
    var m = arg.match(/#\\x([0-9a-f]+)$/i);
196✔
340
    var char;
196✔
341
    if (m) {
196✔
342
        var ord = parseInt(m[1], 16);
1✔
343
        char = String.fromCodePoint(ord);
1✔
344
    } else {
196✔
345
        m = arg.match(/#\\([\s\S]+)$/);
195✔
346
        if (m) {
195✔
347
            char = m[1];
195✔
348
        }
195✔
349
    }
195✔
350
    if (char) {
196✔
351
        return LCharacter(char);
196✔
352
    }
196✔
353
    throw new Error('Parse: invalid character');
×
354
}
196✔
355
// ----------------------------------------------------------------------
1✔
356
function parse_complex(arg, radix = 10) {
931✔
357
    function parse_num(n) {
931✔
358
        var value;
1,769✔
359
        if (n === '+') {
1,769✔
360
            value = LNumber(1);
21✔
361
        } else if (n === '-') {
1,769✔
362
            value = LNumber(-1);
15✔
363
        } else if (n.match(int_bare_re)) {
1,748✔
364
            value = LNumber([n, radix]);
505✔
365
        } else if (n.match(rational_bare_re)) {
1,733✔
366
            var parts = n.split('/');
330✔
367
            value = LRational({
330✔
368
                num: LNumber([parts[0], radix]),
330✔
369
                denom: LNumber([parts[1], radix])
330✔
370
            });
330✔
371
        } else if (n.match(float_re)) {
1,228✔
372
            var float = parse_float(n);
534✔
373
            if (parse.exact) {
534✔
374
                return float.toRational();
7✔
375
            }
7✔
376
            return float;
527✔
377
        } else if (n.match(/nan.0$/)) {
898✔
378
            return LNumber(NaN);
248✔
379
        } else if (n.match(/inf.0$/)) {
364✔
380
            if (n[0] === '-') {
116✔
381
                return LNumber(Number.NEGATIVE_INFINITY);
6✔
382
            }
6✔
383
            return LNumber(Number.POSITIVE_INFINITY);
110✔
384
        } else {
116!
385
            throw new Error('Internal Parser Error');
×
386
        }
×
387
        if (parse.inexact) {
1,769✔
388
            return LFloat(value.valueOf(), true);
41✔
389
        }
41✔
390
        return value;
830✔
391
    }
1,769✔
392
    var parse = num_pre_parse(arg);
931✔
393
    radix = parse.radix || radix;
931✔
394
    var parts;
931✔
395
    var bare_match = parse.number.match(complex_bare_match_re);
931✔
396
    if (radix !== 10 && bare_match) {
931✔
397
        parts = bare_match;
41✔
398
    } else {
931✔
399
        parts = parse.number.match(complex_list_re[radix]);
890✔
400
    }
890✔
401
    var re, im;
931✔
402
    im = parse_num(parts[2]);
931✔
403
    if (parts[1]) {
931✔
404
        re = parse_num(parts[1]);
838✔
405
    } else if (im instanceof LFloat) {
931✔
406
        re = LFloat(0);
45✔
407
    } else {
93✔
408
        re = LNumber(0);
48✔
409
    }
48✔
410
    if (im.cmp(0) === 0 && im.__type__ === 'bigint') {
931✔
411
        return re;
6✔
412
    }
6✔
413
    return LComplex({ im, re });
925✔
414
}
931✔
415
// ----------------------------------------------------------------------
1✔
416
function is_int(value) {
814✔
417
    return parseInt(value.toString(), 10) === value;
814✔
418
}
814✔
419
// ----------------------------------------------------------------------
1✔
420
function parse_big_int(str) {
802✔
421
    var num_match = str.match(/^(([-+]?[0-9]*)(?:\.([0-9]+))?)e([-+]?[0-9]+)/i);
802✔
422
    if (num_match) {
802✔
423
        var exponent = parseInt(num_match[4], 10);
62✔
424
        var mantisa;// = parseFloat(num_match[1]);
62✔
425
        var digits = num_match[1].replace(/[-+]?([0-9]*)\..+$/, '$1').length;
62✔
426
        var decimal_points = num_match[3] && num_match[3].length;
62✔
427
        if (digits < Math.abs(exponent)) {
62✔
428
            mantisa = LNumber([num_match[1].replace(/\./, ''), 10]);
60✔
429
            if (decimal_points) {
60✔
430
                exponent -= decimal_points;
55✔
431
            }
55✔
432
        }
60✔
433
    }
62✔
434
    return { exponent, mantisa };
802✔
435
}
802✔
436

1✔
437
// ----------------------------------------------------------------------
1✔
438
function string_to_float(str) {
823✔
439
    if (str.match(/e/i)) {
823✔
440
        const [coefficient, exponent] = str.split('e');
82✔
441
        const decimal_places = Math.abs(parseInt(exponent));
82✔
442
        if (decimal_places < 7 && exponent < 0) {
82✔
443
            const zeros = '0'.repeat(decimal_places - 1);
17✔
444
            const sign = coefficient[0] === '-' ? '-' : '+';
17✔
445
            const digits = coefficient.replace(/(^[-+])|\./g, '');
17✔
446
            const float_str = `${sign}0.${zeros}${digits}`;
17✔
447
            return parseFloat(float_str);
17✔
448
        }
17✔
449
    }
82✔
450
    return parseFloat(str);
806✔
451
}
823✔
452

1✔
453
// ----------------------------------------------------------------------
1✔
454
function parse_float(arg) {
823✔
455
    const parse = num_pre_parse(arg);
823✔
456
    let value = string_to_float(parse.number);
823✔
457
    const simple_number = (parse.number.match(/\.0$/) ||
823✔
458
                         !parse.number.match(/\./)) && !parse.number.match(/e/i);
823✔
459
    if (!parse.inexact) {
823✔
460
        if (parse.exact && simple_number) {
815✔
461
            return LNumber(value);
1✔
462
        }
1✔
463
        // positive big num that eval to int e.g.: 1.2e+20
814✔
464
        if (is_int(value) &&
815✔
465
            Number.isSafeInteger(value) &&
815✔
466
            parse.number.match(/e\+?[0-9]/i)) {
815✔
467
            return LNumber(value);
12✔
468
        }
12✔
469
        // calculate big int and big fraction by hand - it don't fit into JS float
802✔
470
        const { mantisa, exponent } = parse_big_int(parse.number);
802✔
471
        if (mantisa !== undefined && exponent !== undefined) {
815✔
472
            const factor = LNumber(10).pow(LNumber(Math.abs(exponent)));
60✔
473
            if (parse.exact && exponent < 0) {
60✔
474
                return LRational({ num: mantisa, denom: factor });
4✔
475
            } else if (exponent > 0 && (parse.exact || !parse.number.match(/\./))) {
60✔
476
                return LNumber(mantisa).mul(factor);
1✔
477
            }
1✔
478
        }
60✔
479
    }
815✔
480
    value = LFloat(value, true);
805✔
481
    if (parse.exact) {
823✔
482
        return value.toRational();
2✔
483
    }
2✔
484
    return value;
803✔
485
}
823✔
486
// ----------------------------------------------------------------------
1✔
487
function parse_string(string) {
1,240✔
488
    // handle non JSON escapes and skip unicode escape \u (even partial)
1,240✔
489
    string = string.replace(/\\x([0-9a-f]+);/ig, function(_, hex) {
1,240✔
490
        return "\\u" + hex.padStart(4, '0');
3✔
491
    }).replace(/\n/g, '\\n'); // in LIPS strings can be multiline
1,240✔
492
    var m = string.match(/(\\*)(\\x[0-9A-F])/i);
1,240✔
493
    if (m && m[1].length % 2 === 0) {
1,240✔
494
        throw new Error(`Invalid string literal, unclosed ${m[2]}`);
1✔
495
    }
1✔
496
    try {
1,239✔
497
        const str =  LString(JSON.parse(string));
1,239✔
498
        str.freeze();
1,239✔
499
        return str;
1,239✔
500
    } catch (e) {
1,240!
501
        const msg = e.message.replace(/in JSON /, '').replace(/.*Error: /, '');
×
502
        throw new Error(`Invalid string literal: ${msg}`);
×
503
    }
×
504
}
1,240✔
505
// ----------------------------------------------------------------------
1✔
506
function parse_symbol(arg) {
12,337✔
507
    const debug = arg.match(/^name/);
12,337✔
508
    const re = /(^|.)\|/g;
12,337✔
509
    if (arg.match(re)) {
12,337✔
510
        // handle escaped bar and escaped slash
18✔
511
        arg = arg.split('|').filter(Boolean).reduce((acc, str) => {
18✔
512
            let result = '';
5✔
513
            if (str.match(/^\\+$/)) {
5✔
514
                if (str.length > 1) {
4✔
515
                    const count = Math.floor(str.length / 2);
3✔
516
                    result = '\\'.repeat(count);
3✔
517
                }
3✔
518
                if (str.length % 2 !== 0) {
4✔
519
                    result += '|';
1✔
520
                }
1✔
521
            } else {
5✔
522
                result = str;
1✔
523
            }
1✔
524
            return acc + result;
5✔
525
        });
18✔
526
        const chars = {
18✔
527
            t: '\t',
18✔
528
            r: '\r',
18✔
529
            n: '\n'
18✔
530
        };
18✔
531
        arg = arg.replace(/\\(x[^;]+);/g, function(_, chr) {
18✔
532
            return String.fromCharCode(parseInt('0' + chr, 16));
6✔
533
        }).replace(/\\([trn])/g, function(_, chr) {
18✔
534
            return chars[chr];
8✔
535
        });
18✔
536
    }
18✔
537
    return new LSymbol(arg);
12,337✔
538
}
12,337✔
539
// ----------------------------------------------------------------------
1✔
540
function parse_argument(arg, meta = false) {
18,482✔
541
    const token = arg.token;
18,482✔
542
    if (constants.hasOwnProperty(token)) {
18,482✔
543
        return constants[token];
637✔
544
    }
637✔
545
    let result;
17,845✔
546
    if (token.match(/^"[\s\S]*"$/)) {
18,482✔
547
        result = parse_string(token);
1,240✔
548
    } else if (token[0] === '#') {
18,482✔
549
        var regex = token.match(re_re);
333✔
550
        if (regex) {
333✔
551
            result = new RegExp(regex[1], regex[2]);
20✔
552
        } else if (token.match(char_re)) {
333✔
553
            result = parse_character(token);
102✔
554
        }
102✔
555
        // characters with more than one codepoint
333✔
556
        var m = token.match(/#\\(.+)/);
333✔
557
        if (m && ucs2decode(m[1]).length === 1) {
333✔
558
            result = parse_character(token);
94✔
559
        }
94✔
560
    }
333✔
561
    if (token.match(/[0-9a-f]|[+-]i/i)) {
18,482✔
562
        if (token.match(int_re)) {
12,438✔
563
            result = parse_integer(token);
2,719✔
564
        } else if (token.match(float_re)) {
12,438✔
565
            result = parse_float(token);
288✔
566
        } else if (token.match(rational_re)) {
9,719✔
567
            result = parse_rational(token);
212✔
568
        } else if (token.match(complex_re)) {
9,431✔
569
            result = parse_complex(token);
924✔
570
        }
924✔
571
    }
12,438✔
572
    if (!result && token.match(/^#[iexobd]/)) {
18,482✔
573
        throw new Error('Invalid numeric constant: ' + arg);
2✔
574
    }
2✔
575
    if (!result) {
18,482✔
576
        result = parse_symbol(token);
12,337✔
577
    }
12,337✔
578
    if (meta) {
18,482✔
579
        const { col, offset, line } = arg;
12✔
580
        read_only(result, '__col__', col);
12✔
581
        read_only(result, '__offset__', offset);
12✔
582
        read_only(result, '__line__', line);
12✔
583
    }
12✔
584
    return result;
17,842✔
585
}
18,482✔
586
// ----------------------------------------------------------------------
1✔
587
function is_atom_string(str) {
589✔
588
    return !(['(', ')', '[', ']'].includes(str) ||
589✔
589
             specials.names().includes(str));
589✔
590
}
589✔
591
// ----------------------------------------------------------------------
1✔
592
function is_symbol_string(str) {
589✔
593
    return is_atom_string(str) &&
589✔
594
        !(str.match(re_re) ||
326✔
595
          str.match(/^"[\s\S]*"$/) || str.match(int_re) ||
326✔
596
          str.match(float_re) || str.match(complex_re) ||
326✔
597
          str.match(rational_re) || str.match(char_re) ||
326✔
598
          ['#t', '#f', 'nil'].includes(str));
589✔
599
}
589✔
600
// ----------------------------------------------------------------------
1✔
601
var string_re = /"(?:\\[\S\s]|[^"])*"?/g;
1✔
602
// ----------------------------------------------------------------------
1✔
603
function escape_regex(str) {
1✔
604
    if (typeof str === 'string') {
1✔
605
        var special = /([-\\^$[\]()+{}?*.|])/g;
1✔
606
        return str.replace(special, '\\$1');
1✔
607
    }
1✔
608
    return str;
×
609
}
1✔
610
// ----------------------------------------------------------------------
1✔
611
// Stack used in balanced function
1✔
612
// TODO: use it in parser
1✔
613
// ----------------------------------------------------------------------
1✔
614
function Stack() {
35✔
615
    this.data = [];
35✔
616
}
35✔
617
Stack.prototype.push = function(item) {
1✔
618
    this.data.push(item);
134✔
619
};
1✔
620
Stack.prototype.top = function() {
1✔
621
    return this.data[this.data.length - 1];
101✔
622
};
1✔
623
Stack.prototype.pop = function() {
1✔
624
    return this.data.pop();
101✔
625
};
1✔
626
Stack.prototype.is_empty = function() {
1✔
627
    return !this.data.length;
136✔
628
};
1✔
629
// ----------------------------------------------------------------------
1✔
630
function tokens(str) {
53✔
631
    if (str instanceof LString) {
53!
632
        str = str.valueOf();
×
633
    }
×
634
    var lexer = new Lexer(str, { whitespace: true });
53✔
635
    var result = [];
53✔
636
    while (true) {
53✔
637
        const token = lexer.peek(true);
1,647✔
638
        if (token === eof) {
1,647✔
639
            break;
52✔
640
        }
52✔
641
        result.push(token);
1,594✔
642
        lexer.skip();
1,594✔
643
    }
1,594✔
644
    return result;
52✔
645
}
53✔
646
// ----------------------------------------------------------------------
1✔
647
function multiline_formatter(meta) {
88,967✔
648
    var { token, ...rest } = meta;
88,967✔
649
    if (token.match(/^"[\s\S]*"$/) && token.match(/\n/)) {
88,967✔
650
        var re = new RegExp('^ {1,' + (meta.col + 1) + '}', 'mg');
48✔
651
        token = token.replace(re, '');
48✔
652
    }
48✔
653
    return {
88,967✔
654
        token,
88,967✔
655
        ...rest
88,967✔
656
    };
88,967✔
657
}
88,967✔
658
// ----------------------------------------------------------------------
1✔
659
function Thunk(fn, cont = () => {}) {
47,488,311✔
660
    this.fn = fn;
47,488,311✔
661
    this.cont = cont;
47,488,311✔
662
}
47,488,311✔
663
// ----------------------------------------------------------------------
1✔
664
Thunk.prototype.toString = function() {
1✔
665
    return '#<Thunk>';
×
666
};
1✔
667
// ----------------------------------------------------------------------
1✔
668
function trampoline(fn) {
1,598,916✔
669
    return function(...args) {
1,598,916✔
670
        return unwind(fn.apply(this, args));
49,087,239✔
671
    };
1,598,916✔
672
}
1,598,916✔
673
// ----------------------------------------------------------------------
1✔
674
function unwind(result) {
49,087,239✔
675
    while (result instanceof Thunk) {
49,087,239✔
676
        const thunk = result;
47,488,311✔
677
        result = result.fn();
47,488,311✔
678
        if (!(result instanceof Thunk)) {
47,488,311✔
679
            thunk.cont();
19,365,874✔
680
        }
19,365,874✔
681
    }
47,488,311✔
682
    return result;
49,087,239✔
683
}
49,087,239✔
684
// ----------------------------------------------------------------------
1✔
685
function tokenize(str, meta = false) {
53✔
686
    if (str instanceof LString) {
53✔
687
        str = str.toString();
8✔
688
    }
8✔
689
    if (meta) {
53✔
690
        return tokens(str);
45✔
691
    } else {
53✔
692
        var result = tokens(str).map(function(token) {
8✔
693
            // we don't want literal space character to be trimmed
88✔
694
            if (token.token === '#\\ ' || token.token == '#\\\n') {
88!
695
                return token.token;
×
696
            }
×
697
            return token.token.trim();
88✔
698
        }).filter(function(token) {
8✔
699
            return token && !token.match(/^;/) && !token.match(/^#\|[\s\S]*\|#$/);
88✔
700
        });
8✔
701
        return strip_s_comments(result);
8✔
702
    }
8✔
703
}
53✔
704
// ----------------------------------------------------------------------
1✔
705
function strip_s_comments(tokens) {
7✔
706
    var s_count = 0;
7✔
707
    var s_start = null;
7✔
708
    var remove_list = [];
7✔
709
    for (let i = 0; i < tokens.length; ++i) {
7✔
710
        const token = tokens[i];
57✔
711
        if (token === '#;') {
57!
712
            if (['(', '['].includes(tokens[i + 1])) {
×
713
                s_count = 1;
×
714
                s_start = i;
×
715
            } else {
×
716
                remove_list.push([i, i + 2]);
×
717
            }
×
718
            i += 1;
×
719
            continue;
×
720
        }
×
721
        if (s_start !== null) {
57!
722
            if ([')', ']'].includes(token)) {
×
723
                s_count--;
×
724
            } else if (['(', '['].includes(token)) {
×
725
                s_count++;
×
726
            }
×
727
            if (s_count === 0) {
×
728
                remove_list.push([s_start, i + 1]);
×
729
                s_start = null;
×
730
            }
×
731
        }
×
732
    }
57✔
733
    tokens = tokens.slice();
7✔
734
    remove_list.reverse();
7✔
735
    for (const [begin, end] of remove_list) {
7!
736
        tokens.splice(begin, end - begin);
×
737
    }
×
738
    return tokens;
7✔
739
}
7✔
740
// ----------------------------------------------------------------------
1✔
741
// Detect if object is ES6 Symbol that work with polyfills
1✔
742
// ----------------------------------------------------------------------
1✔
743
function is_symbol(x) {
750,574✔
744
    return typeof x === 'symbol' ||
750,574✔
745
        typeof x === 'object' &&
654,093!
746
        Object.prototype.toString.call(x) === '[object Symbol]';
750,574✔
747
}
750,574✔
748
// ----------------------------------------------------------------------
1✔
749
// :: LSymbol constructor
1✔
750
// ----------------------------------------------------------------------
1✔
751
function LSymbol(name, interned = true) {
1,414,404✔
752
    if (name instanceof LString) {
1,414,404✔
753
        name = name.valueOf();
4✔
754
    }
4✔
755
    if (interned && LSymbol.list[name] instanceof LSymbol) {
1,414,404✔
756
        return LSymbol.list[name];
118,505✔
757
    }
118,505✔
758
    if (typeof this !== 'undefined' && this.constructor !== LSymbol ||
1,414,404✔
759
        typeof this === 'undefined') {
1,414,404✔
760
        return new LSymbol(name, interned);
1,285✔
761
    }
1,285✔
762
    this.__name__ = name;
1,294,614✔
763
    if (interned && typeof name === 'string') {
1,414,404✔
764
        LSymbol.list[name] = this;
1,695✔
765
    }
1,695✔
766
}
1,414,404✔
767
LSymbol.list = {};
1✔
768
LSymbol.literal = Symbol.for('__literal__');
1✔
769
LSymbol.object = Symbol.for('__object__');
1✔
770
// ----------------------------------------------------------------------
1✔
771
LSymbol.prototype.is_interned = function() {
1✔
772
    return LSymbol.list[this.__name__] == this;
7✔
773
};
1✔
774
// ----------------------------------------------------------------------
1✔
775
LSymbol.is = function(symbol, name) {
1✔
776
    return symbol instanceof LSymbol &&
13,546,009✔
777
        ((name instanceof LSymbol && symbol.__name__ === name.__name__) ||
8,184,979✔
778
         (typeof name === 'string' && symbol.__name__ === name) ||
8,184,979✔
779
         (name instanceof RegExp && name.test(symbol.__name__)));
13,546,009✔
780
};
1✔
781
// ----------------------------------------------------------------------
1✔
782
LSymbol.prototype.toString = function(quote) {
1✔
783
    //return '#<symbol \'' + this.name + '\'>';
750,574✔
784
    if (is_symbol(this.__name__)) {
750,574✔
785
        return symbol_to_string(this.__name__);
96,481✔
786
    }
96,481✔
787
    var str = this.valueOf();
654,093✔
788
    // those special characters can be normal symbol when printed
654,093✔
789
    if (quote && str.match(/(^;|[\s()[\]'])/)) {
750,574✔
790
        return `|${str}|`;
2✔
791
    }
2✔
792
    return str;
654,091✔
793
};
1✔
794
LSymbol.prototype.literal = function() {
1✔
795
    if (this.is_gensym()) {
5,117✔
796
        return this[LSymbol.literal];
216✔
797
    }
216✔
798
    return this.valueOf();
4,901✔
799
};
1✔
800
LSymbol.prototype.serialize = function() {
1✔
801
    if (LString.isString(this.__name__)) {
2✔
802
        return this.__name__;
2✔
803
    }
2✔
804
    return [symbol_to_string(this.__name__)];
×
805
};
1✔
806
LSymbol.prototype.valueOf = function() {
1✔
807
    return this.__name__.valueOf();
10,858,376✔
808
};
1✔
809
// -------------------------------------------------------------------------
1✔
810
LSymbol.prototype.is_gensym = function() {
1✔
811
    return is_gensym(this.__name__);
5,168✔
812
};
1✔
813
// -------------------------------------------------------------------------
1✔
814
function symbol_to_string(obj) {
96,481✔
815
    return obj.toString().replace(/^Symbol\(([^)]+)\)/, '$1');
96,481✔
816
}
96,481✔
817
// -------------------------------------------------------------------------
1✔
818
function is_gensym(symbol) {
1,298,334✔
819
    if (typeof symbol === 'symbol') {
1,298,334✔
820
        return !!symbol.toString().match(/^Symbol\(#:/);
295✔
821
    }
295✔
822
    return false;
1,298,039✔
823
}
1,298,334✔
824
// -------------------------------------------------------------------------
1✔
825
const gensym = (function() {
1✔
826
    var count = 0;
1✔
827
    function with_props(name, sym) {
1✔
828
        var symbol = new LSymbol(sym, false);
1,292,824✔
829
        hidden_prop(symbol, '__literal__', name);
1,292,824✔
830
        return symbol;
1,292,824✔
831
    }
1,292,824✔
832
    return function(name = null) {
1✔
833
        if (name instanceof LSymbol) {
1,292,894✔
834
            if (name.is_gensym()) {
40!
835
                return name;
×
836
            }
×
837
            name = name.valueOf();
40✔
838
        }
40✔
839
        if (is_gensym(name)) {
1,292,894✔
840
            // don't do double gynsyms in nested syntax-rules
70✔
841
            return LSymbol(name, false);
70✔
842
        }
70✔
843
        // use ES6 symbol as name for lips symbol (they are unique)
1,292,824✔
844
        if (name !== null) {
1,292,894✔
845
            return with_props(name, Symbol(`#:${name}`));
1,251,112✔
846
        }
1,251,112✔
847
        count++;
41,712✔
848
        return with_props(count, Symbol(`#:g${count}`));
41,712✔
849
    };
1✔
850
})();
1✔
851
// ----------------------------------------------------------------------
1✔
852
// :: helper function that make symbols in names array hygienic
1✔
853
// ----------------------------------------------------------------------
1✔
854
function hygienic_begin(envs, expr) {
1,158,813✔
855
    const begin = global_env.get('begin');
1,158,813✔
856
    const g_begin = gensym('begin');
1,158,813✔
857
    envs.forEach(env => {
1,158,813✔
858
        env.set(g_begin, begin);
1,858,911✔
859
    });
1,158,813✔
860
    return new Pair(g_begin, expr);
1,158,813✔
861
}
1,158,813✔
862

1✔
863
// ----------------------------------------------------------------------
1✔
864
// Class used to escape promises: feature #54
1✔
865
// ----------------------------------------------------------------------
1✔
866
function QuotedPromise(promise) {
19✔
867
    var internal = {
19✔
868
        pending: true,
19✔
869
        rejected: false,
19✔
870
        fulfilled: false,
19✔
871
        reason: undefined,
19✔
872
        type: undefined
19✔
873
    };
19✔
874
    // then added to __promise__ is needed otherwise rejection
19✔
875
    // will give UnhandledPromiseRejectionWarning in Node.js
19✔
876
    promise = promise.then(v => {
19✔
877
        internal.type = type(v);
17✔
878
        internal.fulfilled = true;
17✔
879
        internal.pending = false;
17✔
880
        return v;
17✔
881
    });
19✔
882
    // promise without catch, used for valueOf - for rejecting
19✔
883
    // that should throw an error when used with await
19✔
884
    read_only(this, '_promise', promise, { hidden: true });
19✔
885
    if (is_function(promise.catch)) {
19✔
886
        // prevent exception on unhandled rejecting when using
19✔
887
        // '>(Promise.reject (new Error "zonk")) in REPL
19✔
888
        promise = promise.catch((err) => {
19✔
889
            internal.rejected = true;
2✔
890
            internal.pending = false;
2✔
891
            internal.reason = err;
2✔
892
        });
19✔
893
    }
19✔
894
    Object.keys(internal).forEach(name => {
19✔
895
        Object.defineProperty(this, `__${name}__`, {
95✔
896
            enumerable: true,
95✔
897
            get: () => internal[name]
95✔
898
        });
95✔
899
    });
19✔
900
    read_only(this, '__promise__', promise);
19✔
901
    // prevent resolving when returned from real promise #153
19✔
902
    this.then = false;
19✔
903
}
19✔
904
// ----------------------------------------------------------------------
1✔
905
QuotedPromise.prototype.then = function(fn) {
1✔
906
    return new QuotedPromise(this.valueOf().then(fn));
6✔
907
};
1✔
908
// ----------------------------------------------------------------------
1✔
909
QuotedPromise.prototype.catch = function(fn) {
1✔
910
    return new QuotedPromise(this.valueOf().catch(fn));
1✔
911
};
1✔
912
// ----------------------------------------------------------------------
1✔
913
QuotedPromise.prototype.valueOf = function() {
1✔
914
    if (!this._promise) {
16!
915
        throw new Error('QuotedPromise: invalid promise created');
×
916
    }
×
917
    return this._promise;
16✔
918
};
1✔
919
// ----------------------------------------------------------------------
1✔
920
QuotedPromise.prototype.toString = function() {
1✔
921
    if (this.__pending__) {
5✔
922
        return QuotedPromise.pending_str;
3✔
923
    }
3✔
924
    if (this.__rejected__) {
5✔
925
        return QuotedPromise.rejected_str;
1✔
926
    }
1✔
927
    return `#<js-promise resolved (${this.__type__})>`;
1✔
928
};
1✔
929
QuotedPromise.pending_str = '#<js-promise (pending)>';
1✔
930
QuotedPromise.rejected_str = '#<js-promise (rejected)>';
1✔
931
// ----------------------------------------------------------------------
1✔
932
// wrapper over Promise.all that ignore quoted promises
1✔
933
// ----------------------------------------------------------------------
1✔
934
function promise_all(arg) {
1,913✔
935
    if (Array.isArray(arg)) {
1,913✔
936
        return Promise.all(escape_quoted_promises(arg))
1,913✔
937
            .then(unescape_quoted_promises);
1,913✔
938
    }
1,913✔
939
    return arg;
×
940
}
1,913✔
941
// ----------------------------------------------------------------------
1✔
942
function escape_quoted_promises(array) {
1,913✔
943
    // using loops for performance
1,913✔
944
    var escaped = new Array(array.length), i = array.length;
1,913✔
945
    while (i--) {
1,913✔
946
        const value = array[i];
2,138✔
947
        if (value instanceof QuotedPromise) {
2,138✔
948
            escaped[i] = new Value(value);
1✔
949
        } else {
2,138✔
950
            escaped[i] = value;
2,137✔
951
        }
2,137✔
952
    }
2,138✔
953
    return escaped;
1,913✔
954
}
1,913✔
955
// ----------------------------------------------------------------------
1✔
956
function unescape_quoted_promises(array) {
1,912✔
957
    var unescaped = new Array(array.length), i = array.length;
1,912✔
958
    while (i--) {
1,912✔
959
        var value = array[i];
2,136✔
960
        if (value instanceof Value) {
2,136✔
961
            unescaped[i] = value.valueOf();
1✔
962
        } else {
2,136✔
963
            unescaped[i] = value;
2,135✔
964
        }
2,135✔
965
    }
2,136✔
966
    return unescaped;
1,912✔
967
}
1,912✔
968
// ----------------------------------------------------------------------
1✔
969
// :: Parser macros transformers
1✔
970
// ----------------------------------------------------------------------
1✔
971
var specials = {
1✔
972
    LITERAL: Symbol.for('literal'),
1✔
973
    SPLICE: Symbol.for('splice'),
1✔
974
    SYMBOL: Symbol.for('symbol'),
1✔
975
    names: function() {
1✔
976
        return Object.keys(this.__list__);
97,164✔
977
    },
1✔
978
    type: function(name) {
1✔
979
        try {
2,061✔
980
            return this.get(name).type;
2,061✔
981
        } catch(e) {
2,061!
982
            console.log({name});
×
983
            console.log(e);
×
984
            return null;
×
985
        }
×
986
    },
1✔
987
    get: function(name) {
1✔
988
        return this.__list__[name];
3,092✔
989
    },
1✔
990
    // events are used in Lexer dynamic rules
1✔
991
    off: function(name, fn = null) {
1✔
992
        if (Array.isArray(name)) {
×
993
            name.forEach(name => this.off(name, fn));
×
994
        } else if (fn === null) {
×
995
            delete this.__events__[name];
×
996
        } else {
×
997
            this.__events__ = this.__events__.filter(test => test !== fn);
×
998
        }
×
999
    },
1✔
1000
    on: function(name, fn) {
1✔
1001
        if (Array.isArray(name)) {
3✔
1002
            name.forEach(name => this.on(name, fn));
1✔
1003
        } else if (!this.__events__[name]) {
3✔
1004
            this.__events__[name] = [fn];
2✔
1005
        } else {
2!
1006
            this.__events__[name].push(fn);
×
1007
        }
×
1008
    },
1✔
1009
    trigger: function(name, ...args) {
1✔
1010
        if (this.__events__[name]) {
35✔
1011
            this.__events__[name].forEach(fn => fn(...args));
30✔
1012
        }
30✔
1013
    },
1✔
1014
    remove: function(name) {
1✔
1015
        delete this.__list__[name];
7✔
1016
        this.trigger('remove');
7✔
1017
    },
1✔
1018
    append: function(name, value, type) {
1✔
1019
        this.__list__[name] = {
28✔
1020
            seq: name,
28✔
1021
            symbol: value,
28✔
1022
            type
28✔
1023
        };
28✔
1024
        this.trigger('append');
28✔
1025
    },
1✔
1026
    __events__: {},
1✔
1027
    __list__: {}
1✔
1028
};
1✔
1029
function is_special(token) {
96,820✔
1030
    return specials.names().includes(token);
96,820✔
1031
}
96,820✔
1032
function is_builtin(token) {
1,031✔
1033
    return specials.__builtins__.includes(token);
1,031✔
1034
}
1,031✔
1035
function is_literal(special) {
1,030✔
1036
    return specials.type(special) === specials.LITERAL;
1,030✔
1037
}
1,030✔
1038
function is_symbol_extension(special) {
1,031✔
1039
    return specials.type(special) === specials.SYMBOL;
1,031✔
1040
}
1,031✔
1041
// ----------------------------------------------------------------------
1✔
1042
const defined_specials = [
1✔
1043
    ["'", new LSymbol('quote'), specials.LITERAL],
1✔
1044
    ['`', new LSymbol('quasiquote'), specials.LITERAL],
1✔
1045
    [',@', new LSymbol('unquote-splicing'), specials.LITERAL],
1✔
1046
    [',', new LSymbol('unquote'), specials.LITERAL],
1✔
1047
    ["'>", new LSymbol('quote-promise'), specials.LITERAL]
1✔
1048
];
1✔
1049

1✔
1050
const builtins = defined_specials.map(arr => arr[0]);
1✔
1051
Object.freeze(builtins);
1✔
1052

1✔
1053
Object.defineProperty(specials, '__builtins__', {
1✔
1054
    writable: false,
1✔
1055
    enumerable: true,
1✔
1056
    value: builtins
1✔
1057
});
1✔
1058
defined_specials.forEach(([seq, symbol, type]) => {
1✔
1059
    specials.append(seq, symbol, type);
5✔
1060
});
1✔
1061
// ----------------------------------------------------------------------
1✔
1062
// :: Finite State Machine based incremental Lexer
1✔
1063
// ----------------------------------------------------------------------
1✔
1064
/* Lexer debugger
1✔
1065
   var DEBUG = false;
1✔
1066
   function log(...args) {
1✔
1067
   if (DEBUG) {
1✔
1068
   console.log(...args);
1✔
1069
   }
1✔
1070
   }
1✔
1071
*/
1✔
1072
class Lexer {
1✔
1073
    constructor(input, { whitespace = false } = {}) {
1✔
1074
        read_only(this, '__input__', input);
120✔
1075
        var internals = {};
120✔
1076
        // hide internals from introspection
120✔
1077
        [
120✔
1078
            '_i', '_whitespace', '_col', '_newline', '_line',
120✔
1079
            '_state', '_next', '_token', '_prev_char'
120✔
1080
        ].forEach(name => {
120✔
1081
            Object.defineProperty(this, name, {
1,080✔
1082
                configurable: false,
1,080✔
1083
                enumerable: false,
1,080✔
1084
                get() {
1,080✔
1085
                    return internals[name];
1,862,793✔
1086
                },
1,080✔
1087
                set(value) {
1,080✔
1088
                    internals[name] = value;
362,013✔
1089
                }
362,013✔
1090
            });
1,080✔
1091
        });
120✔
1092
        this._whitespace = whitespace;
120✔
1093
        this._i = this._line = this._col = this._newline = 0;
120✔
1094
        this._state = this._next = this._token = null;
120✔
1095
        this._prev_char = '';
120✔
1096
    }
120✔
1097
    get(name) {
1✔
1098
        return this.__internal[name];
×
1099
    }
×
1100
    set(name, value) {
1✔
1101
        this.__internal[name] = value;
×
1102
    }
×
1103
    token(meta = false) {
1✔
1104
        if (meta) {
138,825✔
1105
            let line = this._line;
138,806✔
1106
            if (this._whitespace && this._token === '\n') {
138,806✔
1107
                --line;
86✔
1108
            }
86✔
1109
            return {
138,806✔
1110
                token: this._token,
138,806✔
1111
                col: this._col,
138,806✔
1112
                offset: this._i,
138,806✔
1113
                line
138,806✔
1114
            };
138,806✔
1115
        }
138,806✔
1116
        return this._token;
19✔
1117
    }
138,825✔
1118
    peek(meta = false) {
1✔
1119
        if (this._i >= this.__input__.length) {
90,860✔
1120
            return eof;
86✔
1121
        }
86✔
1122
        if (this._token) {
90,860✔
1123
            read_only(this, '__token__', this.token(true));
47,950✔
1124
            return this.token(meta);
47,950✔
1125
        }
47,950✔
1126
        var found = this.next_token();
42,824✔
1127
        if (found) {
90,860✔
1128
            this._token = this.__input__.substring(this._i, this._next);
42,815✔
1129
            if (!this.__token__) {
42,815✔
1130
                // handle case when accessing __token__ from the syntax extension
110✔
1131
                // (e.g. string interpolation) as the first expression in a REPL
110✔
1132
                read_only(this, '__token__', this.token(true));
110✔
1133
            }
110✔
1134
            return this.token(meta);
42,815✔
1135
        }
42,815✔
1136
        return eof;
7✔
1137
    }
90,860✔
1138
    skip() {
1✔
1139
        if (this._next !== null) {
42,803✔
1140
            this._token = null;
42,803✔
1141
            this._i = this._next;
42,803✔
1142
        }
42,803✔
1143
    }
42,803✔
1144
    read_line() {
1✔
1145
        var len = this.__input__.length;
7✔
1146
        if (this._i >= len) {
7✔
1147
            return eof;
1✔
1148
        }
1✔
1149
        for (let i = this._i; i < len; ++i) {
7✔
1150
            var char = this.__input__[i];
81✔
1151
            if (char === '\n') {
81✔
1152
                const line = this.__input__.substring(this._i, i);
3✔
1153
                this._i = i + 1;
3✔
1154
                ++this._line;
3✔
1155
                return line;
3✔
1156
            }
3✔
1157
        }
81✔
1158
        return this.read_rest();
3✔
1159
    }
7✔
1160
    read_rest() {
1✔
1161
        const i = this._i;
4✔
1162
        this._i = this.__input__.length;
4✔
1163
        return this.__input__.substring(i);
4✔
1164
    }
4✔
1165
    read_string(num) {
1✔
1166
        const len = this.__input__.length;
3✔
1167
        if (this._i >= len) {
3!
1168
            return eof;
×
1169
        }
×
1170
        if (num + this._i >= len) {
3✔
1171
            return this.read_rest();
1✔
1172
        }
1✔
1173
        const end = this._i + num;
2✔
1174
        const result = this.__input__.substring(this._i, end);
2✔
1175
        const found = result.match(/\n/g);
2✔
1176
        if (found) {
3!
1177
            this._line += found.length;
×
1178
        }
×
1179
        this._i = end;
2✔
1180
        return result;
2✔
1181
    }
3✔
1182
    peek_char() {
1✔
1183
        if (this._i >= this.__input__.length) {
237✔
1184
            return eof;
4✔
1185
        }
4✔
1186
        return LCharacter(this.__input__[this._i]);
233✔
1187
    }
237✔
1188
    read_char() {
1✔
1189
        const char = this.peek_char();
207✔
1190
        this.skip_char();
207✔
1191
        return char;
207✔
1192
    }
207✔
1193
    skip_char() {
1✔
1194
        if (this._i < this.__input__.length) {
207✔
1195
            ++this._i;
204✔
1196
            this._token = null;
204✔
1197
        }
204✔
1198
    }
207✔
1199
    match_rule(rule, { prev_char, char, next_char } = {}) {
1✔
1200
        var [ re, prev_re, next_re, state ] = rule;
15,572,017✔
1201
        if (rule.length !== 5) {
15,572,017!
1202
            throw new Error(`Lexer: Invalid rule of length ${rule.length}`);
×
1203
        }
×
1204
        if (is_string(re)) {
15,572,017✔
1205
            if (re !== char) {
9,523,202✔
1206
                return false;
9,330,178✔
1207
            }
9,330,178✔
1208
        } else if (!char.match(re)) {
15,572,017✔
1209
            return false;
4,758,468✔
1210
        }
4,758,468✔
1211
        if (!match_or_null(prev_re, prev_char)) {
15,572,017✔
1212
            return false;
682,802✔
1213
        }
682,802✔
1214
        if (!match_or_null(next_re, next_char)) {
15,572,017✔
1215
            return false;
518,580✔
1216
        }
518,580✔
1217
        if (state !== this._state) {
15,572,017✔
1218
            return false;
224,347✔
1219
        }
224,347✔
1220
        return true;
57,642✔
1221
    }
15,572,017✔
1222
    next_token() {
1✔
1223
        if (this._i >= this.__input__.length) {
42,824!
1224
            return false;
×
1225
        }
×
1226
        let start = true;
42,824✔
1227
        loop:
42,824✔
1228
        for (let i = this._i, len = this.__input__.length; i < len; ++i) {
42,824✔
1229
            const char = this.__input__[i];
215,822✔
1230
            const prev_char = this.__input__[i - 1] || '';
215,822✔
1231
            const next_char = this.__input__[i + 1] || '';
215,822✔
1232
            if (char === '\n') {
215,822✔
1233
                ++this._line;
6,288✔
1234
                const newline = this._newline;
6,288✔
1235
                if (this._state === null) {
6,288✔
1236
                    // keep beginning of the newline to calculate col
6,233✔
1237
                    // we don't want to check inside the token (e.g. strings)
6,233✔
1238
                    this._newline = i + 1;
6,233✔
1239
                }
6,233✔
1240
                if (this._whitespace && this._state === null) {
6,288✔
1241
                    this._next = i + 1;
86✔
1242
                    this._col = this._i - newline;
86✔
1243
                    return true;
86✔
1244
                }
86✔
1245
            }
6,288✔
1246
            // skip leading spaces
215,736✔
1247
            if (start && this._state === null && char.match(/\s/)) {
215,822✔
1248
                if (this._whitespace) {
76,838✔
1249
                    if (!next_char.match(/\s/)) {
539✔
1250
                        this._next = i + 1;
434✔
1251
                        this._col = this._i - this._newline;
434✔
1252
                        return true;
434✔
1253
                    } else {
539✔
1254
                        continue;
105✔
1255
                    }
105✔
1256
                } else {
76,838✔
1257
                    this._i = i + 1;
76,299✔
1258
                    continue;
76,299✔
1259
                }
76,299✔
1260
            }
76,838✔
1261
            start = false;
138,898✔
1262
            for (let rule of Lexer.rules) {
215,822✔
1263
                if (this.match_rule(rule, { prev_char, char, next_char })) {
15,572,017✔
1264
                    // change state to null if end of the token
57,642✔
1265
                    var next_state = rule[rule.length - 1];
57,642✔
1266
                    this._state = next_state;
57,642✔
1267
                    if (this._state === null) {
57,642✔
1268
                        this._next = i + 1;
42,295✔
1269
                        this._col = this._i - this._newline;
42,295✔
1270
                        return true;
42,295✔
1271
                    }
42,295✔
1272
                    // token is activated
15,347✔
1273
                    continue loop;
15,347✔
1274
                }
15,347✔
1275
            }
15,572,017✔
1276
            if (this._state !== null) {
81,256✔
1277
                // collect char in token
81,256✔
1278
                continue loop;
81,256✔
1279
            }
81,256✔
1280
            // no rule for token
×
1281
            const line = this.__input__.split('\n')[this._line];
×
1282
            throw new Error(`Invalid Syntax at line ${this._line + 1}\n${line}`);
×
1283
        }
×
1284
        // we need to ignore comments because they can be the last expression in code
9✔
1285
        // without extra newline at the end
9✔
1286
        if (![null, Lexer.comment].includes(this._state)) {
42,824✔
1287
            const line_number = this.__input__.substring(0, this._newline).match(/\n/g)?.length ?? 0;
2!
1288
            const line = this.__input__.substring(this._newline);
2✔
1289
            if (this.__input__[this._i] === '#') {
2✔
1290
                const expr = this.__input__.substring(this._i).replace(/^([^\s()\[\]]+).*/, '$1');
1✔
1291
                throw new Error(`Invalid Syntax at line ${line_number + 1}: invalid token ${expr}`);
1✔
1292
            }
1✔
1293
            throw new Unterminated(`Invalid Syntax at line ${line_number + 1}: Unterminated expression ${line}`);
1✔
1294
        }
1✔
1295
    }
42,824✔
1296
}
1✔
1297
// ----------------------------------------------------------------------
1✔
1298
// TODO: cache the rules creation or whole list
1✔
1299
// ----------------------------------------------------------------------
1✔
1300
// State rule for literal symbol
1✔
1301
// ----------------------------------------------------------------------
1✔
1302
Lexer.literal_rule = function literal_rule(string, symbol, p_re = null, n_re = null) {
1✔
1303
    if (string.length === 0) {
445!
1304
        throw new Error('Lexer: invalid literal rule');
×
1305
    }
×
1306
    if (string.length === 1) {
445✔
1307
        return [[string, p_re, n_re, null, null]];
125✔
1308
    }
125✔
1309
    var rules = [];
320✔
1310
    for (let i = 0, len = string.length; i < len; ++i) {
445✔
1311
        const rule = [];
1,369✔
1312
        rule.push(string[i]);
1,369✔
1313
        rule.push(string[i - 1] || p_re);
1,369✔
1314
        rule.push(string[i + 1] || n_re);
1,369✔
1315
        if (i === 0) {
1,369✔
1316
            rule.push(null);
320✔
1317
            rule.push(symbol);
320✔
1318
        } else if (i === len - 1) {
1,369✔
1319
            rule.push(symbol);
320✔
1320
            rule.push(null);
320✔
1321
        } else {
1,049✔
1322
            rule.push(symbol);
729✔
1323
            rule.push(symbol);
729✔
1324
        }
729✔
1325
        rules.push(rule);
1,369✔
1326
    }
1,369✔
1327
    return rules;
320✔
1328
};
1✔
1329
// ----------------------------------------------------------------------
1✔
1330
Lexer.string = Symbol.for('string');
1✔
1331
Lexer.string_escape = Symbol.for('string_escape');
1✔
1332
Lexer.symbol = Symbol.for('symbol');
1✔
1333
Lexer.comment = Symbol.for('comment');
1✔
1334
Lexer.regex = Symbol.for('regex');
1✔
1335
Lexer.regex_init = Symbol.for('regex_init');
1✔
1336
Lexer.regex_class = Symbol.for('regex_class');
1✔
1337
Lexer.character = Symbol.for('character');
1✔
1338
Lexer.bracket = Symbol.for('bracket');
1✔
1339
Lexer.b_symbol = Symbol.for('b_symbol');
1✔
1340
Lexer.b_symbol_ex = Symbol.for('b_symbol_ex');
1✔
1341

1✔
1342
Lexer.b_comment = Symbol.for('b_comment');
1✔
1343
Lexer.i_comment = Symbol.for('i_comment');
1✔
1344
Lexer.l_datum = Symbol.for('l_datum');
1✔
1345
Lexer.dot = Symbol.for('dot');
1✔
1346
// ----------------------------------------------------------------------
1✔
1347
Lexer.boundary = /^$|[\s()[\]']/;
1✔
1348
// ----------------------------------------------------------------------
1✔
1349
Lexer._rules = [
1✔
1350
    // char_re prev_re next_re from_state to_state
1✔
1351
    // null as to_state mean that is single char token
1✔
1352
    // string
1✔
1353
    [/"/, null, null, Lexer.string, null],
1✔
1354
    [/"/, null, null, null, Lexer.string],
1✔
1355
    [/"/, null, null, Lexer.string_escape, Lexer.string],
1✔
1356
    [/\\/, null, null, Lexer.string, Lexer.string_escape],
1✔
1357
    [/./, /\\/, null, Lexer.string_escape, Lexer.string],
1✔
1358

1✔
1359
    // hash special symbols, lexer don't need to distinguish those
1✔
1360
    // we only care if it's not pick up by vectors literals
1✔
1361
    [/#/, null, /[bdxoei]/i, null, Lexer.symbol],
1✔
1362

1✔
1363
    // characters
1✔
1364
    [/#/, null, /\\/, null, Lexer.character],
1✔
1365
    [/\\/, /#/, /\s/, Lexer.character, Lexer.character],
1✔
1366
    [/\\/, /#/, /[()[\]]/, Lexer.character, Lexer.character],
1✔
1367
    [/\s/, /\\/, null, Lexer.character, null],
1✔
1368
    [/\S/, null, Lexer.boundary, Lexer.character, null],
1✔
1369

1✔
1370
    // regex
1✔
1371
    [/#/, Lexer.boundary, /\//, null, Lexer.regex_init],
1✔
1372
    [/./, /\//, null, Lexer.regex_init, Lexer.regex],
1✔
1373
    [/[ \t]/, null, null, Lexer.regex, Lexer.regex],
1✔
1374
    [/\[/, /[^\\]/, null, Lexer.regex, Lexer.regex_class],
1✔
1375
    [/\]/, /[^\\]/, null, Lexer.regex_class, Lexer.regex],
1✔
1376
    [/[()[\]]/, null, null, Lexer.regex, Lexer.regex],
1✔
1377
    [/\//, /\\/, null, Lexer.regex, Lexer.regex],
1✔
1378
    [/\//, null, Lexer.boundary, Lexer.regex, null],
1✔
1379
    [/[gimyus]/, /\//, Lexer.boundary, Lexer.regex, null],
1✔
1380
    [/[gimyus]/, /\//, /[gimyus]/, Lexer.regex, Lexer.regex],
1✔
1381
    [/[gimyus]/, /[gimyus]/, Lexer.boundary, Lexer.regex, null],
1✔
1382

1✔
1383
    // comment
1✔
1384
    [/;/, /^$|[^#]/, null, null, Lexer.comment],
1✔
1385
    [/\n/, ';', null, Lexer.comment, null],
1✔
1386
    [/[\s\S]/, null, /\n/, Lexer.comment, null],
1✔
1387
    [/\s/, null, null, Lexer.comment, Lexer.comment],
1✔
1388

1✔
1389
    // block comment
1✔
1390
    [/#/, null, /\|/, null, Lexer.b_comment],
1✔
1391
    [/\s/, null, null, Lexer.b_comment, Lexer.b_comment],
1✔
1392
    [/#/, /\|/, null, Lexer.b_comment, null],
1✔
1393

1✔
1394
    // inline commentss
1✔
1395
    [/#/, null, /;/, null, Lexer.i_comment],
1✔
1396
    [/;/, /#/, null, Lexer.i_comment, null],
1✔
1397

1✔
1398
    // datum label
1✔
1399
    [/#/, null, /[0-9]/, null, Lexer.l_datum],
1✔
1400
    [/=/, /[0-9]/, null, Lexer.l_datum, null],
1✔
1401
    [/#/, /[0-9]/, null, Lexer.l_datum, null],
1✔
1402

1✔
1403
    // for dot comma `(a .,b)
1✔
1404
    [/\./, Lexer.boundary, /,/, null, null],
1✔
1405

1✔
1406
    // block symbols
1✔
1407
    [/\|/, null, null, null, Lexer.b_symbol],
1✔
1408
    [/\s/, null, null, Lexer.b_symbol, Lexer.b_symbol],
1✔
1409
    [/\|/, null, Lexer.boundary, Lexer.b_symbol, null],
1✔
1410
    [/\|/, null, /\S/, Lexer.b_symbol, Lexer.b_symbol_ex],
1✔
1411
    [/\S/, null, Lexer.boundary, Lexer.b_symbol_ex, null]
1✔
1412
];
1✔
1413
// ----------------------------------------------------------------------
1✔
1414
Lexer._brackets = [
1✔
1415
    [/[()[\]]/, null, null, null, null]
1✔
1416
];
1✔
1417
// ----------------------------------------------------------------------
1✔
1418
// :: symbols should be matched last
1✔
1419
// ----------------------------------------------------------------------
1✔
1420
Lexer._symbol_rules = [
1✔
1421
    [/\S/, Lexer.boundary, Lexer.boundary, null, null],
1✔
1422
    [/\S/, Lexer.boundary, null, null, Lexer.symbol],
1✔
1423
    [/\S/, null, Lexer.boundary, null, null],
1✔
1424
    [/\S/, null, null, null, Lexer.symbol],
1✔
1425
    [/\S/, null, Lexer.boundary, Lexer.symbol, null]
1✔
1426
];
1✔
1427
// ----------------------------------------------------------------------
1✔
1428
// :: Dynamic getter or Lexer state rules, parser uses this
1✔
1429
// :: so user code can modify Lexer using syntax extensions
1✔
1430
// ----------------------------------------------------------------------
1✔
1431
Lexer._cache = {
1✔
1432
    valid: false,
1✔
1433
    rules: null
1✔
1434
};
1✔
1435
// ----------------------------------------------------------------------
1✔
1436
specials.on(['remove', 'append'], function() {
1✔
1437
    Lexer._cache.valid = false;
30✔
1438
    Lexer._cache.rules = null;
30✔
1439
});
1✔
1440
// those constants need to be add as rules to the Lexer to work with vector literals
1✔
1441
const parsable_contants = {
1✔
1442
    '#null': null,
1✔
1443
    '#void': undefined
1✔
1444
};
1✔
1445
const directives = [
1✔
1446
    '#!fold-case',
1✔
1447
    '#!no-fold-case'
1✔
1448
];
1✔
1449
const hash_literals = ['#t', '#f'];
1✔
1450
// ----------------------------------------------------------------------
1✔
1451
Object.defineProperty(Lexer, 'rules', {
1✔
1452
    get() {
1✔
1453
        if (Lexer._cache.valid) {
138,898✔
1454
            return Lexer._cache.rules;
138,880✔
1455
        }
138,880✔
1456
        const parsable = Object.keys(parsable_contants).concat(directives, hash_literals);
18✔
1457
        const tokens = specials.names().concat(parsable).sort((a, b) => {
18✔
1458
            return b.length - a.length || a.localeCompare(b);
1,631✔
1459
        });
18✔
1460

18✔
1461
        // syntax-extensions tokens that share the same first character after hash
18✔
1462
        // should have same symbol, but because tokens are sorted, the longer
18✔
1463
        // tokens are always process first.
18✔
1464
        const special_rules = tokens.reduce((acc, token) => {
18✔
1465
            let symbol;
445✔
1466
            let after = null;
445✔
1467
            if (token[0] === '#') {
445✔
1468
                if (token.length === 1) {
297✔
1469
                    symbol = Symbol.for(token);
17✔
1470
                } else {
297✔
1471
                    if (hash_literals.includes(token)) {
280✔
1472
                        after = Lexer.boundary;
36✔
1473
                    }
36✔
1474
                    symbol = Symbol.for(token[1]);
280✔
1475
                }
280✔
1476
            } else {
445✔
1477
                symbol = Symbol.for(token);
148✔
1478
            }
148✔
1479
            const rules = Lexer.literal_rule(token, symbol, null, after);
445✔
1480
            return acc.concat(rules);
445✔
1481
        }, []);
18✔
1482

18✔
1483
        Lexer._cache.rules = Lexer._rules.concat(
18✔
1484
            Lexer._brackets,
18✔
1485
            special_rules,
18✔
1486
            Lexer._symbol_rules
18✔
1487
        );
18✔
1488

18✔
1489
        Lexer._cache.valid = true;
18✔
1490
        return Lexer._cache.rules;
18✔
1491
    }
138,898✔
1492
});
1✔
1493
// ----------------------------------------------------------------------
1✔
1494
function match_or_null(re, char) {
2,283,940✔
1495
    if (is_string(re)) {
2,283,940✔
1496
        return re === char;
196,809✔
1497
    }
196,809✔
1498
    return re === null || char.match(re);
2,283,940✔
1499
}
2,283,940✔
1500
// ----------------------------------------------------------------------
1✔
1501
// :: Parser inspired by BiwaScheme
1✔
1502
// :: ref: https://github.com/biwascheme/biwascheme/blob/master/src/system/parser.js
1✔
1503
// ----------------------------------------------------------------------
1✔
1504
class Parser {
1✔
1505
    constructor({ env, meta = false, formatter = multiline_formatter } = {}) {
1✔
1506
        read_only(this, '_formatter', formatter, { hidden: true });
67✔
1507
        read_only(this, '__env__', env);
67✔
1508
        read_only(this, '_meta', meta, { hidden: true });
67✔
1509
        // datum labels
67✔
1510
        read_only(this, '_refs', [], { hidden: true });
67✔
1511
        read_only(this, '_state', {
67✔
1512
            parentheses: 0,
67✔
1513
            line: 0,
67✔
1514
            fold_case: false
67✔
1515
        }, { hidden: true });
67✔
1516
    }
67✔
1517
    prepare(arg) {
1✔
1518
        if (arg instanceof LString) {
67✔
1519
            arg = arg.toString();
39✔
1520
        }
39✔
1521
        this._reset_state();
67✔
1522
        if (arg instanceof Lexer) {
67!
1523
            read_only(this, '__lexer__', arg);
×
1524
        } else {
67✔
1525
            read_only(this, '__lexer__', new Lexer(arg));
67✔
1526
        }
67✔
1527
    }
67✔
1528
    _with_syntax_scope(fn) {
1✔
1529
        // expose parser and change stdin so parser extension can use current-input
166✔
1530
        // to read data from the parser stream #150
166✔
1531
        const internal = get_internal(this.__env__);
166✔
1532
        const stdin = internal.get('stdin');
166✔
1533
        global_env.set('lips',  { ...lips, __parser__: this });
166✔
1534
        internal.set('stdin', new ParserInputPort(this, this.__env__));
166✔
1535
        const cleanup = () => {
166✔
1536
            global_env.set('lips', lips);
166✔
1537
            internal.set('stdin', stdin);
166✔
1538
        }
166✔
1539
        return unpromise(fn(), (result) => {
166✔
1540
            cleanup();
166✔
1541
            return result;
166✔
1542
        }, cleanup);
166✔
1543
    }
166✔
1544
    _reset_state() {
1✔
1545
        Object.assign(this._state, {
67✔
1546
            parentheses: 0,
67✔
1547
            line: 0
67✔
1548
        });
67✔
1549
    }
67✔
1550
    _resolve(name) {
1✔
1551
        return this.__env__ && this.__env__.get(name, { throwError: false });
×
1552
    }
×
1553
    async _peek() {
1✔
1554
        let token;
89,007✔
1555
        while (true) {
89,007✔
1556
            token = this.__lexer__.peek(true);
89,192✔
1557
            if (token === eof) {
89,192✔
1558
                return eof;
39✔
1559
            }
39✔
1560
            if (this._is_comment(token.token)) {
89,192✔
1561
                this.skip();
168✔
1562
                continue;
168✔
1563
            }
168✔
1564
            if (is_directive(token.token)) {
89,192✔
1565
                this.skip();
2✔
1566
                if (token.token === '#!fold-case') {
2✔
1567
                    this._state.fold_case = true;
1✔
1568
                } else if (token.token === '#!no-fold-case') {
1✔
1569
                    this._state.fold_case = false;
1✔
1570
                }
1✔
1571
                continue;
2✔
1572
            }
2✔
1573
            if (token.token === '#;') {
89,192✔
1574
                this.skip();
15✔
1575
                if (this.__lexer__.peek() === eof) {
15!
1576
                    throw new Error('Lexer: syntax error eof found after comment');
×
1577
                }
×
1578
                await this._read_object();
15✔
1579
                continue;
15✔
1580
            }
15✔
1581
            break;
88,967✔
1582
        }
88,967✔
1583
        token = this._formatter(token);
88,967✔
1584
        if (this._state.fold_case) {
89,007✔
1585
            token.token = foldcase_string(token.token);
51✔
1586
        }
51✔
1587
        return token;
88,967✔
1588
    }
89,007✔
1589
    async peek() {
1✔
1590
        const token = this._peek();
×
1591
        if (this._meta) {
×
1592
            return token;
×
1593
        }
×
1594
        return token.token;
×
1595
    }
×
1596
    _reset() {
1✔
1597
        this._refs.length = 0;
585✔
1598
    }
585✔
1599
    skip() {
1✔
1600
        this.__lexer__.skip();
41,209✔
1601
    }
41,209✔
1602
    async _read() {
1✔
1603
        const token = await this._peek();
18,482✔
1604
        this.skip();
18,482✔
1605
        return token;
18,482✔
1606
    }
18,482✔
1607
    async read() {
1✔
1608
        const token = await this.peek();
×
1609
        this.skip();
×
1610
        return token;
×
1611
    }
×
1612
    _match_datum_label(token) {
1✔
1613
        var m = token.token.match(/^#([0-9]+)=$/);
29,151✔
1614
        return m && m[1];
29,151✔
1615
    }
29,151✔
1616
    _match_datum_ref(token) {
1✔
1617
        var m = token.token.match(/^#([0-9]+)#$/);
29,156✔
1618
        return m && m[1];
29,156✔
1619
    }
29,156✔
1620
    _is_open(token) {
1✔
1621
        return ['(', '['].includes(token.token);
29,142✔
1622
    }
29,142✔
1623
    _is_close(token) {
1✔
1624
        return [')', ']'].includes(token.token);
69,444✔
1625
    }
69,444✔
1626
    async _read_list() {
1✔
1627
        let head = nil, prev = head, dot;
10,660✔
1628
        while (true) {
10,660✔
1629
            const token = await this._peek();
39,267✔
1630
            if (token === eof) {
39,267!
1631
                break;
×
1632
            }
×
1633
            if (this._is_close(token)) {
39,267✔
1634
                --this._state.parentheses;
10,657✔
1635
                this.skip();
10,657✔
1636
                break;
10,657✔
1637
            }
10,657✔
1638
            if (token.token === '.' && !is_nil(head)) {
39,267✔
1639
                this.skip();
180✔
1640
                prev.cdr = await this._read_object();
180✔
1641
                dot = true;
180✔
1642
            } else if (dot) {
39,267✔
1643
                throw new Error('Parser: syntax error more than one element after dot');
1✔
1644
            } else {
28,430✔
1645
                const node = await this._read_object();
28,429✔
1646
                const cur = new Pair(node, nil);
28,427✔
1647
                if (is_nil(head)) {
28,429✔
1648
                    head = cur;
10,342✔
1649
                } else {
28,429✔
1650
                    prev.cdr = cur;
18,085✔
1651
                }
18,085✔
1652
                prev = cur;
28,427✔
1653
            }
28,427✔
1654
        }
39,267✔
1655
        return head;
10,657✔
1656
    }
10,660✔
1657
    async _read_value() {
1✔
1658
        let token = await this._read();
18,482✔
1659
        if (token === eof || token.token === eof) {
18,482!
1660
            throw new Error('Parser: Expected token eof found');
×
1661
        }
×
1662
        return parse_argument(token, this._meta);
18,482✔
1663
    }
18,482✔
1664
    _is_comment(token) {
1✔
1665
        return token.match(/^;/) || (token.match(/^#\|/) && token.match(/\|#$/));
89,152!
1666
    }
89,152✔
1667
    evaluate(code) {
1✔
1668
        return evaluate(code, { env: this.__env__, error: (e) => {
124✔
1669
            throw e;
×
1670
        } });
124✔
1671
    }
124✔
1672
    // public API that handle R7RS datum labels
1✔
1673
    async read_object() {
1✔
1674
        this._reset();
585✔
1675
        var object = await this._read_object();
585✔
1676
        if (object instanceof DatumReference) {
585!
1677
            object = object.valueOf();
×
1678
        }
×
1679
        if (this._refs.length) {
585✔
1680
            return unpromise(this._resolve_object(object), object => {
1✔
1681
                if (is_pair(object)) {
1✔
1682
                    // mark cycles on parser level
1✔
1683
                    object.mark_cycles();
1✔
1684
                }
1✔
1685
                return object;
1✔
1686
            });
1✔
1687
        }
1✔
1688
        return object;
577✔
1689
    }
585✔
1690
    balanced() {
1✔
1691
        return this._state.parentheses === 0;
553✔
1692
    }
553✔
1693
    _ballancing_error(expr, prev) {
1✔
1694
        const count = this._state.parentheses;
2✔
1695
        let e;
2✔
1696
        if (count < 0) {
2✔
1697
            e = new Error('Parser: unexpected parenthesis');
2✔
1698
            if (prev) {
2✔
1699
                e.__code__ = [prev.toString() + ')'];
1✔
1700
            } else {
1✔
1701
                e.__code__ = [')'];
1✔
1702
            }
1✔
1703
        } else {
2!
1704
            e = new Error('Parser: expected parenthesis but eof found');
×
1705
            const re = new RegExp(`\\){${count}}$`);
×
1706
            e.__code__ = [expr.toString().replace(re, '')];
×
1707
        }
×
1708
        this._agument_exception(e);
2✔
1709
        throw e;
2✔
1710
    }
2✔
1711
    _agument_exception(e) {
1✔
1712
        const token = this.__lexer__.__token__;
4✔
1713
        if ('col' in token) {
4✔
1714
            const { col, offset, line } = token;
4✔
1715
            e.message += ` at line ${line + 1} and column ${col + 1}`;
4✔
1716
            read_only(e, '__col__', col);
4✔
1717
            read_only(e, '__offset__', offset);
4✔
1718
            read_only(e, '__line__', line);
4✔
1719
        }
4✔
1720
    }
4✔
1721
    // TODO: Cover This function (array and object branch)
1✔
1722
    async _resolve_object(object) {
1✔
1723
        if (Array.isArray(object)) {
1!
1724
            return object.map(item => this._resolve_object(item));
×
1725
        }
×
1726
        if (is_plain_object(object)) {
1!
1727
            var result = {};
×
1728
            Object.keys(object).forEach(key => {
×
1729
                result[key] = this._resolve_object(object[key]);
×
1730
            });
×
1731
            return result;
×
1732
        }
×
1733
        if (is_pair(object)) {
1✔
1734
            return this._resolve_pair(object);
1✔
1735
        }
1✔
1736
        return object;
×
1737
    }
1✔
1738
    async _resolve_pair(pair) {
1✔
1739
        if (is_pair(pair)) {
254✔
1740
            if (pair.car instanceof DatumReference) {
129✔
1741
                pair.car = await pair.car.valueOf();
4✔
1742
            } else {
129✔
1743
                this._resolve_pair(pair.car);
125✔
1744
            }
125✔
1745
            if (pair.cdr instanceof DatumReference) {
129✔
1746
                pair.cdr = await pair.cdr.valueOf();
1✔
1747
            } else {
129✔
1748
                this._resolve_pair(pair.cdr);
128✔
1749
            }
128✔
1750
        }
129✔
1751
        return pair;
254✔
1752
    }
254✔
1753
    get_line() {
1✔
1754
        return this._state.line;
×
1755
    }
×
1756
    async _read_object() {
1✔
1757
        const token = await this._peek();
30,227✔
1758
        if (token === eof) {
30,227✔
1759
            return token;
39✔
1760
        }
39✔
1761
        this._state.line = this.__lexer__.__token__.line;
30,187✔
1762
        if (is_special(token.token)) {
30,227✔
1763
            // Built-in parser extensions are mapping short symbols to longer symbols
1,031✔
1764
            // that can be function or macro. Parser doesn't care
1,031✔
1765
            // if it's not built-in and the extension can be macro or function.
1,031✔
1766
            // FUNCTION: when it's used, it gets arguments like FEXPR and the
1,031✔
1767
            // result is returned by parser as is the macro.
1,031✔
1768
            // MACRO: if macro is used, then it is evaluated in place and the
1,031✔
1769
            // result is returned by parser and it is quoted.
1,031✔
1770
            const special = specials.get(token.token);
1,031✔
1771
            const builtin = is_builtin(token.token);
1,031✔
1772
            this.skip();
1,031✔
1773
            let expr, extension;
1,031✔
1774
            const is_symbol = is_symbol_extension(token.token);
1,031✔
1775
            const was_close_paren = this._is_close(await this._peek());
1,031✔
1776
            const object = is_symbol ? undefined : await this._read_object();
1,031✔
1777
            if (object === eof) {
1,031!
1778
                throw new Unterminated('Expecting expression eof found');
×
1779
            }
×
1780
            if (!builtin) {
1,031✔
1781
                extension = this.__env__.get(special.symbol);
166✔
1782
                if (typeof extension === 'function') {
166✔
1783
                    let args;
42✔
1784
                    if (is_literal(token.token)) {
42✔
1785
                        args = [object];
4✔
1786
                    } else if (is_nil(object)) {
42!
1787
                        args = [];
×
1788
                    } else if (is_pair(object)) {
38✔
1789
                        args = object.to_array(false);
20✔
1790
                    }
20✔
1791
                    if (args || is_symbol) {
42✔
1792
                        return this._with_syntax_scope(() => {
42✔
1793
                            return call_function(extension, is_symbol ? [] : args, {
42✔
1794
                                env: this.__env__,
42✔
1795
                                dynamic_env: this.__env__,
42✔
1796
                                use_dynamic: false
42✔
1797
                            });
42✔
1798
                        });
42✔
1799
                    }
42✔
1800
                    const e = new Error('Parse Error: Invalid parser extension ' +
×
1801
                                        `invocation ${special.symbol}`);
×
1802
                    this._agument_exception(e);
×
1803
                    throw e;
×
1804
                }
×
1805
            }
166✔
1806
            if (is_literal(token.token)) {
1,031✔
1807
                if (was_close_paren) {
866✔
1808
                    const e = new Error('Parse Error: expecting datum');
2✔
1809
                    this._agument_exception(e);
2✔
1810
                    throw e
2✔
1811
                }
2✔
1812
                expr = new Pair(
864✔
1813
                    special.symbol,
864✔
1814
                    new Pair(
864✔
1815
                        object,
864✔
1816
                        nil
864✔
1817
                    )
864✔
1818
                );
864✔
1819
            } else {
1,031✔
1820
                expr = new Pair(
122✔
1821
                    special.symbol,
122✔
1822
                    object
122✔
1823
                );
122✔
1824
            }
122✔
1825
            // Built-in parser extensions just expand into lists like 'x ==> (quote x)
986✔
1826
            if (builtin) {
1,031✔
1827
                return expr;
862✔
1828
            }
862✔
1829
            // Evaluate parser extension at parse time
124✔
1830
            if (extension instanceof Macro) {
124✔
1831
                var result = await this._with_syntax_scope(() => {
124✔
1832
                    return this.evaluate(expr);
124✔
1833
                });
124✔
1834
                // We need literal quotes to make that macro's return pairs works
124✔
1835
                // because after the parser returns the value it will be evaluated again
124✔
1836
                // by the interpreter, so we create quoted expressions.
124✔
1837
                if (is_pair(result) || result instanceof LSymbol) {
124✔
1838
                    return Pair.fromArray([LSymbol('quote'), result]);
3✔
1839
                }
3✔
1840
                return result;
121✔
1841
            } else {
1,031!
1842
                const e = new Error('Parse Error: invalid parser extension: ' +
×
1843
                                    special.symbol);
×
1844
                this._agument_exception(e);
×
1845
                throw e;
×
1846
            }
×
1847
        }
1,031✔
1848
        const ref = this._match_datum_ref(token);
29,156✔
1849
        if (ref !== null) {
30,227✔
1850
            this.skip();
5✔
1851
            if (this._refs[ref]) {
5✔
1852
                return new DatumReference(ref, this._refs[ref]);
5✔
1853
            }
5✔
1854
            const e = new Error(`Parse Error: invalid datum label #${ref}#`);
×
1855
            this._agument_exception(e);
×
1856
            throw e;
×
1857
        }
×
1858
        const ref_label = this._match_datum_label(token);
29,151✔
1859
        if (ref_label !== null) {
30,227✔
1860
            this.skip();
5✔
1861
            this._refs[ref_label] = this._read_object();
5✔
1862
            return this._refs[ref_label];
5✔
1863
        } else if (this._is_close(token)) {
30,227✔
1864
            --this._state.parentheses;
4✔
1865
            this.skip();
4✔
1866
            // invalid state, we don't need to return anything
4✔
1867
        } else if (this._is_open(token)) {
29,146✔
1868
            ++this._state.parentheses;
10,660✔
1869
            this.skip();
10,660✔
1870
            return this._read_list();
10,660✔
1871
        } else {
29,142✔
1872
            return this._read_value();
18,482✔
1873
        }
18,482✔
1874
    }
30,227✔
1875
}
1✔
1876
class Unterminated extends Error { }
1✔
1877
Parser.Unterminated = Unterminated;
1✔
1878
// ----------------------------------------------------------------------
1✔
1879
// :: Parser helper that handles circular list structures
1✔
1880
// :: using datum labels
1✔
1881
// ----------------------------------------------------------------------
1✔
1882
class DatumReference {
1✔
1883
    constructor(name, data) {
1✔
1884
        this.name = name;
5✔
1885
        this.data = data;
5✔
1886
    }
5✔
1887
    valueOf() {
1✔
1888
        return this.data;
5✔
1889
    }
5✔
1890
}
1✔
1891
// ----------------------------------------------------------------------
1✔
1892
// :: Tokens are the array of strings from tokenizer
1✔
1893
// :: the return value is an array of lips code created out of Pair class.
1✔
1894
// :: env is needed for parser extensions that will invoke the function
1✔
1895
// :: or macro assigned to symbol, this function is async because
1✔
1896
// :: it evaluates the code, from parser extensions, that may return a promise.
1✔
1897
// ----------------------------------------------------------------------
1✔
1898
async function* _parse(arg, env) {
47✔
1899
    if (!env) {
47✔
1900
        if (global_env) {
44✔
1901
            env = global_env.get('**interaction-environment**', {
44✔
1902
                throwError: false
44✔
1903
            });
44✔
1904
        } else {
44!
1905
            env = user_env;
×
1906
        }
×
1907
    }
44✔
1908
    let parser;
46✔
1909
    if (arg instanceof Parser) {
47✔
1910
        parser = arg;
2✔
1911
    } else {
47✔
1912
        parser = new Parser({ env });
44✔
1913
        parser.prepare(arg);
44✔
1914
    }
44✔
1915
    let prev;
46✔
1916
    while (true) {
47✔
1917
        const expr = await parser.read_object();
560✔
1918
        if (!parser.balanced()) {
560✔
1919
            parser._ballancing_error(expr, prev);
2✔
1920
        }
2✔
1921
        if (expr === eof) {
560✔
1922
            break;
37✔
1923
        }
37✔
1924
        prev = expr;
514✔
1925
        yield expr;
514✔
1926
    }
514✔
1927
}
47✔
1928
// ----------------------------------------------------------------------
1✔
1929
function unpromise(value, fn = x => x, error = null) {
18,159,302✔
1930
    if (is_promise(value)) {
18,159,302✔
1931
        var ret = value.then(fn);
16,906✔
1932
        if (error === null) {
16,906✔
1933
            return ret;
7,869✔
1934
        } else {
16,906✔
1935
            return ret.catch(error);
9,037✔
1936
        }
9,037✔
1937
    }
16,906✔
1938
    if (value instanceof Array) {
18,159,302✔
1939
        return unpromise_array(value, fn, error);
3,490,075✔
1940
    }
3,490,075✔
1941
    if (is_plain_object(value)) {
18,159,302✔
1942
        return unpromise_object(value, fn, error);
6,857✔
1943
    }
6,857✔
1944
    return fn(value);
14,645,464✔
1945
}
18,159,302✔
1946
// ----------------------------------------------------------------------
1✔
1947
function unpromise_array(array, fn, error) {
3,490,075✔
1948
    if (array.find(is_promise)) {
3,490,075✔
1949
        return unpromise(promise_all(array), (arr) => {
10✔
1950
            if (Object.isFrozen(array)) {
10!
1951
                Object.freeze(arr);
×
1952
            }
×
1953
            return fn(arr);
10✔
1954
        }, error);
10✔
1955
    }
10✔
1956
    return fn(array);
3,490,065✔
1957
}
3,490,075✔
1958
// ----------------------------------------------------------------------
1✔
1959
function unpromise_object(object, fn, error) {
6,857✔
1960
    const keys = Object.keys(object);
6,857✔
1961
    const values = [], anyPromise = [];
6,857✔
1962
    let i = keys.length;
6,857✔
1963
    while (i--) {
6,857✔
1964
        const key = keys[i];
68,019✔
1965
        const value = object[key];
68,019✔
1966
        values[i] = value;
68,019✔
1967
        if (is_promise(value)) {
68,019✔
1968
            anyPromise.push(value);
1✔
1969
        }
1✔
1970
    }
68,019✔
1971
    if (anyPromise.length) {
6,857✔
1972
        return unpromise(promise_all(values), (values) => {
1✔
1973
            const result = {};
1✔
1974
            values.forEach((value, i) => {
1✔
1975
                const key = keys[i];
2✔
1976
                result[key] = value;
2✔
1977
            });
1✔
1978
            if (Object.isFrozen(object)) {
1!
1979
                Object.freeze(result);
×
1980
            }
×
1981
            return result;
1✔
1982
        }, error);
1✔
1983
    }
1✔
1984
    return fn(object);
6,856✔
1985
}
6,857✔
1986
// ----------------------------------------------------------------------
1✔
1987
function read_only(object, property, value, { hidden = false } = {}) {
96,458✔
1988
    Object.defineProperty(object, property, {
96,458✔
1989
        value,
96,458✔
1990
        writable: false,
96,458✔
1991
        configurable: true,
96,458✔
1992
        enumerable: !hidden
96,458✔
1993
    });
96,458✔
1994
}
96,458✔
1995
// ----------------------------------------------------------------------
1✔
1996
// :: Function similar to Array.from that work on async iterators
1✔
1997
// ----------------------------------------------------------------------
1✔
1998
async function uniterate_async(object) {
40✔
1999
    const result = [];
40✔
2000
    for await (let item of object) {
40✔
2001
        result.push(item);
32✔
2002
    }
32✔
2003
    return result;
32✔
2004
}
40✔
2005
// ----------------------------------------------------------------------
1✔
2006
// :: Function that return matcher function that match any value
1✔
2007
// :: the function is used in find Scheme function to find an item
1✔
2008
// :: in the list
1✔
2009
// ----------------------------------------------------------------------
1✔
2010
function matcher(name, arg) {
29✔
2011
    if (arg instanceof RegExp) {
29✔
2012
        return x => String(x).match(arg);
3✔
2013
    } else if (is_function(arg)) {
29✔
2014
        return arg;
9✔
2015
    } else if (arg instanceof LNumber) {
26✔
2016
        return x => LNumber(x).cmp(arg) === 0;
5✔
2017
    } else if (arg instanceof LString) {
17✔
2018
        const string = arg.__string__;
3✔
2019
        return x => LString.is(x, string);
3✔
2020
    } else if (arg instanceof LSymbol) {
12✔
2021
        const name = arg.__name__;
3✔
2022
        return x => LSymbol.is(x, name);
3✔
2023
    } else if (arg instanceof LCharacter) {
9✔
2024
        const char = arg.__char__;
3✔
2025
        return x => LCharacter.is(x, char);
3✔
2026
    }
3✔
2027
    return x => arg === x;
3✔
2028
}
29✔
2029
// ----------------------------------------------------------------------
1✔
2030
// :: Documentation decorator to LIPS functions if lines starts with :
1✔
2031
// :: they are ignored (not trimmed) otherwise it trims so
1✔
2032
// :: so you can have indent in source code
1✔
2033
// ----------------------------------------------------------------------
1✔
2034
function doc(name, fn, doc, dump) {
5,699,813✔
2035
    if (typeof name !== 'string') {
5,699,813✔
2036
        fn = arguments[0];
74,809✔
2037
        doc = arguments[1];
74,809✔
2038
        dump = arguments[2];
74,809✔
2039
        name = null;
74,809✔
2040
    }
74,809✔
2041
    if (doc) {
5,699,813✔
2042
        if (dump) {
5,625,401✔
2043
            fn.__doc__ = doc;
364✔
2044
        } else {
5,625,401✔
2045
            fn.__doc__ = trim_lines(doc);
5,625,037✔
2046
        }
5,625,037✔
2047
    }
5,625,401✔
2048
    if (name) {
5,699,813✔
2049
        fn.__name__ = name;
5,625,004✔
2050
    } else if (fn.name && !is_lambda(fn)) {
5,699,813✔
2051
        fn.__name__ = fn.name;
7✔
2052
    }
7✔
2053
    return fn;
5,699,813✔
2054
}
5,699,813✔
2055
// ----------------------------------------------------------------------
1✔
2056
function trim_lines(string) {
5,625,044✔
2057
    return string.split('\n').map(line => {
5,625,044✔
2058
        return line.trim();
22,500,175✔
2059
    }).join('\n');
5,625,044✔
2060
}
5,625,044✔
2061
// ----------------------------------------------------------------------
1✔
2062
// return last S-Expression
1✔
2063
// @param tokens - array of tokens (objects from tokenizer or strings)
1✔
2064
// @param sexp - number of expression to look behind
1✔
2065
// ----------------------------------------------------------------------
1✔
2066
function previousSexp(tokens, sexp = 1) {
467✔
2067
    var i = tokens.length;
467✔
2068
    if (sexp <= 0) {
467!
2069
        throw Error(`previousSexp: Invalid argument sexp = ${sexp}`);
×
2070
    }
×
2071
    outer: while (sexp-- && i >= 0) {
467✔
2072
        var count = 1;
467✔
2073
        while (count > 0) {
467✔
2074
            var token = tokens[--i];
3,870✔
2075
            if (!token) {
3,870✔
2076
                break outer;
5✔
2077
            }
5✔
2078
            if (token === '(' || token.token === '(') {
3,870✔
2079
                count--;
984✔
2080
            } else if (token === ')' || token.token === ')') {
3,870✔
2081
                count++;
522✔
2082
            }
522✔
2083
        }
3,870✔
2084
        i--;
462✔
2085
    }
462✔
2086
    return tokens.slice(i + 1);
467✔
2087
}
467✔
2088
// ----------------------------------------------------------------------
1✔
2089
// :: Find the number of spaces in line
1✔
2090
// ----------------------------------------------------------------------
1✔
2091
function lineIndent(tokens) {
71✔
2092
    if (!tokens || !tokens.length) {
71!
2093
        return 0;
×
2094
    }
×
2095
    var i = tokens.length;
71✔
2096
    if (tokens[i - 1].token === '\n') {
71!
2097
        return 0;
×
2098
    }
×
2099
    while (--i) {
71✔
2100
        if (tokens[i].token === '\n') {
515✔
2101
            var token = (tokens[i + 1] || {}).token;
52!
2102
            if (token) {
52✔
2103
                return token.length;
49✔
2104
            }
49✔
2105
        }
52✔
2106
    }
515✔
2107
    return 0;
22✔
2108
}
71✔
2109
// ----------------------------------------------------------------------
1✔
2110
// :: Token based pattern matching (used by formatter)
1✔
2111
// ----------------------------------------------------------------------
1✔
2112
/*
1✔
2113
  Function nested_pattern(pattern) {
1✔
2114
  return pattern instanceof Array ||
1✔
2115
  pattern instanceof Pattern;
1✔
2116
  }
1✔
2117
*/
1✔
2118
// ----------------------------------------------------------------------
1✔
2119
function match(pattern, input) {
7,501✔
2120
    return inner_match(pattern, input) === input.length;
7,501✔
2121
    function inner_match(pattern, input) {
7,501✔
2122
        /*
11,001✔
2123
          function empty_match() {
11,001✔
2124
          if (p <= 0 && i <= 0) {
11,001✔
2125
          return false;
11,001✔
2126
          }
11,001✔
2127
          var prev_pattern = pattern[p - 1];
11,001✔
2128
          if (!nested_pattern(prev_pattern)) {
11,001✔
2129
          prev_pattern = [prev_pattern];
11,001✔
2130
          }
11,001✔
2131
          var next_pattern = pattern[p + 1];
11,001✔
2132
          if (next_pattern && !nested_pattern(next_pattern)) {
11,001✔
2133
          next_pattern = [next_pattern];
11,001✔
2134
          }
11,001✔
2135
          return match(prev_pattern, [input[i - 1]]) &&
11,001✔
2136
          (!next_pattern || match(next_pattern, [input[i]]));
11,001✔
2137
          }
11,001✔
2138
        */
11,001✔
2139
        function get_first_match(patterns, input) {
11,001✔
2140
            for (let p of patterns) {
1,910✔
2141
                const m = inner_match(p, input);
1,934✔
2142
                if (m !== -1) {
1,934✔
2143
                    return m;
729✔
2144
                }
729✔
2145
            }
1,934✔
2146
            return -1;
1,181✔
2147
        }
1,910✔
2148
        function not_symbol_match() {
11,001✔
2149
            return pattern[p] === Symbol.for('symbol') && !is_symbol_string(input[i]);
589✔
2150
        }
589✔
2151
        function match_next() {
11,001✔
2152
            var next_pattern = pattern[p + 1];
1,264✔
2153
            var next_input = input[i + 1];
1,264✔
2154
            if (next_pattern !== undefined && next_input !== undefined) {
1,264✔
2155
                return inner_match([next_pattern], [next_input]);
1,172✔
2156
            }
1,172✔
2157
        }
1,264✔
2158
        var p = 0;
11,001✔
2159
        var glob = {};
11,001✔
2160
        for (var i = 0; i < input.length; ++i) {
11,001✔
2161
            if (typeof pattern[p] === 'undefined') {
25,934✔
2162
                return i;
1,624✔
2163
            }
1,624✔
2164
            if (pattern[p] instanceof Pattern) {
25,934✔
2165
                var m;
1,459✔
2166
                if (['+', '*'].includes(pattern[p].flag)) {
1,459✔
2167
                    while (i < input.length) {
1,364✔
2168
                        m = get_first_match(pattern[p].patterns, input.slice(i));
1,815✔
2169
                        if (m === -1) {
1,815✔
2170
                            break;
1,136✔
2171
                        }
1,136✔
2172
                        i += m;
679✔
2173
                    }
679✔
2174
                    i -= 1;
1,364✔
2175
                    p++;
1,364✔
2176
                    continue;
1,364✔
2177
                } else if (pattern[p].flag === '?') {
1,459✔
2178
                    m = get_first_match(pattern[p].patterns, input.slice(i));
95✔
2179
                    if (m === -1) {
95✔
2180
                        i -= 2; // if not found use same test on same input again
45✔
2181
                    } else {
95✔
2182
                        p++;
50✔
2183
                    }
50✔
2184
                    continue;
95✔
2185
                }
95✔
2186
            } else if (pattern[p] instanceof RegExp) {
25,934✔
2187
                if (!input[i].match(pattern[p])) {
15,881✔
2188
                    return -1;
6,217✔
2189
                }
6,217✔
2190
            } else if (lips.LString.isString(pattern[p])) {
22,851!
2191
                if (pattern[p].valueOf() !== input[i]) {
×
2192
                    return -1;
×
2193
                }
×
2194
            } else if (typeof pattern[p] === 'symbol') {
6,970✔
2195
                if (pattern[p] === Symbol.for('*')) {
6,576✔
2196
                    // ignore S-expressions inside for case when next pattern is )
5,987✔
2197
                    glob[p] = glob[p] || 0;
5,987✔
2198
                    //var zero_match = empty_match();
5,987✔
2199
                    if (['(', '['].includes(input[i])) {
5,987✔
2200
                        glob[p]++;
1,759✔
2201
                    } else if ([')', ']'].includes(input[i])) {
5,987✔
2202
                        glob[p]--;
1,207✔
2203
                    }
1,207✔
2204
                    if ((typeof pattern[p + 1] !== 'undefined' &&
5,987✔
2205
                         glob[p] === 0 && match_next() === -1) ||
5,987✔
2206
                        glob[p] > 0) {
5,987✔
2207
                        continue;
5,612✔
2208
                    }
5,612✔
2209
                } else if (not_symbol_match()) {
6,576✔
2210
                    return -1;
263✔
2211
                }
263✔
2212
            } else if (pattern[p] instanceof Array) {
6,970✔
2213
                var inc = inner_match(pattern[p], input.slice(i));
394✔
2214
                if (inc === -1 || inc + i > input.length) {
394✔
2215
                    // if no more input it's not match
176✔
2216
                    return -1;
176✔
2217
                }
176✔
2218
                i += inc - 1;
218✔
2219
                p++;
218✔
2220
                continue;
218✔
2221
            } else {
394!
2222
                return -1;
×
2223
            }
×
2224
            p++;
10,365✔
2225
        }
10,365✔
2226
        if (pattern.length !== p) {
11,001✔
2227
            // if there are still patterns it's not match
2,069✔
2228
            return -1;
2,069✔
2229
        }
2,069✔
2230
        return input.length;
652✔
2231
    }
11,001✔
2232
}
7,501✔
2233
// ----------------------------------------------------------------------
1✔
2234
// :: Code formatter class
1✔
2235
// :: based on http://community.schemewiki.org/?scheme-style
1✔
2236
// :: and GNU Emacs scheme mode
1✔
2237
// :: it rely on meta data from tokenizer function
1✔
2238
// ----------------------------------------------------------------------
1✔
2239
function Formatter(code) {
22✔
2240
    this.__code__ = code.replace(/\r/g, '');
22✔
2241
}
22✔
2242
// ----------------------------------------------------------------------
1✔
2243
Formatter.defaults = {
1✔
2244
    offset: 0,
1✔
2245
    indent: 2,
1✔
2246
    exceptions: {
1✔
2247
        specials: [
1✔
2248
            /* eslint-disable max-len */
1✔
2249
            /^(?:#:)?(?:define(?:-values|-syntax|-macro|-class|-record-type)?|(?:call-with-(?:input-file|output-file|port))|lambda|let-env|try|catch|when|unless|while|syntax-rules|(let|letrec)(-syntax|\*?-values|\*)?)$/
1✔
2250
            /* eslint-enable */
1✔
2251
        ],
1✔
2252
        shift: {
1✔
2253
            1: ['&', '#']
1✔
2254
        }
1✔
2255
    }
1✔
2256
};
1✔
2257
Formatter.match = match;
1✔
2258
// ----------------------------------------------------------------------
1✔
2259
// :: Return indent for next line
1✔
2260
// ----------------------------------------------------------------------
1✔
2261
Formatter.prototype._options = function _options(options) {
1✔
2262
    var defaults = Formatter.defaults;
93✔
2263
    if (typeof options === 'undefined') {
93✔
2264
        return Object.assign({}, defaults);
22✔
2265
    }
22✔
2266
    var exceptions = options && options.exceptions || {};
93!
2267
    var specials = exceptions.specials || [];
93!
2268
    var shift = exceptions.shift || { 1: [] };
93!
2269
    return {
93✔
2270
        ...defaults,
93✔
2271
        ...options,
93✔
2272
        exceptions: {
93✔
2273
            specials: [...defaults.exceptions.specials, ...specials],
93✔
2274
            shift: {
93✔
2275
                ...shift,
93✔
2276
                1: [...defaults.exceptions.shift[1], ...shift[1]]
93✔
2277
            }
93✔
2278
        }
93✔
2279
    };
93✔
2280
};
1✔
2281
// ----------------------------------------------------------------------
1✔
2282
Formatter.prototype.indent = function indent(options) {
1✔
2283
    var tokens = tokenize(this.__code__, true);
×
2284
    return this._indent(tokens, options);
×
2285
};
1✔
2286
// ----------------------------------------------------------------------
1✔
2287
Formatter.exception_shift = function(token, settings) {
1✔
2288
    function match(list) {
105✔
2289
        if (!list.length) {
177!
2290
            return false;
×
2291
        }
×
2292
        if (list.indexOf(token) !== -1) {
177!
2293
            return true;
×
2294
        } else {
177✔
2295
            var regexes = list.filter(s => s instanceof RegExp);
177✔
2296
            if (!regexes.length) {
177✔
2297
                return false;
72✔
2298
            }
72✔
2299
            for (let re of regexes) {
177✔
2300
                if (token.match(re)) {
177✔
2301
                    return true;
33✔
2302
                }
33✔
2303
            }
177✔
2304
        }
72✔
2305
        return false;
72✔
2306
    }
177✔
2307
    if (match(settings.exceptions.specials)) {
105✔
2308
        return settings.indent;
33✔
2309
    }
33✔
2310
    var shift = settings.exceptions.shift;
72✔
2311
    for (var [indent, tokens] of Object.entries(shift)) {
72✔
2312
        if (match(tokens)) {
72!
2313
            return +indent;
×
2314
        }
×
2315
    }
72✔
2316
    return -1;
72✔
2317
};
1✔
2318
// ----------------------------------------------------------------------
1✔
2319
Formatter.prototype._indent = function _indent(tokens, options) {
1✔
2320
    var settings = this._options(options);
71✔
2321
    var spaces = lineIndent(tokens);
71✔
2322
    var sexp = previousSexp(tokens);
71✔
2323
    // one character before S-Expression
71✔
2324
    var before_sexpr = tokens[tokens.length - sexp.length - 1];
71✔
2325
    var last = tokens[tokens.length - 1];
71✔
2326
    if (last.token.match(/^"[\S\s]+[^"]$/)) {
71!
2327
        return spaces + settings.indent;
×
2328
    }
×
2329
    if (sexp && sexp.length) {
71✔
2330
        if (sexp[0].line > 0) {
71✔
2331
            settings.offset = 0;
27✔
2332
        }
27✔
2333
        if (sexp.toString() === tokens.toString() && balanced(sexp)) {
71✔
2334
            return settings.offset + sexp[0].col;
2✔
2335
        } else if (sexp.length === 1) {
71!
2336
            return settings.offset + sexp[0].col + 1;
×
2337
        } else {
69✔
2338
            // search for token before S-Expression for case like #(10 or &(:x
69✔
2339
            var exception = -1;
69✔
2340
            if (before_sexpr) {
69✔
2341
                var shift = Formatter.exception_shift(before_sexpr.token, settings);
36✔
2342
                if (shift !== -1) {
36!
2343
                    exception = shift;
×
2344
                }
×
2345
            }
36✔
2346
            if (exception === -1) {
69✔
2347
                exception = Formatter.exception_shift(sexp[1].token, settings);
69✔
2348
            }
69✔
2349
            if (exception !== -1) {
69✔
2350
                return settings.offset + sexp[0].col + exception;
33✔
2351
            } else if (sexp[0].line < sexp[1].line) {
69!
2352
                return settings.offset + sexp[0].col + 1;
×
2353
            } else if (sexp.length > 3 && sexp[1].line === sexp[3].line) {
36✔
2354
                if (sexp[1].token === '(' || sexp[1].token === '[') {
28✔
2355
                    return settings.offset + sexp[1].col;
19✔
2356
                }
19✔
2357
                return settings.offset + sexp[3].col;
9✔
2358
            } else if (sexp[0].line === sexp[1].line) {
36✔
2359
                return settings.offset + settings.indent + sexp[0].col;
8✔
2360
            } else {
8!
2361
                var next_tokens = sexp.slice(2);
×
2362
                for (var i = 0; i < next_tokens.length; ++i) {
×
2363
                    var token = next_tokens[i];
×
2364
                    if (token.token.trim()) {
×
2365
                        return token.col;
×
2366
                    }
×
2367
                }
×
2368
            }
×
2369
        }
69✔
2370
    } else {
71!
2371
        return 0;
×
2372
    }
×
2373
    return spaces + settings.indent;
×
2374
};
1✔
2375
// ----------------------------------------------------------------------
1✔
2376
function Ahead(pattern) {
1✔
2377
    this.pattern = pattern;
1✔
2378
}
1✔
2379
// TODO: make it print
1✔
2380
Ahead.prototype.toString = function() {
1✔
2381
    return `#<pattern(${this.pattern})>`;
×
2382
};
1✔
2383
// ----------------------------------------------------------------------
1✔
2384
Ahead.prototype.match = function(string) {
1✔
2385
    return string.match(this.pattern);
142✔
2386
};
1✔
2387
// ----------------------------------------------------------------------
1✔
2388
// Pattern has any number of patterns that it matches using OR operator
1✔
2389
// Pattern is in form of array with regular expressions
1✔
2390
// ----------------------------------------------------------------------
1✔
2391
function Pattern(...args) {
6✔
2392
    var flag = args.pop();
6✔
2393
    this.patterns = args;
6✔
2394
    this.flag = flag;
6✔
2395
}
6✔
2396
Pattern.prototype.toString = function() {
1✔
2397
    var patterns = this.patterns.map(x => to_string(x)).join('|');
×
2398
    return `#<pattern(${patterns} ${this.flag})>`;
×
2399
};
1✔
2400
// ----------------------------------------------------------------------
1✔
2401
Formatter.Pattern = Pattern;
1✔
2402
Formatter.Ahead = Ahead;
1✔
2403
var p_o = /^[[(]$/;
1✔
2404
var p_e = /^[\])]$/;
1✔
2405
var not_p = /[^()[\]]/;
1✔
2406
const not_close = new Ahead(/[^)\]]/);
1✔
2407
//const open = new Ahead(/[([]/);
1✔
2408
const glob = Symbol.for('*');
1✔
2409
const sexp_or_atom = new Pattern([p_o, glob, p_e], [not_p], '+');
1✔
2410
const sexp = new Pattern([p_o, glob, p_e], '+');
1✔
2411
const symbol = new Pattern([Symbol.for('symbol')], '?');
1✔
2412
const symbols = new Pattern([Symbol.for('symbol')], '*');
1✔
2413
const identifiers = [p_o, symbols, p_e];
1✔
2414
const let_value = new Pattern([p_o, Symbol.for('symbol'), glob, p_e], '+');
1✔
2415
const syntax_rules = keywords_re('syntax-rules');
1✔
2416
// rules for breaking S-Expressions into lines
1✔
2417
const def_lambda_re = keywords_re('define', 'lambda', 'define-macro', 'syntax-rules');
1✔
2418
/* eslint-disable max-len */
1✔
2419
const non_def = /^(?!.*\b(?:[()[\]]|define(?:-macro)?|let(?:\*|rec|-env|-syntax|)?|lambda|syntax-rules)\b).*$/;
1✔
2420
/* eslint-enable */
1✔
2421
const let_re = /^(?:#:)?(let(?:\*|rec|-env|-syntax)?)$/;
1✔
2422
// match keyword if it's normal token or gensym (prefixed with #:)
1✔
2423
const comment_re = /^;.*/;
1✔
2424
function keywords_re(...args) {
7✔
2425
    return new RegExp(`^(?:#:)?(?:${args.join('|')})$`);
7✔
2426
}
7✔
2427
// line breaking rules
1✔
2428
Formatter.rules = [
1✔
2429
    [[sexp], 0, not_close],
1✔
2430
    [[p_o, keywords_re('begin', 'cond-expand')], 1, not_close],
1✔
2431
    [[p_o, let_re, symbol, p_o, let_value, p_e], 1, not_close],
1✔
2432
    [[p_o, let_re, symbol, sexp_or_atom], 1, not_close],
1✔
2433
    [[p_o, let_re, p_o, let_value], 1, not_close],
1✔
2434
    [[p_o, keywords_re('define-syntax'), /.+/], 1],
1✔
2435
    [[p_o, syntax_rules, not_p, identifiers], 1],
1✔
2436
    [[p_o, syntax_rules, not_p, identifiers, sexp], 1, not_close],
1✔
2437
    [[p_o, syntax_rules, identifiers], 1],
1✔
2438
    [comment_re, -1],
1✔
2439
    [[p_o, syntax_rules, identifiers, sexp], 1, not_close],
1✔
2440
    [[p_o, non_def, new Pattern([/[^()[\]]/], '+'), sexp], 1, not_close],
1✔
2441
    [[p_o, sexp], 1, not_close],
1✔
2442
    [[p_o, not_p, sexp], 1, not_close],
1✔
2443
    [[p_o, keywords_re('lambda', 'if'), not_p], 1, not_close],
1✔
2444
    [[p_o, keywords_re('while'), not_p, sexp], 1, not_close],
1✔
2445
    [[p_o, keywords_re('if'), not_p, glob], 1, not_close],
1✔
2446
    [[p_o, def_lambda_re, identifiers], 0, not_close],
1✔
2447
    [[p_o, def_lambda_re, identifiers, string_re], 0, not_close],
1✔
2448
    [[p_o, def_lambda_re, identifiers, string_re, sexp], 0, not_close],
1✔
2449
    [[p_o, def_lambda_re, identifiers, sexp], 0, not_close]
1✔
2450
];
1✔
2451
// ----------------------------------------------------------------------
1✔
2452
Formatter.prototype.break = function() {
1✔
2453
    var code = this.__code__.replace(/\n[ \t]*/g, '\n ').trim();
22✔
2454
    // function that work when calling tokenize with meta data or not
22✔
2455
    const token = t => {
22✔
2456
        if (t.token.match(string_re) || t.token.match(re_re)) {
669✔
2457
            return t.token;
9✔
2458
        } else {
669✔
2459
            return t.token.replace(/\s+/, ' ');
660✔
2460
        }
660✔
2461
    };
22✔
2462
    const first_token_index = tokens => {
22✔
2463
        for (let i = tokens.length; i--;) {
7,501✔
2464
            const token = tokens[i];
10,633✔
2465
            if (token.trim() && !is_special(token)) {
10,633✔
2466
                return tokens.length - i - 1;
7,501✔
2467
            }
7,501✔
2468
        }
10,633✔
2469
    };
22!
2470
    // Tokenize is part of the parser/lexer that split code into tokens and includes
22✔
2471
    // meta data like number of column or line
22✔
2472
    var tokens = tokenize(code, true).map(token).filter(t => t !== '\n');
22✔
2473
    const { rules } = Formatter;
22✔
2474
    outer: for (let i = 1; i < tokens.length; ++i) {
22✔
2475
        if (!tokens[i].trim()) {
574✔
2476
            continue;
178✔
2477
        }
178✔
2478
        var sub = tokens.slice(0, i);
396✔
2479
        var sexp = {};
396✔
2480
        rules.map(b => b[1]).forEach(count => {
396✔
2481
            count = count.valueOf();
8,316✔
2482
            // some patterns require to check what was before like
8,316✔
2483
            // if inside let binding
8,316✔
2484
            if (count > 0 && !sexp[count]) {
8,316✔
2485
                sexp[count] = previousSexp(sub, count);
396✔
2486
            }
396✔
2487
        });
396✔
2488
        for (let [pattern, count, ext] of rules) {
574✔
2489
            const debug = pattern === comment_re;
7,501✔
2490
            count = count.valueOf();
7,501✔
2491
            // 0 count mean ignore the previous S-Expression
7,501✔
2492
            // -1 count mean check a single token
7,501✔
2493
            const test_sexp = count > 0 ? sexp[count] : sub;
7,501✔
2494
            let input = test_sexp.filter(t => t.trim() && !is_special(t));
7,501✔
2495
            if (!input.length) {
7,501!
2496
                continue;
×
2497
            }
×
2498
            if (count === -1) {
7,501✔
2499
                // NOTE: match work with arrays but since we check for a single token
368✔
2500
                //       we allow to use a single regex and wrap it with an array here
368✔
2501
                pattern = [pattern];
368✔
2502
                input = input.slice(-1);
368✔
2503
            }
368✔
2504
            const inc = first_token_index(test_sexp);
7,501✔
2505
            const m = match(pattern, input);
7,501✔
2506
            const next = tokens.slice(i).find(t => t.trim() && !is_special(t));
7,501✔
2507
            if (m && (ext instanceof Ahead && ext.match(next) || !ext)) {
7,501✔
2508
                const index = i - inc;
71✔
2509
                if (tokens[index] !== '\n') {
71✔
2510
                    if (!tokens[index].trim()) {
71✔
2511
                        tokens[index] = '\n';
71✔
2512
                    } else {
71!
2513
                        tokens.splice(index, 0, '\n');
×
2514
                        i++;
×
2515
                    }
×
2516
                }
71✔
2517
                i += inc;
71✔
2518
                continue outer;
71✔
2519
            }
71✔
2520
        }
7,501✔
2521
    }
325✔
2522
    this.__code__ = tokens.join('');
574✔
2523
    return this;
574✔
2524
};
1✔
2525
// ----------------------------------------------------------------------
1✔
2526
Formatter.prototype._spaces = function(i) {
1✔
2527
    return ' '.repeat(i);
71✔
2528
};
1✔
2529
// ----------------------------------------------------------------------
1✔
2530
// :: Auto formatting of code, it requires to have newlines
1✔
2531
// ----------------------------------------------------------------------
1✔
2532
Formatter.prototype.format = function format(options) {
1✔
2533
    // prepare code with single space after newline
22✔
2534
    // so we have space token to align
22✔
2535
    var code = this.__code__.trim().replace(/[ \t]*\n[ \t]*/g, '\n ');
22✔
2536
    var tokens = tokenize(code, true);
22✔
2537
    var settings = this._options(options);
22✔
2538
    var indent = 0;
22✔
2539
    var offset = 0;
22✔
2540
    for (var i = 0; i < tokens.length; ++i) {
22✔
2541
        var token = tokens[i];
431✔
2542
        if (token.token === '\n') {
431✔
2543
            indent = this._indent(tokens.slice(0, i), settings);
71✔
2544
            offset += indent;
71✔
2545
            if (tokens[i + 1]) {
71✔
2546
                tokens[i + 1].token = this._spaces(indent);
71✔
2547
                // because we have single space as initial indent
71✔
2548
                indent--;
71✔
2549
                offset--;
71✔
2550
                for (var j = i + 2; j < tokens.length; ++j) {
71✔
2551
                    tokens[j].offset += offset;
459✔
2552
                    tokens[j].col += indent;
459✔
2553
                    if (tokens[j].token === '\n') {
459✔
2554
                        // ++i is called after the loop
51✔
2555
                        i = j - 1;
51✔
2556
                        break;
51✔
2557
                    }
51✔
2558
                }
459✔
2559
            }
71✔
2560
        }
71✔
2561
    }
431✔
2562
    return tokens.map(token => {
22✔
2563
        if (token.token.match(string_re)) {
738✔
2564
            if (token.token.match(/\n/)) {
9✔
2565
                var spaces = ' '.repeat(token.col);
3✔
2566
                var lines = token.token.split('\n');
3✔
2567
                token.token = [lines[0]].concat(lines.slice(1).map(line => {
3✔
2568
                    return spaces + line;
3✔
2569
                })).join('\n');
3✔
2570
            }
3✔
2571
        }
9✔
2572
        return token.token;
738✔
2573
    }).join('');
22✔
2574
};
1✔
2575
// ----------------------------------------------------------------------
1✔
2576
// :: the Unicode folding case mapping generated scripts/fold.scm to get
1✔
2577
// :: latest version of the file use `make fold`
1✔
2578
// ----------------------------------------------------------------------
1✔
2579
const fold_case_mapping = {
1✔
2580
    "65": 97, "66": 98, "67": 99, "68": 100,
1✔
2581
    "69": 101, "70": 102, "71": 103, "72": 104,
1✔
2582
    "73": 105, "74": 106, "75": 107, "76": 108,
1✔
2583
    "77": 109, "78": 110, "79": 111, "80": 112,
1✔
2584
    "81": 113, "82": 114, "83": 115, "84": 116,
1✔
2585
    "85": 117, "86": 118, "87": 119, "88": 120,
1✔
2586
    "89": 121, "90": 122, "181": 956, "192": 224,
1✔
2587
    "193": 225, "194": 226, "195": 227, "196": 228,
1✔
2588
    "197": 229, "198": 230, "199": 231, "200": 232,
1✔
2589
    "201": 233, "202": 234, "203": 235, "204": 236,
1✔
2590
    "205": 237, "206": 238, "207": 239, "208": 240,
1✔
2591
    "209": 241, "210": 242, "211": 243, "212": 244,
1✔
2592
    "213": 245, "214": 246, "216": 248, "217": 249,
1✔
2593
    "218": 250, "219": 251, "220": 252, "221": 253,
1✔
2594
    "222": 254, "256": 257, "258": 259, "260": 261,
1✔
2595
    "262": 263, "264": 265, "266": 267, "268": 269,
1✔
2596
    "270": 271, "272": 273, "274": 275, "276": 277,
1✔
2597
    "278": 279, "280": 281, "282": 283, "284": 285,
1✔
2598
    "286": 287, "288": 289, "290": 291, "292": 293,
1✔
2599
    "294": 295, "296": 297, "298": 299, "300": 301,
1✔
2600
    "302": 303, "306": 307, "308": 309, "310": 311,
1✔
2601
    "313": 314, "315": 316, "317": 318, "319": 320,
1✔
2602
    "321": 322, "323": 324, "325": 326, "327": 328,
1✔
2603
    "330": 331, "332": 333, "334": 335, "336": 337,
1✔
2604
    "338": 339, "340": 341, "342": 343, "344": 345,
1✔
2605
    "346": 347, "348": 349, "350": 351, "352": 353,
1✔
2606
    "354": 355, "356": 357, "358": 359, "360": 361,
1✔
2607
    "362": 363, "364": 365, "366": 367, "368": 369,
1✔
2608
    "370": 371, "372": 373, "374": 375, "376": 255,
1✔
2609
    "377": 378, "379": 380, "381": 382, "383": 115,
1✔
2610
    "385": 595, "386": 387, "388": 389, "390": 596,
1✔
2611
    "391": 392, "393": 598, "394": 599, "395": 396,
1✔
2612
    "398": 477, "399": 601, "400": 603, "401": 402,
1✔
2613
    "403": 608, "404": 611, "406": 617, "407": 616,
1✔
2614
    "408": 409, "412": 623, "413": 626, "415": 629,
1✔
2615
    "416": 417, "418": 419, "420": 421, "422": 640,
1✔
2616
    "423": 424, "425": 643, "428": 429, "430": 648,
1✔
2617
    "431": 432, "433": 650, "434": 651, "435": 436,
1✔
2618
    "437": 438, "439": 658, "440": 441, "444": 445,
1✔
2619
    "452": 454, "453": 454, "455": 457, "456": 457,
1✔
2620
    "458": 460, "459": 460, "461": 462, "463": 464,
1✔
2621
    "465": 466, "467": 468, "469": 470, "471": 472,
1✔
2622
    "473": 474, "475": 476, "478": 479, "480": 481,
1✔
2623
    "482": 483, "484": 485, "486": 487, "488": 489,
1✔
2624
    "490": 491, "492": 493, "494": 495, "497": 499,
1✔
2625
    "498": 499, "500": 501, "502": 405, "503": 447,
1✔
2626
    "504": 505, "506": 507, "508": 509, "510": 511,
1✔
2627
    "512": 513, "514": 515, "516": 517, "518": 519,
1✔
2628
    "520": 521, "522": 523, "524": 525, "526": 527,
1✔
2629
    "528": 529, "530": 531, "532": 533, "534": 535,
1✔
2630
    "536": 537, "538": 539, "540": 541, "542": 543,
1✔
2631
    "544": 414, "546": 547, "548": 549, "550": 551,
1✔
2632
    "552": 553, "554": 555, "556": 557, "558": 559,
1✔
2633
    "560": 561, "562": 563, "570": 11365, "571": 572,
1✔
2634
    "573": 410, "574": 11366, "577": 578, "579": 384,
1✔
2635
    "580": 649, "581": 652, "582": 583, "584": 585,
1✔
2636
    "586": 587, "588": 589, "590": 591, "837": 953,
1✔
2637
    "880": 881, "882": 883, "886": 887, "895": 1011,
1✔
2638
    "902": 940, "904": 941, "905": 942, "906": 943,
1✔
2639
    "908": 972, "910": 973, "911": 974, "913": 945,
1✔
2640
    "914": 946, "915": 947, "916": 948, "917": 949,
1✔
2641
    "918": 950, "919": 951, "920": 952, "921": 953,
1✔
2642
    "922": 954, "923": 955, "924": 956, "925": 957,
1✔
2643
    "926": 958, "927": 959, "928": 960, "929": 961,
1✔
2644
    "931": 963, "932": 964, "933": 965, "934": 966,
1✔
2645
    "935": 967, "936": 968, "937": 969, "938": 970,
1✔
2646
    "939": 971, "962": 963, "975": 983, "976": 946,
1✔
2647
    "977": 952, "981": 966, "982": 960, "984": 985,
1✔
2648
    "986": 987, "988": 989, "990": 991, "992": 993,
1✔
2649
    "994": 995, "996": 997, "998": 999, "1000": 1001,
1✔
2650
    "1002": 1003, "1004": 1005, "1006": 1007, "1008": 954,
1✔
2651
    "1009": 961, "1012": 952, "1013": 949, "1015": 1016,
1✔
2652
    "1017": 1010, "1018": 1019, "1021": 891, "1022": 892,
1✔
2653
    "1023": 893, "1024": 1104, "1025": 1105, "1026": 1106,
1✔
2654
    "1027": 1107, "1028": 1108, "1029": 1109, "1030": 1110,
1✔
2655
    "1031": 1111, "1032": 1112, "1033": 1113, "1034": 1114,
1✔
2656
    "1035": 1115, "1036": 1116, "1037": 1117, "1038": 1118,
1✔
2657
    "1039": 1119, "1040": 1072, "1041": 1073, "1042": 1074,
1✔
2658
    "1043": 1075, "1044": 1076, "1045": 1077, "1046": 1078,
1✔
2659
    "1047": 1079, "1048": 1080, "1049": 1081, "1050": 1082,
1✔
2660
    "1051": 1083, "1052": 1084, "1053": 1085, "1054": 1086,
1✔
2661
    "1055": 1087, "1056": 1088, "1057": 1089, "1058": 1090,
1✔
2662
    "1059": 1091, "1060": 1092, "1061": 1093, "1062": 1094,
1✔
2663
    "1063": 1095, "1064": 1096, "1065": 1097, "1066": 1098,
1✔
2664
    "1067": 1099, "1068": 1100, "1069": 1101, "1070": 1102,
1✔
2665
    "1071": 1103, "1120": 1121, "1122": 1123, "1124": 1125,
1✔
2666
    "1126": 1127, "1128": 1129, "1130": 1131, "1132": 1133,
1✔
2667
    "1134": 1135, "1136": 1137, "1138": 1139, "1140": 1141,
1✔
2668
    "1142": 1143, "1144": 1145, "1146": 1147, "1148": 1149,
1✔
2669
    "1150": 1151, "1152": 1153, "1162": 1163, "1164": 1165,
1✔
2670
    "1166": 1167, "1168": 1169, "1170": 1171, "1172": 1173,
1✔
2671
    "1174": 1175, "1176": 1177, "1178": 1179, "1180": 1181,
1✔
2672
    "1182": 1183, "1184": 1185, "1186": 1187, "1188": 1189,
1✔
2673
    "1190": 1191, "1192": 1193, "1194": 1195, "1196": 1197,
1✔
2674
    "1198": 1199, "1200": 1201, "1202": 1203, "1204": 1205,
1✔
2675
    "1206": 1207, "1208": 1209, "1210": 1211, "1212": 1213,
1✔
2676
    "1214": 1215, "1216": 1231, "1217": 1218, "1219": 1220,
1✔
2677
    "1221": 1222, "1223": 1224, "1225": 1226, "1227": 1228,
1✔
2678
    "1229": 1230, "1232": 1233, "1234": 1235, "1236": 1237,
1✔
2679
    "1238": 1239, "1240": 1241, "1242": 1243, "1244": 1245,
1✔
2680
    "1246": 1247, "1248": 1249, "1250": 1251, "1252": 1253,
1✔
2681
    "1254": 1255, "1256": 1257, "1258": 1259, "1260": 1261,
1✔
2682
    "1262": 1263, "1264": 1265, "1266": 1267, "1268": 1269,
1✔
2683
    "1270": 1271, "1272": 1273, "1274": 1275, "1276": 1277,
1✔
2684
    "1278": 1279, "1280": 1281, "1282": 1283, "1284": 1285,
1✔
2685
    "1286": 1287, "1288": 1289, "1290": 1291, "1292": 1293,
1✔
2686
    "1294": 1295, "1296": 1297, "1298": 1299, "1300": 1301,
1✔
2687
    "1302": 1303, "1304": 1305, "1306": 1307, "1308": 1309,
1✔
2688
    "1310": 1311, "1312": 1313, "1314": 1315, "1316": 1317,
1✔
2689
    "1318": 1319, "1320": 1321, "1322": 1323, "1324": 1325,
1✔
2690
    "1326": 1327, "1329": 1377, "1330": 1378, "1331": 1379,
1✔
2691
    "1332": 1380, "1333": 1381, "1334": 1382, "1335": 1383,
1✔
2692
    "1336": 1384, "1337": 1385, "1338": 1386, "1339": 1387,
1✔
2693
    "1340": 1388, "1341": 1389, "1342": 1390, "1343": 1391,
1✔
2694
    "1344": 1392, "1345": 1393, "1346": 1394, "1347": 1395,
1✔
2695
    "1348": 1396, "1349": 1397, "1350": 1398, "1351": 1399,
1✔
2696
    "1352": 1400, "1353": 1401, "1354": 1402, "1355": 1403,
1✔
2697
    "1356": 1404, "1357": 1405, "1358": 1406, "1359": 1407,
1✔
2698
    "1360": 1408, "1361": 1409, "1362": 1410, "1363": 1411,
1✔
2699
    "1364": 1412, "1365": 1413, "1366": 1414, "4256": 11520,
1✔
2700
    "4257": 11521, "4258": 11522, "4259": 11523, "4260": 11524,
1✔
2701
    "4261": 11525, "4262": 11526, "4263": 11527, "4264": 11528,
1✔
2702
    "4265": 11529, "4266": 11530, "4267": 11531, "4268": 11532,
1✔
2703
    "4269": 11533, "4270": 11534, "4271": 11535, "4272": 11536,
1✔
2704
    "4273": 11537, "4274": 11538, "4275": 11539, "4276": 11540,
1✔
2705
    "4277": 11541, "4278": 11542, "4279": 11543, "4280": 11544,
1✔
2706
    "4281": 11545, "4282": 11546, "4283": 11547, "4284": 11548,
1✔
2707
    "4285": 11549, "4286": 11550, "4287": 11551, "4288": 11552,
1✔
2708
    "4289": 11553, "4290": 11554, "4291": 11555, "4292": 11556,
1✔
2709
    "4293": 11557, "4295": 11559, "4301": 11565, "5112": 5104,
1✔
2710
    "5113": 5105, "5114": 5106, "5115": 5107, "5116": 5108,
1✔
2711
    "5117": 5109, "7296": 1074, "7297": 1076, "7298": 1086,
1✔
2712
    "7299": 1089, "7300": 1090, "7301": 1090, "7302": 1098,
1✔
2713
    "7303": 1123, "7304": 42571, "7312": 4304, "7313": 4305,
1✔
2714
    "7314": 4306, "7315": 4307, "7316": 4308, "7317": 4309,
1✔
2715
    "7318": 4310, "7319": 4311, "7320": 4312, "7321": 4313,
1✔
2716
    "7322": 4314, "7323": 4315, "7324": 4316, "7325": 4317,
1✔
2717
    "7326": 4318, "7327": 4319, "7328": 4320, "7329": 4321,
1✔
2718
    "7330": 4322, "7331": 4323, "7332": 4324, "7333": 4325,
1✔
2719
    "7334": 4326, "7335": 4327, "7336": 4328, "7337": 4329,
1✔
2720
    "7338": 4330, "7339": 4331, "7340": 4332, "7341": 4333,
1✔
2721
    "7342": 4334, "7343": 4335, "7344": 4336, "7345": 4337,
1✔
2722
    "7346": 4338, "7347": 4339, "7348": 4340, "7349": 4341,
1✔
2723
    "7350": 4342, "7351": 4343, "7352": 4344, "7353": 4345,
1✔
2724
    "7354": 4346, "7357": 4349, "7358": 4350, "7359": 4351,
1✔
2725
    "7680": 7681, "7682": 7683, "7684": 7685, "7686": 7687,
1✔
2726
    "7688": 7689, "7690": 7691, "7692": 7693, "7694": 7695,
1✔
2727
    "7696": 7697, "7698": 7699, "7700": 7701, "7702": 7703,
1✔
2728
    "7704": 7705, "7706": 7707, "7708": 7709, "7710": 7711,
1✔
2729
    "7712": 7713, "7714": 7715, "7716": 7717, "7718": 7719,
1✔
2730
    "7720": 7721, "7722": 7723, "7724": 7725, "7726": 7727,
1✔
2731
    "7728": 7729, "7730": 7731, "7732": 7733, "7734": 7735,
1✔
2732
    "7736": 7737, "7738": 7739, "7740": 7741, "7742": 7743,
1✔
2733
    "7744": 7745, "7746": 7747, "7748": 7749, "7750": 7751,
1✔
2734
    "7752": 7753, "7754": 7755, "7756": 7757, "7758": 7759,
1✔
2735
    "7760": 7761, "7762": 7763, "7764": 7765, "7766": 7767,
1✔
2736
    "7768": 7769, "7770": 7771, "7772": 7773, "7774": 7775,
1✔
2737
    "7776": 7777, "7778": 7779, "7780": 7781, "7782": 7783,
1✔
2738
    "7784": 7785, "7786": 7787, "7788": 7789, "7790": 7791,
1✔
2739
    "7792": 7793, "7794": 7795, "7796": 7797, "7798": 7799,
1✔
2740
    "7800": 7801, "7802": 7803, "7804": 7805, "7806": 7807,
1✔
2741
    "7808": 7809, "7810": 7811, "7812": 7813, "7814": 7815,
1✔
2742
    "7816": 7817, "7818": 7819, "7820": 7821, "7822": 7823,
1✔
2743
    "7824": 7825, "7826": 7827, "7828": 7829, "7835": 7777,
1✔
2744
    "7840": 7841, "7842": 7843, "7844": 7845, "7846": 7847,
1✔
2745
    "7848": 7849, "7850": 7851, "7852": 7853, "7854": 7855,
1✔
2746
    "7856": 7857, "7858": 7859, "7860": 7861, "7862": 7863,
1✔
2747
    "7864": 7865, "7866": 7867, "7868": 7869, "7870": 7871,
1✔
2748
    "7872": 7873, "7874": 7875, "7876": 7877, "7878": 7879,
1✔
2749
    "7880": 7881, "7882": 7883, "7884": 7885, "7886": 7887,
1✔
2750
    "7888": 7889, "7890": 7891, "7892": 7893, "7894": 7895,
1✔
2751
    "7896": 7897, "7898": 7899, "7900": 7901, "7902": 7903,
1✔
2752
    "7904": 7905, "7906": 7907, "7908": 7909, "7910": 7911,
1✔
2753
    "7912": 7913, "7914": 7915, "7916": 7917, "7918": 7919,
1✔
2754
    "7920": 7921, "7922": 7923, "7924": 7925, "7926": 7927,
1✔
2755
    "7928": 7929, "7930": 7931, "7932": 7933, "7934": 7935,
1✔
2756
    "7944": 7936, "7945": 7937, "7946": 7938, "7947": 7939,
1✔
2757
    "7948": 7940, "7949": 7941, "7950": 7942, "7951": 7943,
1✔
2758
    "7960": 7952, "7961": 7953, "7962": 7954, "7963": 7955,
1✔
2759
    "7964": 7956, "7965": 7957, "7976": 7968, "7977": 7969,
1✔
2760
    "7978": 7970, "7979": 7971, "7980": 7972, "7981": 7973,
1✔
2761
    "7982": 7974, "7983": 7975, "7992": 7984, "7993": 7985,
1✔
2762
    "7994": 7986, "7995": 7987, "7996": 7988, "7997": 7989,
1✔
2763
    "7998": 7990, "7999": 7991, "8008": 8000, "8009": 8001,
1✔
2764
    "8010": 8002, "8011": 8003, "8012": 8004, "8013": 8005,
1✔
2765
    "8025": 8017, "8027": 8019, "8029": 8021, "8031": 8023,
1✔
2766
    "8040": 8032, "8041": 8033, "8042": 8034, "8043": 8035,
1✔
2767
    "8044": 8036, "8045": 8037, "8046": 8038, "8047": 8039,
1✔
2768
    "8120": 8112, "8121": 8113, "8122": 8048, "8123": 8049,
1✔
2769
    "8126": 953, "8136": 8050, "8137": 8051, "8138": 8052,
1✔
2770
    "8139": 8053, "8152": 8144, "8153": 8145, "8154": 8054,
1✔
2771
    "8155": 8055, "8168": 8160, "8169": 8161, "8170": 8058,
1✔
2772
    "8171": 8059, "8172": 8165, "8184": 8056, "8185": 8057,
1✔
2773
    "8186": 8060, "8187": 8061, "8486": 969, "8490": 107,
1✔
2774
    "8491": 229, "8498": 8526, "8544": 8560, "8545": 8561,
1✔
2775
    "8546": 8562, "8547": 8563, "8548": 8564, "8549": 8565,
1✔
2776
    "8550": 8566, "8551": 8567, "8552": 8568, "8553": 8569,
1✔
2777
    "8554": 8570, "8555": 8571, "8556": 8572, "8557": 8573,
1✔
2778
    "8558": 8574, "8559": 8575, "8579": 8580, "9398": 9424,
1✔
2779
    "9399": 9425, "9400": 9426, "9401": 9427, "9402": 9428,
1✔
2780
    "9403": 9429, "9404": 9430, "9405": 9431, "9406": 9432,
1✔
2781
    "9407": 9433, "9408": 9434, "9409": 9435, "9410": 9436,
1✔
2782
    "9411": 9437, "9412": 9438, "9413": 9439, "9414": 9440,
1✔
2783
    "9415": 9441, "9416": 9442, "9417": 9443, "9418": 9444,
1✔
2784
    "9419": 9445, "9420": 9446, "9421": 9447, "9422": 9448,
1✔
2785
    "9423": 9449, "11264": 11312, "11265": 11313, "11266": 11314,
1✔
2786
    "11267": 11315, "11268": 11316, "11269": 11317, "11270": 11318,
1✔
2787
    "11271": 11319, "11272": 11320, "11273": 11321, "11274": 11322,
1✔
2788
    "11275": 11323, "11276": 11324, "11277": 11325, "11278": 11326,
1✔
2789
    "11279": 11327, "11280": 11328, "11281": 11329, "11282": 11330,
1✔
2790
    "11283": 11331, "11284": 11332, "11285": 11333, "11286": 11334,
1✔
2791
    "11287": 11335, "11288": 11336, "11289": 11337, "11290": 11338,
1✔
2792
    "11291": 11339, "11292": 11340, "11293": 11341, "11294": 11342,
1✔
2793
    "11295": 11343, "11296": 11344, "11297": 11345, "11298": 11346,
1✔
2794
    "11299": 11347, "11300": 11348, "11301": 11349, "11302": 11350,
1✔
2795
    "11303": 11351, "11304": 11352, "11305": 11353, "11306": 11354,
1✔
2796
    "11307": 11355, "11308": 11356, "11309": 11357, "11310": 11358,
1✔
2797
    "11311": 11359, "11360": 11361, "11362": 619, "11363": 7549,
1✔
2798
    "11364": 637, "11367": 11368, "11369": 11370, "11371": 11372,
1✔
2799
    "11373": 593, "11374": 625, "11375": 592, "11376": 594,
1✔
2800
    "11378": 11379, "11381": 11382, "11390": 575, "11391": 576,
1✔
2801
    "11392": 11393, "11394": 11395, "11396": 11397, "11398": 11399,
1✔
2802
    "11400": 11401, "11402": 11403, "11404": 11405, "11406": 11407,
1✔
2803
    "11408": 11409, "11410": 11411, "11412": 11413, "11414": 11415,
1✔
2804
    "11416": 11417, "11418": 11419, "11420": 11421, "11422": 11423,
1✔
2805
    "11424": 11425, "11426": 11427, "11428": 11429, "11430": 11431,
1✔
2806
    "11432": 11433, "11434": 11435, "11436": 11437, "11438": 11439,
1✔
2807
    "11440": 11441, "11442": 11443, "11444": 11445, "11446": 11447,
1✔
2808
    "11448": 11449, "11450": 11451, "11452": 11453, "11454": 11455,
1✔
2809
    "11456": 11457, "11458": 11459, "11460": 11461, "11462": 11463,
1✔
2810
    "11464": 11465, "11466": 11467, "11468": 11469, "11470": 11471,
1✔
2811
    "11472": 11473, "11474": 11475, "11476": 11477, "11478": 11479,
1✔
2812
    "11480": 11481, "11482": 11483, "11484": 11485, "11486": 11487,
1✔
2813
    "11488": 11489, "11490": 11491, "11499": 11500, "11501": 11502,
1✔
2814
    "11506": 11507, "42560": 42561, "42562": 42563, "42564": 42565,
1✔
2815
    "42566": 42567, "42568": 42569, "42570": 42571, "42572": 42573,
1✔
2816
    "42574": 42575, "42576": 42577, "42578": 42579, "42580": 42581,
1✔
2817
    "42582": 42583, "42584": 42585, "42586": 42587, "42588": 42589,
1✔
2818
    "42590": 42591, "42592": 42593, "42594": 42595, "42596": 42597,
1✔
2819
    "42598": 42599, "42600": 42601, "42602": 42603, "42604": 42605,
1✔
2820
    "42624": 42625, "42626": 42627, "42628": 42629, "42630": 42631,
1✔
2821
    "42632": 42633, "42634": 42635, "42636": 42637, "42638": 42639,
1✔
2822
    "42640": 42641, "42642": 42643, "42644": 42645, "42646": 42647,
1✔
2823
    "42648": 42649, "42650": 42651, "42786": 42787, "42788": 42789,
1✔
2824
    "42790": 42791, "42792": 42793, "42794": 42795, "42796": 42797,
1✔
2825
    "42798": 42799, "42802": 42803, "42804": 42805, "42806": 42807,
1✔
2826
    "42808": 42809, "42810": 42811, "42812": 42813, "42814": 42815,
1✔
2827
    "42816": 42817, "42818": 42819, "42820": 42821, "42822": 42823,
1✔
2828
    "42824": 42825, "42826": 42827, "42828": 42829, "42830": 42831,
1✔
2829
    "42832": 42833, "42834": 42835, "42836": 42837, "42838": 42839,
1✔
2830
    "42840": 42841, "42842": 42843, "42844": 42845, "42846": 42847,
1✔
2831
    "42848": 42849, "42850": 42851, "42852": 42853, "42854": 42855,
1✔
2832
    "42856": 42857, "42858": 42859, "42860": 42861, "42862": 42863,
1✔
2833
    "42873": 42874, "42875": 42876, "42877": 7545, "42878": 42879,
1✔
2834
    "42880": 42881, "42882": 42883, "42884": 42885, "42886": 42887,
1✔
2835
    "42891": 42892, "42893": 613, "42896": 42897, "42898": 42899,
1✔
2836
    "42902": 42903, "42904": 42905, "42906": 42907, "42908": 42909,
1✔
2837
    "42910": 42911, "42912": 42913, "42914": 42915, "42916": 42917,
1✔
2838
    "42918": 42919, "42920": 42921, "42922": 614, "42923": 604,
1✔
2839
    "42924": 609, "42925": 620, "42926": 618, "42928": 670,
1✔
2840
    "42929": 647, "42930": 669, "42931": 43859, "42932": 42933,
1✔
2841
    "42934": 42935, "42936": 42937, "42938": 42939, "42940": 42941,
1✔
2842
    "42942": 42943, "42944": 42945, "42946": 42947, "42948": 42900,
1✔
2843
    "42949": 642, "42950": 7566, "42951": 42952, "42953": 42954,
1✔
2844
    "42960": 42961, "42966": 42967, "42968": 42969, "42997": 42998,
1✔
2845
    "43888": 5024, "43889": 5025, "43890": 5026, "43891": 5027,
1✔
2846
    "43892": 5028, "43893": 5029, "43894": 5030, "43895": 5031,
1✔
2847
    "43896": 5032, "43897": 5033, "43898": 5034, "43899": 5035,
1✔
2848
    "43900": 5036, "43901": 5037, "43902": 5038, "43903": 5039,
1✔
2849
    "43904": 5040, "43905": 5041, "43906": 5042, "43907": 5043,
1✔
2850
    "43908": 5044, "43909": 5045, "43910": 5046, "43911": 5047,
1✔
2851
    "43912": 5048, "43913": 5049, "43914": 5050, "43915": 5051,
1✔
2852
    "43916": 5052, "43917": 5053, "43918": 5054, "43919": 5055,
1✔
2853
    "43920": 5056, "43921": 5057, "43922": 5058, "43923": 5059,
1✔
2854
    "43924": 5060, "43925": 5061, "43926": 5062, "43927": 5063,
1✔
2855
    "43928": 5064, "43929": 5065, "43930": 5066, "43931": 5067,
1✔
2856
    "43932": 5068, "43933": 5069, "43934": 5070, "43935": 5071,
1✔
2857
    "43936": 5072, "43937": 5073, "43938": 5074, "43939": 5075,
1✔
2858
    "43940": 5076, "43941": 5077, "43942": 5078, "43943": 5079,
1✔
2859
    "43944": 5080, "43945": 5081, "43946": 5082, "43947": 5083,
1✔
2860
    "43948": 5084, "43949": 5085, "43950": 5086, "43951": 5087,
1✔
2861
    "43952": 5088, "43953": 5089, "43954": 5090, "43955": 5091,
1✔
2862
    "43956": 5092, "43957": 5093, "43958": 5094, "43959": 5095,
1✔
2863
    "43960": 5096, "43961": 5097, "43962": 5098, "43963": 5099,
1✔
2864
    "43964": 5100, "43965": 5101, "43966": 5102, "43967": 5103,
1✔
2865
    "65313": 65345, "65314": 65346, "65315": 65347, "65316": 65348,
1✔
2866
    "65317": 65349, "65318": 65350, "65319": 65351, "65320": 65352,
1✔
2867
    "65321": 65353, "65322": 65354, "65323": 65355, "65324": 65356,
1✔
2868
    "65325": 65357, "65326": 65358, "65327": 65359, "65328": 65360,
1✔
2869
    "65329": 65361, "65330": 65362, "65331": 65363, "65332": 65364,
1✔
2870
    "65333": 65365, "65334": 65366, "65335": 65367, "65336": 65368,
1✔
2871
    "65337": 65369, "65338": 65370, "66560": 66600, "66561": 66601,
1✔
2872
    "66562": 66602, "66563": 66603, "66564": 66604, "66565": 66605,
1✔
2873
    "66566": 66606, "66567": 66607, "66568": 66608, "66569": 66609,
1✔
2874
    "66570": 66610, "66571": 66611, "66572": 66612, "66573": 66613,
1✔
2875
    "66574": 66614, "66575": 66615, "66576": 66616, "66577": 66617,
1✔
2876
    "66578": 66618, "66579": 66619, "66580": 66620, "66581": 66621,
1✔
2877
    "66582": 66622, "66583": 66623, "66584": 66624, "66585": 66625,
1✔
2878
    "66586": 66626, "66587": 66627, "66588": 66628, "66589": 66629,
1✔
2879
    "66590": 66630, "66591": 66631, "66592": 66632, "66593": 66633,
1✔
2880
    "66594": 66634, "66595": 66635, "66596": 66636, "66597": 66637,
1✔
2881
    "66598": 66638, "66599": 66639, "66736": 66776, "66737": 66777,
1✔
2882
    "66738": 66778, "66739": 66779, "66740": 66780, "66741": 66781,
1✔
2883
    "66742": 66782, "66743": 66783, "66744": 66784, "66745": 66785,
1✔
2884
    "66746": 66786, "66747": 66787, "66748": 66788, "66749": 66789,
1✔
2885
    "66750": 66790, "66751": 66791, "66752": 66792, "66753": 66793,
1✔
2886
    "66754": 66794, "66755": 66795, "66756": 66796, "66757": 66797,
1✔
2887
    "66758": 66798, "66759": 66799, "66760": 66800, "66761": 66801,
1✔
2888
    "66762": 66802, "66763": 66803, "66764": 66804, "66765": 66805,
1✔
2889
    "66766": 66806, "66767": 66807, "66768": 66808, "66769": 66809,
1✔
2890
    "66770": 66810, "66771": 66811, "66928": 66967, "66929": 66968,
1✔
2891
    "66930": 66969, "66931": 66970, "66932": 66971, "66933": 66972,
1✔
2892
    "66934": 66973, "66935": 66974, "66936": 66975, "66937": 66976,
1✔
2893
    "66938": 66977, "66940": 66979, "66941": 66980, "66942": 66981,
1✔
2894
    "66943": 66982, "66944": 66983, "66945": 66984, "66946": 66985,
1✔
2895
    "66947": 66986, "66948": 66987, "66949": 66988, "66950": 66989,
1✔
2896
    "66951": 66990, "66952": 66991, "66953": 66992, "66954": 66993,
1✔
2897
    "66956": 66995, "66957": 66996, "66958": 66997, "66959": 66998,
1✔
2898
    "66960": 66999, "66961": 67000, "66962": 67001, "66964": 67003,
1✔
2899
    "66965": 67004, "68736": 68800, "68737": 68801, "68738": 68802,
1✔
2900
    "68739": 68803, "68740": 68804, "68741": 68805, "68742": 68806,
1✔
2901
    "68743": 68807, "68744": 68808, "68745": 68809, "68746": 68810,
1✔
2902
    "68747": 68811, "68748": 68812, "68749": 68813, "68750": 68814,
1✔
2903
    "68751": 68815, "68752": 68816, "68753": 68817, "68754": 68818,
1✔
2904
    "68755": 68819, "68756": 68820, "68757": 68821, "68758": 68822,
1✔
2905
    "68759": 68823, "68760": 68824, "68761": 68825, "68762": 68826,
1✔
2906
    "68763": 68827, "68764": 68828, "68765": 68829, "68766": 68830,
1✔
2907
    "68767": 68831, "68768": 68832, "68769": 68833, "68770": 68834,
1✔
2908
    "68771": 68835, "68772": 68836, "68773": 68837, "68774": 68838,
1✔
2909
    "68775": 68839, "68776": 68840, "68777": 68841, "68778": 68842,
1✔
2910
    "68779": 68843, "68780": 68844, "68781": 68845, "68782": 68846,
1✔
2911
    "68783": 68847, "68784": 68848, "68785": 68849, "68786": 68850,
1✔
2912
    "71840": 71872, "71841": 71873, "71842": 71874, "71843": 71875,
1✔
2913
    "71844": 71876, "71845": 71877, "71846": 71878, "71847": 71879,
1✔
2914
    "71848": 71880, "71849": 71881, "71850": 71882, "71851": 71883,
1✔
2915
    "71852": 71884, "71853": 71885, "71854": 71886, "71855": 71887,
1✔
2916
    "71856": 71888, "71857": 71889, "71858": 71890, "71859": 71891,
1✔
2917
    "71860": 71892, "71861": 71893, "71862": 71894, "71863": 71895,
1✔
2918
    "71864": 71896, "71865": 71897, "71866": 71898, "71867": 71899,
1✔
2919
    "71868": 71900, "71869": 71901, "71870": 71902, "71871": 71903,
1✔
2920
    "93760": 93792, "93761": 93793, "93762": 93794, "93763": 93795,
1✔
2921
    "93764": 93796, "93765": 93797, "93766": 93798, "93767": 93799,
1✔
2922
    "93768": 93800, "93769": 93801, "93770": 93802, "93771": 93803,
1✔
2923
    "93772": 93804, "93773": 93805, "93774": 93806, "93775": 93807,
1✔
2924
    "93776": 93808, "93777": 93809, "93778": 93810, "93779": 93811,
1✔
2925
    "93780": 93812, "93781": 93813, "93782": 93814, "93783": 93815,
1✔
2926
    "93784": 93816, "93785": 93817, "93786": 93818, "93787": 93819,
1✔
2927
    "93788": 93820, "93789": 93821, "93790": 93822, "93791": 93823,
1✔
2928
    "125184": 125218, "125185": 125219, "125186": 125220, "125187": 125221,
1✔
2929
    "125188": 125222, "125189": 125223, "125190": 125224, "125191": 125225,
1✔
2930
    "125192": 125226, "125193": 125227, "125194": 125228, "125195": 125229,
1✔
2931
    "125196": 125230, "125197": 125231, "125198": 125232, "125199": 125233,
1✔
2932
    "125200": 125234, "125201": 125235, "125202": 125236, "125203": 125237,
1✔
2933
    "125204": 125238, "125205": 125239, "125206": 125240, "125207": 125241,
1✔
2934
    "125208": 125242, "125209": 125243, "125210": 125244, "125211": 125245,
1✔
2935
    "125212": 125246, "125213": 125247, "125214": 125248, "125215": 125249,
1✔
2936
    "125216": 125250, "125217": 125251
1✔
2937
};
1✔
2938
// ----------------------------------------------------------------------
1✔
2939
function foldcase_string(string) {
51✔
2940
    string = string.valueOf();
51✔
2941
    return Array.from(string).map(str => {
51✔
2942
        const ord = str.codePointAt(0);
141✔
2943
        const output = fold_case_mapping[ord];
141✔
2944
        if (output) {
141✔
2945
            return String.fromCodePoint(output);
30✔
2946
        }
30✔
2947
        return str;
111✔
2948
    }).join('');
51✔
2949
}
51✔
2950
// ----------------------------------------------------------------------
1✔
2951
// :: Flatten nested arrays
1✔
2952
// :: ref: https://stackoverflow.com/a/27282907/387194
1✔
2953
// ----------------------------------------------------------------------
1✔
2954
function flatten(array, mutable) {
×
2955
    var toString = Object.prototype.toString;
×
2956
    var arrayTypeStr = '[object Array]';
×
2957

×
2958
    var result = [];
×
2959
    var nodes = (mutable && array) || array.slice();
×
2960
    var node;
×
2961

×
2962
    if (!array.length) {
×
2963
        return result;
×
2964
    }
×
2965

×
2966
    node = nodes.pop();
×
2967

×
2968
    do {
×
2969
        if (toString.call(node) === arrayTypeStr) {
×
2970
            nodes.push.apply(nodes, node);
×
2971
        } else {
×
2972
            result.push(node);
×
2973
        }
×
2974
    } while (nodes.length && (node = nodes.pop()) !== undefined);
×
2975

×
2976
    result.reverse(); // we reverse result to restore the original order
×
2977
    return result;
×
2978
}
×
2979
// ----------------------------------------------------------------------
1✔
2980
// :: Fisher-Yates (aka Knuth) Shuffle
1✔
2981
// :: ref: https://stackoverflow.com/a/2450976/387194
1✔
2982
// ----------------------------------------------------------------------
1✔
2983
function shuffle(array, random) {
3✔
2984
  let currentIndex = array.length,  randomIndex;
3✔
2985

3✔
2986
  // While there remain elements to shuffle.
3✔
2987
  while (currentIndex > 0) {
3✔
2988

11✔
2989
    // Pick a remaining element.
11✔
2990
    randomIndex = Math.floor(random() * currentIndex);
11✔
2991
    currentIndex--;
11✔
2992

11✔
2993
    // And swap it with the current element.
11✔
2994
    [array[currentIndex], array[randomIndex]] = [
11✔
2995
      array[randomIndex], array[currentIndex]];
11✔
2996
  }
11✔
2997

3✔
2998
  return array;
3✔
2999
}
3✔
3000
// ----------------------------------------------------------------------
1✔
3001
// :: Nil constructor with only once instance
1✔
3002
// ----------------------------------------------------------------------
1✔
3003
function Nil() {}
1✔
3004
Nil.prototype.toString = function() {
1✔
3005
    return '()';
3,592✔
3006
};
1✔
3007
Nil.prototype.valueOf = function() {
1✔
3008
    return undefined;
9✔
3009
};
1✔
3010
Nil.prototype.serialize = function() {
1✔
3011
    return 0;
1✔
3012
};
1✔
3013
Nil.prototype.to_object = function() {
1✔
3014
    return {};
1✔
3015
};
1✔
3016
Nil.prototype.append = function(x) {
1✔
3017
    return new Pair(x, nil);
×
3018
};
1✔
3019
Nil.prototype.to_array = function() {
1✔
3020
    return [];
2✔
3021
};
1✔
3022
var nil = new Nil();
1✔
3023
// ----------------------------------------------------------------------
1✔
3024
// :: Pair constructor
1✔
3025
// ----------------------------------------------------------------------
1✔
3026
function Pair(car, cdr) {
3,823,821✔
3027
    if (typeof this !== 'undefined' && this.constructor !== Pair ||
3,823,821✔
3028
        typeof this === 'undefined') {
3,823,821✔
3029
        return new Pair(car, cdr);
19,055✔
3030
    }
19,055✔
3031
    this.car = car;
3,804,766✔
3032
    this.cdr = cdr;
3,804,766✔
3033
}
3,823,821✔
3034
// ----------------------------------------------------------------------
1✔
3035
function to_array(name, deep) {
2✔
3036
    return function recur(list) {
2✔
3037
        typecheck(name, list, ['pair', 'nil']);
1,947,491✔
3038
        if (is_nil(list)) {
1,947,491✔
3039
            return [];
18,640✔
3040
        }
18,640✔
3041
        var result = [];
1,928,851✔
3042
        var node = list;
1,928,851✔
3043
        while (true) {
1,947,491✔
3044
            if (is_pair(node)) {
5,203,572✔
3045
                if (node.have_cycles('cdr')) {
3,274,721!
3046
                    break;
×
3047
                }
×
3048
                var car = node.car;
3,274,721✔
3049
                if (deep && is_pair(car)) {
3,274,721!
3050
                    car = this.get(name).call(this, car);
×
3051
                }
×
3052
                result.push(car);
3,274,721✔
3053
                node = node.cdr;
3,274,721✔
3054
            } else if (is_nil(node)) {
5,203,572✔
3055
                break;
1,928,850✔
3056
            } else {
1,928,851✔
3057
                throw new Error(`${name}: can't convert improper list`);
1✔
3058
            }
1✔
3059
        }
5,203,572✔
3060
        return result;
1,928,850✔
3061
    };
2✔
3062
}
2✔
3063
// ----------------------------------------------------------------------
1✔
3064
Pair.prototype.flatten = function() {
1✔
3065
    return Pair.fromArray(flatten(this.to_array()));
×
3066
};
1✔
3067
// ----------------------------------------------------------------------
1✔
3068
Pair.prototype.length = function() {
1✔
3069
    var len = 0;
72,637✔
3070
    var node = this;
72,637✔
3071
    while (true) {
72,637✔
3072
        if (!node || is_nil(node) || !is_pair(node) ||
150,907✔
3073
            node.have_cycles('cdr')) {
150,907✔
3074
            break;
72,637✔
3075
        }
72,637✔
3076
        len++;
78,270✔
3077
        node = node.cdr;
78,270✔
3078
    }
78,270✔
3079
    return len;
72,637✔
3080
};
1✔
3081
// ----------------------------------------------------------------------
1✔
3082
Pair.prototype.freeze = function() {
1✔
3083
    let node = this;
1✔
3084
    while (true) {
1✔
3085
        read_only(node, 'car', node.car);
4✔
3086
        if (is_pair(node.car) && !node.have_cycles('car')) {
4!
3087
            node.car.freeze();
×
3088
        }
×
3089
        read_only(node, 'cdr', node.cdr);
4✔
3090
        if (node.have_cycles('cdr')) {
4!
3091
            break;
×
3092
        }
×
3093
        node = node.cdr;
4✔
3094
        if (!is_pair(node)) {
4✔
3095
            break;
1✔
3096
        }
1✔
3097
    }
4✔
3098
};
1✔
3099
// ----------------------------------------------------------------------
1✔
3100
Pair.match = function(obj, item) {
1✔
3101
    if (obj instanceof LSymbol) {
2,824,829✔
3102
        return LSymbol.is(obj, item);
968,327✔
3103
    } else if (is_pair(obj)) {
2,824,829✔
3104
        return Pair.match(obj.car, item) || Pair.match(obj.cdr, item);
1,715,492✔
3105
    } else if (Array.isArray(obj)) {
1,856,502✔
3106
        return obj.some(x => {
2✔
3107
            return Pair.match(x, item);
4✔
3108
        });
2✔
3109
    } else if (is_plain_object(obj)) {
141,010✔
3110
        return Object.values(obj).some(x => {
1✔
3111
            return Pair.match(x, item);
1✔
3112
        });
1✔
3113
    }
1✔
3114
    return false;
141,007✔
3115
};
1✔
3116
// ----------------------------------------------------------------------
1✔
3117
Pair.prototype.find = function(item) {
1✔
3118
    return Pair.match(this, item);
403,205✔
3119
};
1✔
3120

1✔
3121
// ----------------------------------------------------------------------
1✔
3122
Pair.prototype.clone = function(deep = true) {
1✔
3123
    var visited = new Map();
122✔
3124
    function clone(node) {
122✔
3125
        if (is_pair(node)) {
750✔
3126
            if (visited.has(node)) {
314!
3127
                return visited.get(node);
×
3128
            }
×
3129
            var pair = new Pair();
314✔
3130
            visited.set(node, pair);
314✔
3131
            if (deep) {
314✔
3132
                pair.car = clone(node.car);
314✔
3133
            } else {
314!
3134
                pair.car = node.car;
×
3135
            }
×
3136
            pair.cdr = clone(node.cdr);
314✔
3137
            pair[__cycles__] = node[__cycles__];
314✔
3138
            return pair;
314✔
3139
        }
314✔
3140
        return node;
436✔
3141
    }
750✔
3142
    return clone(this);
122✔
3143
};
1✔
3144

1✔
3145
// ----------------------------------------------------------------------
1✔
3146
Pair.prototype.last_pair = function() {
1✔
3147
    let node = this;
360✔
3148
    while (true) {
360✔
3149
        if (!is_pair(node.cdr)) {
1,354✔
3150
            return node;
360✔
3151
        }
360✔
3152
        if (node.have_cycles('cdr')) {
1,354!
3153
            break;
×
3154
        }
×
3155
        node = node.cdr;
994✔
3156
    }
994✔
3157
};
1✔
3158

1✔
3159
// ----------------------------------------------------------------------
1✔
3160
Pair.prototype.to_array = function(deep = true) {
1✔
3161
    var result = [];
126✔
3162
    if (is_pair(this.car)) {
126✔
3163
        if (deep) {
4!
3164
            result.push(this.car.to_array());
×
3165
        } else {
4✔
3166
            result.push(this.car);
4✔
3167
        }
4✔
3168
    } else {
126✔
3169
        result.push(this.car.valueOf());
122✔
3170
    }
122✔
3171
    if (is_pair(this.cdr)) {
126✔
3172
        result = result.concat(this.cdr.to_array(deep));
81✔
3173
    }
81✔
3174
    return result;
126✔
3175
};
1✔
3176

1✔
3177
// ----------------------------------------------------------------------
1✔
3178
// :: TODO: change to Pair.from_array
1✔
3179
// ----------------------------------------------------------------------
1✔
3180
Pair.fromArray = function(array, deep = true, quote = false) {
1✔
3181
    if (is_pair(array) || quote && array instanceof Array && array[__data__]) {
91,199!
3182
        return array;
×
3183
    }
×
3184
    if (deep === false) {
91,199✔
3185
        var list = nil;
42,905✔
3186
        for (let i = array.length; i--;) {
42,905✔
3187
            list = new Pair(array[i], list);
45,951✔
3188
        }
45,951✔
3189
        return list;
42,905✔
3190
    }
42,905✔
3191
    if (array.length && !(array instanceof Array)) {
91,199!
3192
        array = [...array];
×
3193
    }
×
3194
    var result = nil;
48,294✔
3195
    var i = array.length;
48,294✔
3196
    while (i--) {
91,199✔
3197
        let car = array[i];
61,286✔
3198
        if (car instanceof Array) {
61,286✔
3199
            car = Pair.fromArray(car, deep, quote);
1,740✔
3200
        } else if (typeof car === 'string') {
61,286✔
3201
            car = LString(car);
45,544✔
3202
        } else if (typeof car === 'number' && !Number.isNaN(car)) {
59,546✔
3203
            car = LNumber(car);
8,031✔
3204
        }
8,031✔
3205
        result = new Pair(car, result);
61,286✔
3206
    }
61,286✔
3207
    return result;
48,294✔
3208
};
1✔
3209

1✔
3210
// ----------------------------------------------------------------------
1✔
3211
// By default to_object was created to create JavaScript objects,
1✔
3212
// so it uses valueOf to get native values.
1✔
3213
// Literal parameter was a hack to allow creating LComplex from LIPS code
1✔
3214
// ----------------------------------------------------------------------
1✔
3215
Pair.prototype.to_object = function(literal = false) {
1✔
3216
    var node = this;
16✔
3217
    var result = {};
16✔
3218
    while (true) {
16✔
3219
        if (is_pair(node) && is_pair(node.car)) {
45✔
3220
            var pair = node.car;
29✔
3221
            var name = pair.car;
29✔
3222
            if (name instanceof LSymbol) {
29✔
3223
                name = name.__name__;
22✔
3224
            }
22✔
3225
            if (name instanceof LString) {
29✔
3226
                name = name.valueOf();
7✔
3227
            }
7✔
3228
            var cdr = pair.cdr;
29✔
3229
            if (is_pair(cdr)) {
29!
3230
                cdr = cdr.to_object(literal);
×
3231
            }
×
3232
            if (is_native(cdr)) {
29✔
3233
                if (!literal) {
28✔
3234
                    cdr = cdr.valueOf();
6✔
3235
                }
6✔
3236
            }
28✔
3237
            result[name] = cdr;
29✔
3238
            node = node.cdr;
29✔
3239
        } else {
45✔
3240
            break;
16✔
3241
        }
16✔
3242
    }
45✔
3243
    return result;
16✔
3244
};
1✔
3245

1✔
3246
// ----------------------------------------------------------------------
1✔
3247
Pair.fromPairs = function(array) {
1✔
3248
    return array.reduce((list, pair) => {
×
3249
        return new Pair(
×
3250
            new Pair(
×
3251
                new LSymbol(pair[0]),
×
3252
                pair[1]
×
3253
            ),
×
3254
            list
×
3255
        );
×
3256
    }, nil);
×
3257
};
1✔
3258

1✔
3259
// ----------------------------------------------------------------------
1✔
3260
Pair.fromObject = function(obj) {
1✔
3261
    var array = Object.keys(obj).map((key) => [key, obj[key]]);
×
3262
    return Pair.fromPairs(array);
×
3263
};
1✔
3264

1✔
3265
// ----------------------------------------------------------------------
1✔
3266
Pair.prototype.reduce = function(fn) {
1✔
3267
    var node = this;
84,293✔
3268
    var result = nil;
84,293✔
3269
    while (true) {
84,293✔
3270
        if (!is_nil(node)) {
168,586✔
3271
            result = fn(result, node.car);
84,293✔
3272
            node = node.cdr;
84,293✔
3273
        } else {
84,293✔
3274
            break;
84,293✔
3275
        }
84,293✔
3276
    }
168,586✔
3277
    return result;
84,293✔
3278
};
1✔
3279

1✔
3280
// ----------------------------------------------------------------------
1✔
3281
Pair.prototype.reverse = function() {
1✔
3282
    if (this.have_cycles()) {
331!
3283
        throw new Error("You can't reverse list that have cycles");
×
3284
    }
×
3285
    var node = this;
331✔
3286
    var prev = nil;
331✔
3287
    while (!is_nil(node)) {
331✔
3288
        var next = node.cdr;
817✔
3289
        node.cdr = prev;
817✔
3290
        prev = node;
817✔
3291
        node = next;
817✔
3292
    }
817✔
3293
    return prev;
331✔
3294
};
1✔
3295

1✔
3296
// ----------------------------------------------------------------------
1✔
3297
Pair.prototype.transform = function(fn) {
1✔
3298
    var visited = [];
×
3299
    function recur(pair) {
×
3300
        if (is_pair(pair)) {
×
3301
            if (pair.replace) {
×
3302
                delete pair.replace;
×
3303
                return pair;
×
3304
            }
×
3305
            var car = fn(pair.car);
×
3306
            if (is_pair(car)) {
×
3307
                car = recur(car);
×
3308
                visited.push(car);
×
3309
            }
×
3310
            var cdr = fn(pair.cdr);
×
3311
            if (is_pair(cdr)) {
×
3312
                cdr = recur(cdr);
×
3313
                visited.push(cdr);
×
3314
            }
×
3315
            return new Pair(car, cdr);
×
3316
        }
×
3317
        return pair;
×
3318
    }
×
3319
    return recur(this);
×
3320
};
1✔
3321

1✔
3322
// ----------------------------------------------------------------------
1✔
3323
Pair.prototype.map = function(fn) {
1✔
3324
    if (typeof this.car !== 'undefined') {
3,524✔
3325
        return new Pair(fn(this.car), is_nil(this.cdr) ? nil : this.cdr.map(fn));
3,524✔
3326
    } else {
3,524!
3327
        return nil;
×
3328
    }
×
3329
};
1✔
3330
const repr = new Map();
1✔
3331
// ----------------------------------------------------------------------
1✔
3332
function is_plain_object(object) {
17,427,998✔
3333
    return object && typeof object === 'object' && object.constructor === Object;
17,427,998✔
3334
}
17,427,998✔
3335
// ----------------------------------------------------------------------
1✔
3336
var props = Object.getOwnPropertyNames(Array.prototype);
1✔
3337
var array_methods = [];
1✔
3338
props.forEach(x => {
1✔
3339
    array_methods.push(Array[x], Array.prototype[x]);
40✔
3340
});
1✔
3341
// ----------------------------------------------------------------------
1✔
3342
function is_array_method(x) {
19,903✔
3343
    x = unbind(x);
19,903✔
3344
    return array_methods.includes(x);
19,903✔
3345
}
19,903✔
3346
// ----------------------------------------------------------------------
1✔
3347
function is_lips_function(x) {
3,088,440✔
3348
    return is_function(x) && (is_lambda(x) || x.__doc__);
3,088,440✔
3349
}
3,088,440✔
3350
// ----------------------------------------------------------------------
1✔
3351
function user_repr(obj) {
14✔
3352
    const constructor = obj.constructor || Object;
14!
3353
    const plain_object = is_plain_object(obj);
14✔
3354
    var iterator = is_function(obj[Symbol.asyncIterator]) ||
14✔
3355
        is_function(obj[Symbol.iterator]);
14✔
3356
    let fn;
14✔
3357
    if (repr.has(constructor)) {
14!
3358
        fn = repr.get(constructor);
×
3359
    } else {
14✔
3360
        repr.forEach(function(value, key) {
14✔
3361
            key = unbind(key);
240✔
3362
            // if key is Object it should only work for plain_object
240✔
3363
            // because otherwise it will match every object
240✔
3364
            // we don't use instanceof so it don't work for subclasses
240✔
3365
            if (constructor === key &&
240✔
3366
                (key === Object && plain_object && !iterator || key !== Object)) {
240✔
3367
                fn = value;
15✔
3368
            }
15✔
3369
        });
14✔
3370
    }
14✔
3371
    return fn;
14✔
3372
}
14✔
3373
// ----------------------------------------------------------------------
1✔
3374
var str_mapping = new Map();
1✔
3375
[
1✔
3376
    [true, '#t'],
1✔
3377
    [false, '#f'],
1✔
3378
    [null, '#null'],
1✔
3379
    [undefined, '#void']
1✔
3380
].forEach(([key, value]) => {
1✔
3381
    str_mapping.set(key, value);
4✔
3382
});
1✔
3383
// ----------------------------------------------------------------------
1✔
3384
// :: Debug function that can be used with JSON.stringify
1✔
3385
// :: that will show symbols
1✔
3386
// ----------------------------------------------------------------------
1✔
3387
/* c8 ignore next 22 */
1✔
3388
function symbolize(obj) {
1✔
3389
    if (obj && typeof obj === 'object') {
1✔
3390
        var result = {};
1✔
3391
        const symbols = Object.getOwnPropertySymbols(obj);
1✔
3392
        symbols.forEach((key) => {
1✔
3393
            const name = key.toString()
1✔
3394
                .replace(/Symbol\(([^)]+)\)/, '$1');
1✔
3395
            result[name] = to_string(obj[key]);
1✔
3396
        });
1✔
3397
        const props = Object.getOwnPropertyNames(obj);
1✔
3398
        props.forEach(key => {
1✔
3399
            const o = obj[key];
1✔
3400
            if (o && typeof o === 'object' && o.constructor === Object) {
1✔
3401
                result[key] = symbolize(o);
1✔
3402
            } else {
1✔
3403
                result[key] = to_string(o);
1✔
3404
            }
1✔
3405
        });
1✔
3406
        return result;
1✔
3407
    }
1✔
3408
    return obj;
1✔
3409
}
1✔
3410
// ----------------------------------------------------------------------
1✔
3411
function get_props(obj) {
×
3412
    return Object.keys(obj).concat(Object.getOwnPropertySymbols(obj));
×
3413
}
×
3414
// ----------------------------------------------------------------------
1✔
3415
function has_own_function(obj, name) {
×
3416
    return obj.hasOwnProperty(name) && is_function(obj.toString);
×
3417
}
×
3418
// ----------------------------------------------------------------------
1✔
3419
function function_to_string(fn) {
×
3420
    if (is_native_function(fn)) {
×
3421
        return '#<procedure(native)>';
×
3422
    }
×
3423
    const constructor = fn.prototype && fn.prototype.constructor;
×
3424
    if (is_function(constructor) && is_lambda(constructor)) {
×
3425
        if (fn[__class__] && constructor.hasOwnProperty('__name__')) {
×
3426
            let name = constructor.__name__;
×
3427
            if (LString.isString(name)) {
×
3428
                name = name.toString();
×
3429
                return `#<class:${name}>`;
×
3430
            }
×
3431
            return '#<class>';
×
3432
        }
×
3433
    }
×
3434
    if (fn.hasOwnProperty('__name__')) {
×
3435
        let name = fn.__name__;
×
3436
        if (typeof name === 'symbol') {
×
3437
            name = symbol_to_string(name);
×
3438
        }
×
3439
        if (typeof name === 'string') {
×
3440
            return `#<procedure:${name}>`;
×
3441
        }
×
3442
    }
×
3443
    if (has_own_function(fn, 'toString')) {
×
3444
        return fn.toString();
×
3445
    } else if (fn.name && !is_lambda(fn)) {
×
3446
        return `#<procedure:${fn.name.trim()}>`;
×
3447
    } else {
×
3448
        return '#<procedure>';
×
3449
    }
×
3450
}
×
3451
// ----------------------------------------------------------------------
1✔
3452
// Instances extracted to make cyclomatic complexity of toString smaller
1✔
3453
const instances = new Map();
1✔
3454
// ----------------------------------------------------------------------
1✔
3455
[
1✔
3456
    [Error, function(e) {
1✔
3457
        return e.message;
×
3458
    }],
1✔
3459
    [Pair, function(pair, { quote, skip_cycles, pair_args }) {
1✔
3460
        // make sure that repr directly after update set the cycle ref
221,448✔
3461
        if (!skip_cycles) {
221,448✔
3462
            pair.mark_cycles();
52,575✔
3463
        }
52,575✔
3464
        return pair.toString(quote, ...pair_args);
221,448✔
3465
    }],
1✔
3466
    [LCharacter, function(chr, { quote }) {
1✔
3467
        if (quote) {
380!
3468
            return chr.toString();
×
3469
        }
×
3470
        return chr.valueOf();
380✔
3471
    }],
1✔
3472
    [LString, function(str, { quote }) {
1✔
3473
        str = str.toString();
71,579✔
3474
        if (quote) {
71,579✔
3475
            return JSON.stringify(str).replace(/\\n/g, '\n');
607✔
3476
        }
607✔
3477
        return str;
70,972✔
3478
    }],
1✔
3479
    [RegExp, function(re) {
1✔
3480
        return '#' + re.toString();
23✔
3481
    }]
1✔
3482
].forEach(([cls, fn]) => {
1✔
3483
    instances.set(cls, fn);
5✔
3484
});
1✔
3485
// ----------------------------------------------------------------------
1✔
3486
const native_types = [
1✔
3487
    LSymbol,
1✔
3488
    Macro,
1✔
3489
    InputPort,
1✔
3490
    OutputPort,
1✔
3491
    Environment,
1✔
3492
    QuotedPromise
1✔
3493
];
1✔
3494
// ----------------------------------------------------------------------
1✔
3495
function to_string(obj, quote, skip_cycles, ...pair_args) {
643,221✔
3496
    if (typeof jQuery !== 'undefined' &&
643,221!
3497
        obj instanceof jQuery.fn.init) {
643,221!
3498
        return '#<jQuery(' + obj.length + ')>';
×
3499
    }
×
3500
    if (str_mapping.has(obj)) {
643,221✔
3501
        return str_mapping.get(obj);
485✔
3502
    }
485✔
3503
    if (is_prototype(obj)) {
643,221✔
3504
        return '#<prototype>';
3✔
3505
    }
3✔
3506
    if (obj) {
642,733✔
3507
        var cls = obj.constructor;
642,733✔
3508
        if (instances.has(cls)) {
642,733✔
3509
            return instances.get(cls)(obj, { quote, skip_cycles, pair_args });
293,430✔
3510
        }
293,430✔
3511
    }
642,733✔
3512
    // standard objects that have toString
349,303✔
3513
    for (let type of native_types) {
643,221✔
3514
        if (obj instanceof type) {
484,552✔
3515
            return obj.toString(quote);
322,261✔
3516
        }
322,261✔
3517
    }
484,552✔
3518
    if (obj instanceof LNumber) {
643,221✔
3519
        return obj.toString();
23,432✔
3520
    }
23,432✔
3521
    // constants
3,610✔
3522
    if ([nil, eof].includes(obj)) {
643,221✔
3523
        return obj.toString();
3,592✔
3524
    }
3,592✔
3525
    if (obj === root) {
643,221!
3526
        return '#<js:global>';
×
3527
    }
×
3528
    if (obj === null) {
643,221!
3529
        return 'null';
×
3530
    }
×
3531
    if (is_function(obj)) {
643,221✔
3532
        if (is_function(obj.toString) && obj.hasOwnProperty('toString')) {
3✔
3533
            // promises
3✔
3534
            return obj.toString().valueOf();
3✔
3535
        }
3✔
3536
        return function_to_string(obj);
×
3537
    }
×
3538
    if (typeof obj === 'object') {
643,221✔
3539
        var constructor = obj.constructor;
14✔
3540
        if (!constructor) {
14!
3541
            // This is case of fs.constants in Node.js that is null constructor object.
×
3542
            // This object can be handled like normal objects that have properties
×
3543
            constructor = Object;
×
3544
        }
×
3545
        var name;
14✔
3546
        if (typeof constructor.__class__ === 'string') {
14!
3547
            name = constructor.__class__;
×
3548
        } else {
14✔
3549
            var fn = user_repr(obj);
14✔
3550
            if (fn) {
14✔
3551
                if (is_function(fn)) {
14✔
3552
                    return fn(obj, quote);
14✔
3553
                } else {
14!
3554
                    throw new Error('toString: Invalid repr value');
×
3555
                }
×
3556
            }
14✔
3557
            name = constructor.name;
×
3558
        }
×
3559
        // user defined representation
×
3560
        if (is_function(obj.toString) && obj.hasOwnProperty('toString')) {
14!
3561
            return obj.toString().valueOf();
×
3562
        }
×
3563
        if (type(obj) === 'instance') {
×
3564
            if (is_lambda(constructor) && constructor.__name__) {
×
3565
                name = constructor.__name__.valueOf();
×
3566
                if (typeof name === 'symbol') {
×
3567
                    name = name.toString().replace(/^Symbol\((?:#:)?([^\)]+)\)$/, '$1');
×
3568
                }
×
3569
            } else if (!is_native_function(constructor)) {
×
3570
                name = 'instance';
×
3571
            }
×
3572
        }
×
3573
        if (is_iterator(obj, Symbol.iterator)) {
×
3574
            if (name) {
×
3575
                return `#<iterator(${name})>`;
×
3576
            }
×
3577
            return '#<iterator>';
×
3578
        }
×
3579
        if (is_iterator(obj, Symbol.asyncIterator)) {
×
3580
            if (name) {
×
3581
                return `#<asyncIterator(${name})>`;
×
3582
            }
×
3583
            return '#<asyncIterator>';
×
3584
        }
×
3585
        if (name !== '') {
×
3586
            return '#<' + name + '>';
×
3587
        }
×
3588
        return '#<Object>';
×
3589
    }
×
3590
    if (typeof obj !== 'string') {
643,221!
3591
        return obj.toString();
×
3592
    }
×
3593
    return obj;
1✔
3594
}
643,221✔
3595

1✔
3596
// ----------------------------------------------------------------------------
1✔
3597
Pair.prototype.mark_cycles = function() {
1✔
3598
    mark_cycles(this);
1,598,915✔
3599
    return this;
1,598,915✔
3600
};
1✔
3601

1✔
3602
// ----------------------------------------------------------------------------
1✔
3603
Pair.prototype.have_cycles = function(name = null) {
1✔
3604
    if (!name) {
267,416,625✔
3605
        return this.have_cycles('car') || this.have_cycles('cdr');
331✔
3606
    }
331✔
3607
    return !!(this[__cycles__] && this[__cycles__][name]);
267,416,625✔
3608
};
1✔
3609

1✔
3610
// ----------------------------------------------------------------------------
1✔
3611
Pair.prototype.is_cycle = function() {
1✔
3612
    return is_cycle(this);
×
3613
};
1✔
3614

1✔
3615
// ----------------------------------------------------------------------------
1✔
3616
function is_cycle(pair) {
×
3617
    if (!is_pair(pair)) {
×
3618
        return false;
×
3619
    }
×
3620
    if (pair.have_cycles()) {
×
3621
        return true;
×
3622
    }
×
3623
    return is_cycle(pair.car, fn) || is_cycle(pair.cdr, fn);
×
3624
}
×
3625

1✔
3626
// ----------------------------------------------------------------------------
1✔
3627
function mark_cycles(pair) {
1,598,915✔
3628
    var seen_pairs = [];
1,598,915✔
3629
    var cycles = [];
1,598,915✔
3630
    var refs = [];
1,598,915✔
3631
    function visit(pair) {
1,598,915✔
3632
        if (!seen_pairs.includes(pair)) {
47,488,326✔
3633
            seen_pairs.push(pair);
47,488,058✔
3634
        }
47,488,058✔
3635
    }
47,488,326✔
3636
    function set(node, type, child, parents) {
1,598,915✔
3637
        if (is_pair(child)) {
94,976,652✔
3638
            if (parents.includes(child)) {
45,889,428✔
3639
                if (!refs.includes(child)) {
17✔
3640
                    refs.push(child);
15✔
3641
                }
15✔
3642
                if (!node[__cycles__]) {
17✔
3643
                    node[__cycles__] = {};
15✔
3644
                }
15✔
3645
                node[__cycles__][type] = child;
17✔
3646
                if (!cycles.includes(node)) {
17✔
3647
                    cycles.push(node);
15✔
3648
                }
15✔
3649
                return true;
17✔
3650
            }
17✔
3651
        }
45,889,428✔
3652
    }
94,976,652✔
3653
    const detect = trampoline(function detect_thunk(pair, parents) {
1,598,915✔
3654
        if (is_pair(pair)) {
96,575,550✔
3655
            delete pair[__ref__];
47,488,326✔
3656
            delete pair[__cycles__];
47,488,326✔
3657
            visit(pair);
47,488,326✔
3658
            parents.push(pair);
47,488,326✔
3659
            var car = set(pair, 'car', pair.car, parents);
47,488,326✔
3660
            var cdr = set(pair, 'cdr', pair.cdr, parents);
47,488,326✔
3661
            if (!car) {
47,488,326✔
3662
                detect(pair.car, parents.slice());
47,488,324✔
3663
            }
47,488,324✔
3664
            if (!cdr) {
47,488,326✔
3665
                return new Thunk(() => {
47,488,311✔
3666
                    return detect_thunk(pair.cdr, parents.slice());
47,488,311✔
3667
                });
47,488,311✔
3668
            }
47,488,311✔
3669
        }
47,488,326✔
3670
    });
1,598,915✔
3671
    function mark_node(node, type) {
1,598,915✔
3672
        if (is_pair(node[__cycles__][type])) {
30✔
3673
            const count = ref_nodes.indexOf(node[__cycles__][type]);
17✔
3674
            node[__cycles__][type] = `#${count}#`;
17✔
3675
        }
17✔
3676
    }
30✔
3677
    detect(pair, []);
1,598,915✔
3678
    var ref_nodes = seen_pairs.filter(node => refs.includes(node));
1,598,915✔
3679
    ref_nodes.forEach((node, i) => {
1,598,915✔
3680
        node[__ref__] = `#${i}=`;
15✔
3681
    });
1,598,915✔
3682
    cycles.forEach(node => {
1,598,915✔
3683
        mark_node(node, 'car');
15✔
3684
        mark_node(node, 'cdr');
15✔
3685
    });
1,598,915✔
3686
}
1,598,915✔
3687

1✔
3688
// ----------------------------------------------------------------------
1✔
3689
// Trampoline based recursive pair to string that don't overflow the stack
1✔
3690
// ----------------------------------------------------------------------
1✔
3691
/* eslint-disable no-unused-vars */
1✔
3692
/* c8 ignore next */
1✔
3693
const pair_to_string = (function() {
1✔
3694
    const prefix = (pair, nested) => {
1✔
3695
        var result = [];
×
3696
        if (pair[__ref__]) {
×
3697
            result.push(pair[__ref__] + '(');
×
3698
        } else if (!nested) {
×
3699
            result.push('(');
×
3700
        }
×
3701
        return result;
×
3702
    };
1✔
3703
    const postfix = (pair, nested) => {
1✔
3704
        if (!nested || pair[__ref__]) {
×
3705
            return [')'];
×
3706
        }
×
3707
        return [];
×
3708
    };
1✔
3709
    return trampoline(function pairToString(pair, quote, extra = {}) {
1✔
3710
        const {
×
3711
            nested = false,
×
3712
            result = [],
×
3713
            cont = () => {
×
3714
                result.push(...postfix(pair, nested));
×
3715
            }
×
3716
        } = extra;
×
3717
        result.push(...prefix(pair, nested));
×
3718
        let car;
×
3719
        if (pair[__cycles__] && pair[__cycles__].car) {
×
3720
            car = pair[__cycles__].car;
×
3721
        } else {
×
3722
            car = to_string(pair.car, quote, true, { result, cont });
×
3723
        }
×
3724
        if (car !== undefined) {
×
3725
            result.push(car);
×
3726
        }
×
3727
        return new Thunk(() => {
×
3728
            if (is_pair(pair.cdr)) {
×
3729
                if (pair[__cycles__] && pair[__cycles__].cdr) {
×
3730
                    result.push(' . ');
×
3731
                    result.push(pair[__cycles__].cdr);
×
3732
                } else {
×
3733
                    if (pair.cdr[__ref__]) {
×
3734
                        result.push(' . ');
×
3735
                    } else {
×
3736
                        result.push(' ');
×
3737
                    }
×
3738
                    return pairToString(pair.cdr, quote, {
×
3739
                        nested: true,
×
3740
                        result,
×
3741
                        cont
×
3742
                    });
×
3743
                }
×
3744
            } else if (!is_nil(pair.cdr)) {
×
3745
                result.push(' . ');
×
3746
                result.push(to_string(pair.cdr, quote));
×
3747
            }
×
3748
        }, cont);
×
3749
    });
1✔
3750
})();
1✔
3751

1✔
3752
// ----------------------------------------------------------------------
1✔
3753
Pair.prototype.toString = function(quote, { nested = false } = {}) {
1✔
3754
    var arr = [];
536,985✔
3755
    if (this[__ref__]) {
536,985✔
3756
        arr.push(this[__ref__] + '(');
4✔
3757
    } else if (!nested) {
536,985✔
3758
        arr.push('(');
221,749✔
3759
    }
221,749✔
3760
    var value;
536,985✔
3761
    if (this[__cycles__] && this[__cycles__].car) {
536,985✔
3762
        value = this[__cycles__].car;
1✔
3763
    } else {
536,985✔
3764
        value = to_string(this.car, quote, true);
536,984✔
3765
    }
536,984✔
3766
    if (value !== undefined) {
536,985✔
3767
        arr.push(value);
536,985✔
3768
    }
536,985✔
3769
    if (is_pair(this.cdr)) {
536,985✔
3770
        if (this[__cycles__] && this[__cycles__].cdr) {
315,237✔
3771
            arr.push(' . ');
4✔
3772
            arr.push(this[__cycles__].cdr);
4✔
3773
        } else {
315,237✔
3774
            if (this.cdr[__ref__]) {
315,233✔
3775
                arr.push(' . ');
1✔
3776
            } else {
315,233✔
3777
                arr.push(' ');
315,232✔
3778
            }
315,232✔
3779
            const cdr = this.cdr.toString(quote, { nested: true });
315,233✔
3780
            arr.push(cdr);
315,233✔
3781
        }
315,233✔
3782
    } else if (!is_nil(this.cdr)) {
536,985✔
3783
        arr = arr.concat([' . ', to_string(this.cdr, quote, true)]);
779✔
3784
    }
779✔
3785
    if (!nested || this[__ref__]) {
536,985✔
3786
        arr.push(')');
221,753✔
3787
    }
221,753✔
3788
    return arr.join('');
536,985✔
3789
};
1✔
3790

1✔
3791
// ----------------------------------------------------------------------
1✔
3792
Pair.prototype.set = function(prop, value) {
1✔
3793
    this[prop] = value;
×
3794
    if (is_pair(value)) {
×
3795
        this.mark_cycles();
×
3796
    }
×
3797
};
1✔
3798

1✔
3799
// ----------------------------------------------------------------------
1✔
3800
Pair.prototype.append = function(arg) {
1✔
3801
    if (arg instanceof Array) {
45,794!
3802
        return this.append(Pair.fromArray(arg));
×
3803
    }
×
3804
    var p = this;
45,794✔
3805
    if (p.car === undefined) {
45,794!
3806
        if (is_pair(arg)) {
×
3807
            this.car = arg.car;
×
3808
            this.cdr = arg.cdr;
×
3809
        } else {
×
3810
            this.car = arg;
×
3811
        }
×
3812
    } else if (!is_nil(arg)) {
45,794✔
3813
        while (true) {
45,787✔
3814
            if (is_pair(p) && !is_nil(p.cdr)) {
57,716✔
3815
                p = p.cdr;
11,929✔
3816
            } else {
57,716✔
3817
                break;
45,787✔
3818
            }
45,787✔
3819
        }
57,716✔
3820
        p.cdr = arg;
45,787✔
3821
    }
45,787✔
3822
    return this;
45,794✔
3823
};
1✔
3824
// ----------------------------------------------------------------------
1✔
3825
Pair.prototype.serialize = function() {
1✔
3826
    return [
8✔
3827
        this.car,
8✔
3828
        this.cdr
8✔
3829
    ];
8✔
3830
};
1✔
3831
// ----------------------------------------------------------------------
1✔
3832
// :: List iterator (for do-iterator macro)
1✔
3833
// ----------------------------------------------------------------------
1✔
3834
Pair.prototype[Symbol.iterator] = function() {
1✔
3835
    var node = this;
125✔
3836
    return {
125✔
3837
        next: function() {
125✔
3838
            var cur = node;
376✔
3839
            node = cur.cdr;
376✔
3840
            if (is_nil(cur)) {
376✔
3841
                return { value: undefined, done: true };
125✔
3842
            } else {
376✔
3843
                return { value: cur.car, done: false };
251✔
3844
            }
251✔
3845
        }
376✔
3846
    };
125✔
3847
};
1✔
3848
// ----------------------------------------------------------------------
1✔
3849
// :: abs that work on BigInt
1✔
3850
// ----------------------------------------------------------------------
1✔
3851
function abs(x) {
×
3852
    return x < 0 ? -x : x;
×
3853
}
×
3854
// ----------------------------------------------------------------------
1✔
3855
function seq_compare(fn, args) {
143,005✔
3856
    var [a, ...rest] = args;
143,005✔
3857
    while (rest.length > 0) {
143,005✔
3858
        var [b] = rest;
143,007✔
3859
        if (!fn(a, b)) {
143,007✔
3860
            return false;
82,786✔
3861
        }
82,786✔
3862
        [a, ...rest] = rest;
60,221✔
3863
    }
60,221✔
3864
    return true;
60,219✔
3865
}
143,005✔
3866

1✔
3867
// ----------------------------------------------------------------------
1✔
3868
function equal(x, y) {
136,366✔
3869
    if (is_function(x)) {
136,366✔
3870
        return is_function(y) && unbind(x) === unbind(y);
20✔
3871
    } else if (x instanceof LNumber) {
136,366✔
3872
        if (!(y instanceof LNumber)) {
3,853✔
3873
            return false;
1,228✔
3874
        }
1,228✔
3875
        let type;
2,625✔
3876
        if (x.__type__ === y.__type__) {
3,853✔
3877
            if (x.__type__ === 'complex') {
644✔
3878
                type = x.__im__.__type__ === y.__im__.__type__ &&
4✔
3879
                    x.__re__.__type__ === y.__re__.__type__;
4✔
3880
            } else {
644✔
3881
                type = true;
640✔
3882
            }
640✔
3883
            if (type && x.cmp(y) === 0) {
644✔
3884
                if (x.valueOf() === 0) {
41✔
3885
                    return Object.is(x.valueOf(), y.valueOf());
5✔
3886
                }
5✔
3887
                return true;
36✔
3888
            }
36✔
3889
        }
644✔
3890
        return false;
2,584✔
3891
    } else if (typeof x === 'number') {
136,346!
3892
        if (typeof y !== 'number') {
×
3893
            return false;
×
3894
        }
×
3895
        if (Number.isNaN(x)) {
×
3896
            return Number.isNaN(y);
×
3897
        }
×
3898
        if (x === Number.NEGATIVE_INFINITY) {
×
3899
            return y === Number.NEGATIVE_INFINITY;
×
3900
        }
×
3901
        if (x === Number.POSITIVE_INFINITY) {
×
3902
            return y === Number.POSITIVE_INFINITY;
×
3903
        }
×
3904
        return equal(LNumber(x), LNumber(y));
×
3905
    } else if (x instanceof LCharacter) {
132,493✔
3906
        if (!(y instanceof LCharacter)) {
183✔
3907
            return false;
155✔
3908
        }
155✔
3909
        return x.__char__ === y.__char__;
28✔
3910
    } else {
132,493✔
3911
        return x === y;
132,310✔
3912
    }
132,310✔
3913
}
136,366✔
3914
// ----------------------------------------------------------------------
1✔
3915
function same_atom(a, b) {
1,146✔
3916
    if (type(a) !== type(b)) {
1,146✔
3917
        return false;
472✔
3918
    }
472✔
3919
    if (!is_atom(a)) {
1,146!
3920
        return false;
×
3921
    }
×
3922
    if (a instanceof RegExp) {
1,146!
3923
        return a.source === b.source;
×
3924
    }
×
3925
    if (a instanceof LString) {
1,146✔
3926
        return a.valueOf() === b.valueOf();
39✔
3927
    }
39✔
3928
    return equal(a, b);
635✔
3929
}
1,146✔
3930
// ----------------------------------------------------------------------
1✔
3931
function is_atom(obj) {
9,486✔
3932
    return obj instanceof LSymbol ||
9,486✔
3933
        LString.isString(obj) ||
9,486✔
3934
        is_nil(obj) ||
9,486✔
3935
        obj === null ||
9,486✔
3936
        obj instanceof LCharacter ||
9,486✔
3937
        obj instanceof LNumber ||
9,486✔
3938
        obj === true ||
9,486✔
3939
        obj === false;
9,486✔
3940
}
9,486✔
3941
// ----------------------------------------------------------------------
1✔
3942
var truncate = (function() {
1✔
3943
    if (Math.trunc) {
1✔
3944
        return Math.trunc;
1✔
3945
    } else {
1!
3946
        return function(x) {
×
3947
            if (x === 0) {
×
3948
                return 0;
×
3949
            } else if (x < 0) {
×
3950
                return Math.ceil(x);
×
3951
            } else {
×
3952
                return Math.floor(x);
×
3953
            }
×
3954
        };
×
3955
    }
×
3956
})();
1✔
3957
// ----------------------------------------------------------------------
1✔
3958
// :: Macro constructor
1✔
3959
// ----------------------------------------------------------------------
1✔
3960
function Macro(name, fn, doc, dump) {
94✔
3961
    if (typeof this !== 'undefined' && this.constructor !== Macro ||
94✔
3962
        typeof this === 'undefined') {
94!
3963
        return new Macro(name, fn);
×
3964
    }
×
3965
    typecheck('Macro', name, 'string', 1);
94✔
3966
    typecheck('Macro', fn, 'function', 2);
94✔
3967
    if (doc) {
94✔
3968
        if (dump) {
46✔
3969
            this.__doc__ = doc;
41✔
3970
        } else {
46✔
3971
            this.__doc__ = trim_lines(doc);
5✔
3972
        }
5✔
3973
    }
46✔
3974
    this.__name__ = name;
94✔
3975
    this.__fn__ = fn;
94✔
3976
}
94✔
3977
// ----------------------------------------------------------------------
1✔
3978
Macro.defmacro = function(name, fn, doc, dump) {
1✔
3979
    var macro = new Macro(name, fn, doc, dump);
71✔
3980
    macro.__defmacro__ = true;
71✔
3981
    return macro;
71✔
3982
};
1✔
3983
// ----------------------------------------------------------------------
1✔
3984
Macro.prototype.invoke = function(code, { env, ...rest }, macro_expand) {
1✔
3985
    var args = {
3,465,551✔
3986
        ...rest,
3,465,551✔
3987
        macro_expand
3,465,551✔
3988
    };
3,465,551✔
3989
    var result = this.__fn__.call(env, code, args, this.__name__);
3,465,551✔
3990
    return result;
3,465,551✔
3991
    //return macro_expand ? quote(result) : result;
3,465,551✔
3992
};
1✔
3993
// ----------------------------------------------------------------------
1✔
3994
Macro.prototype.toString = function() {
1✔
3995
    return `#<macro:${this.__name__}>`;
×
3996
};
1✔
3997
// ----------------------------------------------------------------------
1✔
3998
const macro = 'define-macro';
1✔
3999
// ----------------------------------------------------------------------
1✔
4000
// :: define-macro macro
1✔
4001
// ----------------------------------------------------------------------
1✔
4002
function is_lambda_macro(macro) {
64✔
4003
    return macro.car instanceof LSymbol &&
64✔
4004
        is_pair(macro.cdr) &&
64✔
4005
        is_pair(macro.cdr.car) &&
64✔
4006
        LSymbol.is(macro.cdr.car.car, 'lambda');
64✔
4007
}
64✔
4008
// ----------------------------------------------------------------------
1✔
4009
function is_named_macro(macro) {
64✔
4010
    return is_pair(macro.car) && macro.car.car instanceof LSymbol;
64✔
4011
}
64✔
4012
// ----------------------------------------------------------------------
1✔
4013
function define_macro(name, args, body, __doc__, { use_dynamic, error }) {
64✔
4014
    const makro_instance = Macro.defmacro(name, function(code) {
64✔
4015
        const env = macro_args_env(args, code, this);
84,293✔
4016
        const eval_args = {
84,293✔
4017
            env,
84,293✔
4018
            dynamic_env: env,
84,293✔
4019
            use_dynamic,
84,293✔
4020
            error
84,293✔
4021
        };
84,293✔
4022
        // evaluate macro
84,293✔
4023
        if (is_pair(body)) {
84,293✔
4024
            // this eval will return lips code
84,293✔
4025
            const result = body.reduce(function(result, node) {
84,293✔
4026
                return evaluate(node, eval_args);
84,293✔
4027
            });
84,293✔
4028
            return unpromise(result, function(result) {
84,293✔
4029
                if (typeof result === 'object') {
84,293✔
4030
                    delete result[__data__];
84,291✔
4031
                }
84,291✔
4032
                return result;
84,293✔
4033
            });
84,293✔
4034
        }
84,293✔
4035
    }, __doc__, true);
64✔
4036
    makro_instance.__code__ = new Pair(new LSymbol('define-macro'), macro);
64✔
4037
    return makro_instance;
64✔
4038
}
64✔
4039
// ----------------------------------------------------------------------
1✔
4040
function macro_args_env(params, code, scope) {
84,293✔
4041
    const env = new Environment({}, scope, 'defmacro');
84,293✔
4042
    let arg = code;
84,293✔
4043
    while (true) {
84,293✔
4044
        if (is_nil(params)) {
131,743✔
4045
            break;
3✔
4046
        }
3✔
4047
        if (params instanceof LSymbol) {
131,743✔
4048
            env.__env__[params.__name__] = arg;
82,608✔
4049
            break;
82,608✔
4050
        } else if (!is_nil(params.car)) {
131,743✔
4051
            if (is_nil(arg)) {
49,132!
4052
                env.__env__[params.car.__name__] = nil;
×
4053
            } else {
49,132✔
4054
                if (is_pair(arg.car)) {
49,132✔
4055
                    arg.car[__data__] = true;
24,949✔
4056
                }
24,949✔
4057
                env.__env__[params.car.__name__] = arg.car;
49,132✔
4058
            }
49,132✔
4059
        }
49,132✔
4060
        if (is_nil(params.cdr)) {
131,743✔
4061
            break;
1,682✔
4062
        }
1,682✔
4063
        if (!is_nil(arg)) {
47,450✔
4064
            arg = arg.cdr;
47,450✔
4065
        }
47,450✔
4066
        params = params.cdr;
47,450✔
4067
    }
47,450✔
4068
    return env;
84,293✔
4069
}
84,293✔
4070
// ----------------------------------------------------------------------
1✔
4071
const recur_guard = -10000;
1✔
4072
function macro_expand(single) {
2✔
4073
    return async function(code, args) {
2✔
4074
        const env = args['env'] = this;
7✔
4075
        let bindings = [];
7✔
4076
        const let_names = ['let', 'let*', 'letrec', 'letrec*'];
7✔
4077
        const let_macros = let_names.map(name => {
7✔
4078
            return global_env.get(name);
28✔
4079
        });
7✔
4080
        var lambda = global_env.get('lambda');
7✔
4081
        var define = global_env.get('define');
7✔
4082
        function is_let_macro(name) {
7✔
4083
            return let_names.includes(name);
68✔
4084
        }
68✔
4085
        function builtin_let(name) {
7✔
4086
            if (!is_let_macro(name)) {
14✔
4087
                return false;
10✔
4088
            }
10✔
4089
            return let_macros.includes(env.get(name));
4✔
4090
        }
14✔
4091
        function is_procedure(value, node) {
7✔
4092
            return value === define && is_pair(node.cdr.car);
50✔
4093
        }
50✔
4094
        function is_lambda(value) {
7✔
4095
            return value === lambda;
49✔
4096
        }
49✔
4097
        function proc_bindings(node) {
7✔
4098
            var names = [];
3✔
4099
            while (true) {
3✔
4100
                if (!is_nil(node)) {
7✔
4101
                    if (node instanceof LSymbol) {
4!
4102
                        names.push(node.valueOf());
×
4103
                        break;
×
4104
                    }
×
4105
                    names.push(node.car.valueOf());
4✔
4106
                    node = node.cdr;
4✔
4107
                } else {
7✔
4108
                    break;
3✔
4109
                }
3✔
4110
            }
7✔
4111
            return [...bindings, ...names];
3✔
4112
        }
3✔
4113
        function let_binding(node) {
7✔
4114
            return [...bindings, ...node.to_array(false).map(function(node) {
4✔
4115
                if (is_pair(node)) {
4✔
4116
                    return node.car.valueOf();
4✔
4117
                }
4✔
4118
                const t = type(node);
×
4119
                const msg = `macroexpand: Invalid let binding expectig pair got ${t}`;
×
4120
                throw new Error(msg);
×
4121
            })];
4✔
4122
        }
4✔
4123
        function is_macro(name, value) {
7✔
4124
            return value instanceof Macro &&
54✔
4125
                value.__defmacro__ &&
54✔
4126
                !bindings.includes(name);
54✔
4127
        }
54✔
4128
        async function expand_let_binding(node, n) {
7✔
4129
            if (is_nil(node)) {
8✔
4130
                return nil;
4✔
4131
            }
4✔
4132
            var pair = node.car;
4✔
4133
            return new Pair(
4✔
4134
                new Pair(
4✔
4135
                    pair.car,
4✔
4136
                    await traverse(pair.cdr, n, env)
4✔
4137
                ),
4✔
4138
                await expand_let_binding(node.cdr)
4✔
4139
            );
4✔
4140
        }
8✔
4141
        async function traverse(node, n, env) {
7✔
4142
            if (is_pair(node) && node.car instanceof LSymbol) {
97✔
4143
                if (node[__data__]) {
54!
4144
                    return node;
×
4145
                }
×
4146
                var name = node.car.valueOf();
54✔
4147
                var value = env.get(node.car, { throwError: false });
54✔
4148
                var is_let = is_let_macro(name);
54✔
4149

54✔
4150
                var is_binding = is_let ||
54✔
4151
                    is_procedure(value, node) ||
54✔
4152
                    is_lambda(value);
54✔
4153

54✔
4154
                if (is_macro(name, value) && !builtin_let(name)) {
54✔
4155
                    var code = value instanceof Syntax ? node : node.cdr;
10✔
4156
                    var result = await value.invoke(code, { ...args, env }, true);
10✔
4157
                    if (value instanceof Syntax) {
10✔
4158
                        const { expr, scope } = result;
1✔
4159
                        if (is_pair(expr)) {
1✔
4160
                            if (n !== -1 && n <= 1 || n < recur_guard) {
1!
4161
                                return expr;
×
4162
                            }
×
4163
                            if (n !== -1) {
1!
4164
                                n = n - 1;
×
4165
                            }
×
4166
                            return traverse(expr, n, scope);
1✔
4167
                        }
1✔
4168
                        result = expr;
×
4169
                    }
×
4170
                    if (result instanceof LSymbol) {
10!
4171
                        return quote(result);
×
4172
                    }
×
4173
                    if (is_pair(result)) {
10✔
4174
                        if (n !== -1 && n <= 1 || n < recur_guard) {
7!
4175
                            return result;
×
4176
                        }
×
4177
                        if (n !== -1) {
7!
4178
                            n = n - 1;
×
4179
                        }
×
4180
                        return traverse(result, n, env);
7✔
4181
                    }
7✔
4182
                    if (is_atom(result)) {
10!
4183
                        return result;
×
4184
                    }
×
4185
                } else if (is_binding && is_pair(node.cdr.car)) {
54✔
4186
                    var second;
7✔
4187
                    if (is_let) {
7✔
4188
                        bindings = let_binding(node.cdr.car);
4✔
4189
                        second = await expand_let_binding(node.cdr.car, n);
4✔
4190
                    } else {
7✔
4191
                        bindings = proc_bindings(node.cdr.car);
3✔
4192
                        second = node.cdr.car;
3✔
4193
                    }
3✔
4194
                    return new Pair(
7✔
4195
                        node.car,
7✔
4196
                        new Pair(
7✔
4197
                            second,
7✔
4198
                            await traverse(node.cdr.cdr, n, env)
7✔
4199
                        )
7✔
4200
                    );
7✔
4201
                }
7✔
4202
            }
54✔
4203
            // TODO: CYCLE DETECT
82✔
4204
            var car = node.car;
82✔
4205
            if (is_pair(car)) {
97✔
4206
                car = await traverse(car, n, env);
28✔
4207
            }
28✔
4208
            var cdr = node.cdr;
82✔
4209
            if (is_pair(cdr)) {
97✔
4210
                cdr = await traverse(cdr, n, env);
43✔
4211
            }
43✔
4212
            var pair = new Pair(car, cdr);
82✔
4213
            return pair;
82✔
4214
        }
97✔
4215
        //var this.__code__ = code;
7✔
4216
        if (is_pair(code.cdr) && LNumber.isNumber(code.cdr.car)) {
7!
4217
            return quote((await traverse(code, code.cdr.car.valueOf(), env)).car);
×
4218
        }
×
4219
        if (single) {
7!
4220
            return quote((await traverse(code, 1, env)).car);
×
4221
        }
×
4222
        return quote((await traverse(code, -1, env)).car);
7✔
4223
    };
2✔
4224
}
2✔
4225
// ----------------------------------------------------------------------
1✔
4226
// TODO: Don't put Syntax as Macro they are not runtime
1✔
4227
// ----------------------------------------------------------------------
1✔
4228
function Syntax(fn, env) {
193✔
4229
    this.__env__ = env;
193✔
4230
    this.__fn__ = fn;
193✔
4231
    // allow macroexpand
193✔
4232
    this.__defmacro__ = true;
193✔
4233
}
193✔
4234
Syntax.__merge_env__ = Symbol.for('merge');
1✔
4235
// ----------------------------------------------------------------------
1✔
4236
Syntax.prototype = Object.create(Macro.prototype);
1✔
4237
Syntax.prototype.invoke = function(code, { error, env, use_dynamic }, macro_expand) {
1✔
4238
    var args = {
525✔
4239
        error,
525✔
4240
        env,
525✔
4241
        use_dynamic,
525✔
4242
        dynamic_env: this.__env__,
525✔
4243
        macro_expand
525✔
4244
    };
525✔
4245
    return this.__fn__.call(env, code, args, this.__name__ || 'syntax');
525✔
4246
};
1✔
4247
Syntax.prototype.constructor = Syntax;
1✔
4248
Syntax.prototype.toString = function() {
1✔
4249
    if (this.__name__) {
×
4250
        return `#<syntax:${this.__name__}>`;
×
4251
    }
×
4252
    return '#<syntax>';
×
4253
};
1✔
4254
// ----------------------------------------------------------------------
1✔
4255
// :: SRFI-139
1✔
4256
// ----------------------------------------------------------------------
1✔
4257
class SyntaxParameter {
1✔
4258
    constructor(syntax) {
1✔
4259
        read_only(this, '_syntax', syntax, { hidden: true });
21✔
4260
        read_only(this._syntax, '_param', true, { hidden: true });
21✔
4261
    }
21✔
4262
}
1✔
4263
Syntax.Parameter = SyntaxParameter;
1✔
4264
// ----------------------------------------------------------------------
1✔
4265
// :: for usage in syntax-rule when pattern match it will return
1✔
4266
// :: list of bindings from code that match the pattern
1✔
4267
// :: TODO detect cycles
1✔
4268
// ----------------------------------------------------------------------
1✔
4269
function extract_patterns(pattern, code, symbols, ellipsis_symbol, scope = {}) {
1,027✔
4270
    const bindings = {
1,027✔
4271
        '...': {
1,027✔
4272
            symbols: { }, // symbols ellipsis (x ...)
1,027✔
4273
            lists: [ ]
1,027✔
4274
        },
1,027✔
4275
        symbols: { }
1,027✔
4276
    };
1,027✔
4277
    const { expansion, define } = scope;
1,027✔
4278
    // pattern_names parameter is used to distinguish
1,027✔
4279
    // multiple matches of ((x ...) ...) against ((1 2 3) (1 2 3))
1,027✔
4280
    // in loop we add x to the list so we know that this is not
1,027✔
4281
    // duplicated ellipsis symbol
1,027✔
4282
    log(symbols);
1,027✔
4283
    /* eslint-disable complexity */
1,027✔
4284
    function traverse(pattern, code, state = {}) {
1,027✔
4285
        const { ellipsis = false, trailing = false, pattern_names = [] } = state;
8,810✔
4286
        log({
8,810✔
4287
            code,
8,810✔
4288
            pattern
8,810✔
4289
        });
8,810✔
4290
        if (is_atom(pattern) && !(pattern instanceof LSymbol)) {
8,810✔
4291
            return same_atom(pattern, code);
1,146✔
4292
        }
1,146✔
4293
        if (pattern instanceof LSymbol) {
8,810✔
4294
            const literal = pattern.literal(); // TODO: literal() may be SLOW
2,867✔
4295
            if (symbols.includes(literal)) {
2,867✔
4296
                if (!LSymbol.is(code, literal) && !LSymbol.is(pattern, code)) {
354✔
4297
                    return false;
140✔
4298
                }
140✔
4299
                const ref = expansion.ref(literal);
214✔
4300
                return !ref || ref === define || ref === global_env;
354✔
4301
            }
354✔
4302
        }
2,867✔
4303
        if (Array.isArray(pattern) && Array.isArray(code)) {
8,810✔
4304
            log('<<< a 1');
62✔
4305
            if (pattern.length === 0 && code.length === 0) {
62✔
4306
                return true;
12✔
4307
            }
12✔
4308
            if (LSymbol.is(pattern[1], ellipsis_symbol)) {
62✔
4309
                if (pattern[0] instanceof LSymbol) {
21✔
4310
                    const name = pattern[0].valueOf();
19✔
4311
                    log('<<< a 2 ' + ellipsis);
19✔
4312
                    if (ellipsis) {
19✔
4313
                        const count = code.length - 2;
19✔
4314
                        const array_head = count > 0 ? code.slice(0, count) : code;
19✔
4315
                        const as_list = Pair.fromArray(array_head, false);
19✔
4316
                        if (!bindings['...'].symbols[name]) {
19✔
4317
                            bindings['...'].symbols[name] = new Pair(as_list, nil);
7✔
4318
                        } else {
19✔
4319
                            bindings['...'].symbols[name].append(new Pair(as_list, nil));
12✔
4320
                        }
12✔
4321
                    } else {
19!
4322
                        bindings['...'].symbols[name] = Pair.fromArray(code, false);
×
4323
                    }
×
4324
                } else if (Array.isArray(pattern[0])) {
21✔
4325
                    log('<<< a 3');
2✔
4326
                    const names = [...pattern_names];
2✔
4327
                    let node = code;
2✔
4328
                    const new_state = { ...state, pattern_names: names, ellipsis: true };
2✔
4329
                    if (!code.every(node => traverse(pattern[0], node, new_state))) {
2!
4330
                        return false;
×
4331
                    }
×
4332
                }
2✔
4333
                if (pattern.length > 2) {
21✔
4334
                    const pat = pattern.slice(2);
12✔
4335
                    return traverse(pat, code.slice(-pat.length), state);
12✔
4336
                }
12✔
4337
                return true;
9✔
4338
            }
9✔
4339
            const first = traverse(pattern[0], code[0], state);
29✔
4340
            log({first, pattern: pattern[0], code: code[0]});
29✔
4341
            const rest = traverse(pattern.slice(1), code.slice(1), state);
29✔
4342
            log({first, rest});
29✔
4343
            return first && rest;
62✔
4344
        }
62✔
4345
        // pattern (a b (x ...)) and (x ...) match nil
7,248✔
4346
        if (is_pair(pattern) &&
8,810✔
4347
            is_pair(pattern.car) &&
8,810✔
4348
            is_pair(pattern.car.cdr) &&
8,810✔
4349
            LSymbol.is(pattern.car.cdr.car, ellipsis_symbol)) {
8,810✔
4350
            log('>> 0');
386✔
4351
            if (is_nil(code)) {
386✔
4352
                log({ pattern });
7✔
4353
                if (pattern.car.car instanceof LSymbol) {
7✔
4354
                    let name = pattern.car.car.valueOf();
7✔
4355
                    if (bindings['...'].symbols[name]) {
7!
4356
                        throw new Error('syntax: named ellipsis can only ' +
×
4357
                                        'appear onces');
×
4358
                    }
×
4359
                    bindings['...'].symbols[name] = code;
7✔
4360
                }
7✔
4361
            }
7✔
4362
        }
386✔
4363
        if (is_pair(pattern) &&
8,810✔
4364
            is_pair(pattern.cdr) &&
8,810✔
4365
            LSymbol.is(pattern.cdr.car, ellipsis_symbol)) {
8,810✔
4366
            log('>> 1 (a)');
927✔
4367
            // pattern (... ???) - SRFI-46
927✔
4368
            if (!is_nil(pattern.cdr.cdr)) {
927✔
4369
                if (is_pair(pattern.cdr.cdr)) {
30✔
4370
                    log('>> 1 (b)');
26✔
4371
                    // if we have (x ... a b) we need to remove two from the end
26✔
4372
                    const list_len = pattern.cdr.cdr.length();
26✔
4373
                    const improper_list = !is_nil(pattern.last_pair().cdr);
26✔
4374
                    if (!is_pair(code)) {
26✔
4375
                        return false;
3✔
4376
                    }
3✔
4377
                    let code_len = code.length();
23✔
4378
                    let list = code;
23✔
4379
                    const trailing = improper_list ? 1 : 1;
26✔
4380
                    while (code_len - trailing > list_len) {
26✔
4381
                        list = list.cdr;
39✔
4382
                        code_len--;
39✔
4383
                    }
39✔
4384
                    const rest = list.cdr;
23✔
4385
                    list.cdr = nil;
23✔
4386
                    const new_sate = { ...state, trailing: improper_list };
23✔
4387
                    if (!traverse(pattern.cdr.cdr, rest, new_sate)) {
26!
4388
                        return false;
×
4389
                    }
×
4390
                }
26✔
4391
            }
30✔
4392
            if (pattern.car instanceof LSymbol) {
927✔
4393
                let name = pattern.car.__name__;
782✔
4394
                if (bindings['...'].symbols[name] &&
782✔
4395
                    !pattern_names.includes(name) && !ellipsis) {
782!
4396
                    throw new Error('syntax: named ellipsis can only appear onces');
×
4397
                }
×
4398
                log('>> 1 (next)');
782✔
4399
                if (is_nil(code)) {
782✔
4400
                    log('>> 2');
182✔
4401
                    if (ellipsis) {
182✔
4402
                        log('NIL');
4✔
4403
                        bindings['...'].symbols[name] = nil;
4✔
4404
                    } else {
182✔
4405
                        log('NULL');
178✔
4406
                        bindings['...'].symbols[name] = null;
178✔
4407
                    }
178✔
4408
                } else if (is_pair(code) && (is_pair(code.car) || is_nil(code.car))) {
782✔
4409
                    log('>> 3 ' + ellipsis);
184✔
4410
                    if (ellipsis) {
184✔
4411
                        if (bindings['...'].symbols[name]) {
7✔
4412
                            let node = bindings['...'].symbols[name];
4✔
4413
                            if (is_nil(node)) {
4✔
4414
                                node = new Pair(nil, new Pair(code, nil));
1✔
4415
                            } else {
4✔
4416
                                node = node.append(new Pair(code, nil));
3✔
4417
                            }
3✔
4418
                            bindings['...'].symbols[name] = node;
4✔
4419
                        } else {
7✔
4420
                            bindings['...'].symbols[name] = new Pair(code, nil);
3✔
4421
                        }
3✔
4422
                    } else {
184✔
4423
                        log('>> 4');
177✔
4424
                        bindings['...'].symbols[name] = new Pair(code, nil);
177✔
4425
                    }
177✔
4426
                } else {
600✔
4427
                    log('>> 6');
416✔
4428
                    if (is_pair(code)) {
416✔
4429
                        log('>> 7 ' + ellipsis);
332✔
4430
                        // cons (a . b) => (var ... . x)
332✔
4431
                        if (!is_pair(code.cdr) && !is_nil(code.cdr)) {
332✔
4432
                            log('>> 7 (b)');
1✔
4433
                            if (is_nil(pattern.cdr.cdr)) {
1!
4434
                                return false;
×
4435
                            } else if (!bindings['...'].symbols[name]) {
1✔
4436
                                bindings['...'].symbols[name] = new Pair(code.car, nil);
1✔
4437
                                return traverse(pattern.cdr.cdr, code.cdr, state);
1✔
4438
                            }
1✔
4439
                        }
1✔
4440
                        // code as improper list
331✔
4441
                        const last_pair = code.last_pair();
331✔
4442
                        log({ last_pair });
331✔
4443
                        if (!is_nil(last_pair.cdr)) {
332✔
4444
                            log('>> 7 (c)')
2✔
4445
                            if (is_nil(pattern.cdr.cdr)) {
2✔
4446
                                // case (a ...) for (a b . x)
1✔
4447
                                return false;
1✔
4448
                            } else {
1✔
4449
                                log('>> 7 (d)');
1✔
4450
                                // case (a ... . b) for (a b . x)
1✔
4451
                                const copy = code.clone();
1✔
4452
                                copy.last_pair().cdr = nil;
1✔
4453
                                bindings['...'].symbols[name] = copy;
1✔
4454
                                return traverse(pattern.cdr.cdr, last_pair.cdr, state);
1✔
4455
                            }
1✔
4456
                        }
2✔
4457
                        pattern_names.push(name);
329✔
4458
                        if (!bindings['...'].symbols[name]) {
332✔
4459
                            log('>> 7 (e)');
300✔
4460
                            bindings['...'].symbols[name] = new Pair(
300✔
4461
                                code,
300✔
4462
                                nil
300✔
4463
                            );
300✔
4464
                        } else {
332✔
4465
                            log('>> 7 (f)');
29✔
4466
                            const node = bindings['...'].symbols[name];
29✔
4467
                            bindings['...'].symbols[name] = node.append(
29✔
4468
                                new Pair(
29✔
4469
                                    code,
29✔
4470
                                    nil
29✔
4471
                                )
29✔
4472
                            );
29✔
4473
                        }
29✔
4474
                        log({ IIIIII: bindings['...'].symbols[name] });
329✔
4475
                    } else if (pattern.car instanceof LSymbol &&
416✔
4476
                               is_pair(pattern.cdr) &&
84✔
4477
                               LSymbol.is(pattern.cdr.car, ellipsis_symbol)) {
84✔
4478
                        // empty ellipsis with rest  (a b ... . d) #290
84✔
4479
                        log('>> 8');
84✔
4480
                        bindings['...'].symbols[name] = null;
84✔
4481
                        return traverse(pattern.cdr.cdr, code, state);
84✔
4482
                    } else {
84!
4483
                        log('>> 9');
×
4484
                        return false;
×
4485
                        //bindings['...'].symbols[name] = code;
×
4486
                    }
×
4487
                }
416✔
4488
                return true;
695✔
4489
            } else if (is_pair(pattern.car)) {
927✔
4490
                var names = [...pattern_names];
135✔
4491
                if (is_nil(code)) {
135✔
4492
                    log('>> 10');
26✔
4493
                    bindings['...'].lists.push(nil);
26✔
4494
                    return true;
26✔
4495
                }
26✔
4496
                log('>> 11');
109✔
4497
                let node = code;
109✔
4498
                const new_state = { ...state, pattern_names: names, ellipsis: true };
109✔
4499
                while (is_pair(node)) {
135✔
4500
                    if (!traverse(pattern.car, node.car, new_state)) {
150✔
4501
                        return false;
15✔
4502
                    }
15✔
4503
                    node = node.cdr;
135✔
4504
                }
135✔
4505
                return true;
94✔
4506
            } if (Array.isArray(pattern.car) ) {
927✔
4507
                var names = [...pattern_names];
7✔
4508
                let node = code;
7✔
4509
                const new_state = { ...state, pattern_names: names, ellipsis: true };
7✔
4510
                while (is_pair(node)) {
7✔
4511
                    if (!traverse(pattern.car, node.car, new_state)) {
18!
4512
                        return false;
×
4513
                    }
×
4514
                    node = node.cdr;
18✔
4515
                }
18✔
4516
                return true;
7✔
4517
            }
7✔
4518
            return false;
×
4519
        }
×
4520
        if (pattern instanceof LSymbol) {
8,810✔
4521
            if (LSymbol.is(pattern, ellipsis_symbol)) {
2,513!
4522
                throw new Error('syntax: invalid usage of ellipsis');
×
4523
            }
×
4524
            log('>> 12');
2,513✔
4525
            const name = pattern.__name__;
2,513✔
4526
            if (symbols.includes(name)) {
2,513!
4527
                return true;
×
4528
            }
×
4529
            if (ellipsis) {
2,513✔
4530
                log(bindings['...'].symbols[name]);
257✔
4531
                bindings['...'].symbols[name] ??= [];
257✔
4532
                bindings['...'].symbols[name].push(code);
257✔
4533
            } else {
2,513✔
4534
                bindings.symbols[name] = code;
2,256✔
4535
            }
2,256✔
4536
            return true;
2,513✔
4537
        }
2,513✔
4538
        if (is_pair(pattern) && is_pair(code)) {
8,810✔
4539
            log('>> 13');
3,713✔
4540
            log({
3,713✔
4541
                a: 13,
3,713✔
4542
                code,
3,713✔
4543
                pattern
3,713✔
4544
            });
3,713✔
4545
            const rest_pattern = pattern.car instanceof LSymbol &&
3,713✔
4546
                pattern.cdr instanceof LSymbol;
3,713✔
4547
            if (trailing && rest_pattern) {
3,713✔
4548
                log('>> 13 (a)');
2✔
4549
                // handle (x ... y . z)
2✔
4550
                if (!is_nil(code.cdr)) {
2✔
4551
                    return false;
1✔
4552
                }
1✔
4553
                const car = pattern.car.valueOf();
1✔
4554
                const cdr = pattern.cdr.valueOf();
1✔
4555
                bindings.symbols[car] = code.car;
1✔
4556
                bindings.symbols[cdr] = nil;
1✔
4557
                return true;
1✔
4558
                //return is_pair(code.cdr) && code.cdr.length() > 1;
1✔
4559
            }
1✔
4560
            if (is_nil(code.cdr)) {
3,713✔
4561
                log('>> 13 (b)');
802✔
4562
                // last item in in call using in recursive calls on
802✔
4563
                // last element of the list
802✔
4564
                // case of pattern (p . rest) and code (0)
802✔
4565
                if (rest_pattern) {
802✔
4566
                    // fix for SRFI-26 in recursive call of (b) ==> (<> . x)
13✔
4567
                    // where <> is symbol
13✔
4568
                    if (!traverse(pattern.car, code.car, state)) {
13✔
4569
                        return false;
1✔
4570
                    }
1✔
4571
                    log('>> 14');
12✔
4572
                    let name = pattern.cdr.valueOf();
12✔
4573
                    if (!(name in bindings.symbols)) {
13✔
4574
                        bindings.symbols[name] = nil;
11✔
4575
                    }
11✔
4576
                    name = pattern.car.valueOf();
12✔
4577
                    if (!(name in bindings.symbols)) {
13!
4578
                        bindings.symbols[name] = code.car;
×
4579
                    }
×
4580
                    return true;
12✔
4581
                }
12✔
4582
            }
802✔
4583
            log({
3,698✔
4584
                pattern,
3,698✔
4585
                code
3,698✔
4586
            });
3,698✔
4587
            // case (x y) ===> (var0 var1 ... warn) where var1 match nil
3,698✔
4588
            // trailing: true start processing of (var ... x . y)
3,698✔
4589
            if (is_pair(pattern.cdr) &&
3,713✔
4590
                is_pair(pattern.cdr.cdr) &&
3,713✔
4591
                pattern.cdr.car instanceof LSymbol &&
3,713✔
4592
                LSymbol.is(pattern.cdr.cdr.car, ellipsis_symbol) &&
3,713✔
4593
                is_pair(pattern.cdr.cdr.cdr) &&
3,713✔
4594
                !LSymbol.is(pattern.cdr.cdr.cdr.car, ellipsis_symbol) &&
3,713✔
4595
                traverse(pattern.car, code.car, state) &&
3,713✔
4596
                traverse(pattern.cdr.cdr.cdr, code.cdr, {...state, trailing: true })) {
3,713✔
4597
                const name = pattern.cdr.car.__name__;
5✔
4598
                log({
5✔
4599
                    pattern,
5✔
4600
                    code,
5✔
4601
                    name
5✔
4602
                });
5✔
4603
                if (symbols.includes(name)) {
5!
4604
                    return true;
×
4605
                }
×
4606
                bindings['...'].symbols[name] = null;
5✔
4607
                return true;
5✔
4608
            }
5✔
4609
            log('recur');
3,693✔
4610
            log({
3,693✔
4611
                pattern,
3,693✔
4612
                code
3,693✔
4613
            });
3,693✔
4614
            const car = traverse(pattern.car, code.car, state);
3,693✔
4615
            const cdr = traverse(pattern.cdr, code.cdr, state);
3,693✔
4616
            log({
3,693✔
4617
                $car_code: code.car,
3,693✔
4618
                $car_pattern: pattern.car,
3,693✔
4619
                car,
3,693✔
4620
                $cdr_code: code.cdr,
3,693✔
4621
                $cdr_pattern: pattern.cdr,
3,693✔
4622
                cdr
3,693✔
4623
            });
3,693✔
4624
            if (car && cdr) {
3,713✔
4625
                return true;
2,253✔
4626
            }
2,253✔
4627
        } else if (is_nil(pattern) && (is_nil(code) || code === undefined)) {
8,810!
4628
            // undefined is case when you don't have body ...
×
4629
            // and you do recursive call
×
4630
            return true;
×
4631
        } else if (is_pair(pattern.car) &&
95✔
4632
                   LSymbol.is(pattern.car.car, ellipsis_symbol)) {
95!
4633
            // pattern (...)
×
4634
            throw new Error('syntax: invalid usage of ellipsis');
×
4635
        } else {
95✔
4636
            return false;
95✔
4637
        }
95✔
4638
    }
8,810✔
4639
    /* eslint-enable complexity */
1,027✔
4640
    if (traverse(pattern, code)) {
1,027✔
4641
        return bindings;
498✔
4642
    }
498✔
4643
}
1,027✔
4644
// ----------------------------------------------------------------------
1✔
4645
// :: This function is called after syntax-rules macro is evaluated
1✔
4646
// :: and if there are any gensyms added by macro they need to restored
1✔
4647
// :: to original symbols
1✔
4648
// ----------------------------------------------------------------------
1✔
4649
function clear_gensyms(node, gensyms) {
488✔
4650
    function traverse(node) {
488✔
4651
        if (is_pair(node)) {
4,582✔
4652
            if (!gensyms.length) {
2,061✔
4653
                return node;
14✔
4654
            }
14✔
4655
            const car = traverse(node.car);
2,047✔
4656
            const cdr = traverse(node.cdr);
2,047✔
4657
            // TODO: check if it's safe to modify the list
2,047✔
4658
            //       some funky modify of code can happen in macro
2,047✔
4659
            return new Pair(car, cdr);
2,047✔
4660
        } else if (node instanceof LSymbol) {
4,582✔
4661
            var replacement = gensyms.find((gensym) => {
849✔
4662
                return gensym.gensym === node;
1,386✔
4663
            });
849✔
4664
            if (replacement) {
849✔
4665
                return LSymbol(replacement.name);
34✔
4666
            }
34✔
4667
            return node;
815✔
4668
        } else {
2,521✔
4669
            return node;
1,672✔
4670
        }
1,672✔
4671
    }
4,582✔
4672
    return traverse(node);
488✔
4673
}
488✔
4674
// ----------------------------------------------------------------------
1✔
4675
function transform_syntax(options = {}) {
498✔
4676
    const {
498✔
4677
        bindings,
498✔
4678
        expr,
498✔
4679
        scope,
498✔
4680
        symbols,
498✔
4681
        names,
498✔
4682
        ellipsis: ellipsis_symbol } = options;
498✔
4683
    const gensyms = {};
498✔
4684
    function valid_symbol(symbol) {
498✔
4685
        if (symbol instanceof LSymbol) {
2,317✔
4686
            return true;
2,317✔
4687
        }
2,317✔
4688
        return ['string', 'symbol'].includes(typeof symbol);
×
4689
    }
2,317✔
4690
    function transform(symbol) {
498✔
4691
        if (!valid_symbol(symbol)) {
2,317!
4692
            const t = type(symbol);
×
4693
            throw new Error(`syntax: internal error, need symbol got ${t}`);
×
4694
        }
×
4695
        const name = symbol.valueOf();
2,317✔
4696
        if (name === ellipsis_symbol) {
2,317✔
4697
            throw new Error('syntax: internal error, ellipis not transformed');
2✔
4698
        }
2✔
4699
        // symbols are gensyms from nested syntax-rules
2,315✔
4700
        var n_type = typeof name;
2,315✔
4701
        if (['string', 'symbol'].includes(n_type)) {
2,315✔
4702
            if (name in bindings.symbols) {
2,315✔
4703
                return bindings.symbols[name];
849✔
4704
            } else if (n_type === 'string' && name.match(/\./)) {
2,315✔
4705
                // calling method on pattern symbol #83
23✔
4706
                const parts = name.split('.');
23✔
4707
                const first = parts[0];
23✔
4708
                if (first in bindings.symbols) {
23✔
4709
                    return Pair.fromArray([
8✔
4710
                        LSymbol('.'),
8✔
4711
                        bindings.symbols[first]
8✔
4712
                    ].concat(parts.slice(1).map(x => LString(x))));
8✔
4713
                }
8✔
4714
            }
23✔
4715
        }
2,315✔
4716
        if (symbols.includes(name)) {
2,317✔
4717
            return symbol;
96✔
4718
        }
96✔
4719
        return rename(name, symbol);
1,362✔
4720
    }
2,317✔
4721
    function rename(name, symbol) {
498✔
4722
        if (!gensyms[name]) {
1,362✔
4723
            const ref = scope.ref(name);
1,069✔
4724
            // nested syntax-rules needs original symbol to get renamed again
1,069✔
4725
            if (typeof name === 'symbol' && !ref) {
1,069✔
4726
                name = symbol.literal();
8✔
4727
            }
8✔
4728
            if (gensyms[name]) {
1,069✔
4729
                return gensyms[name];
2✔
4730
            }
2✔
4731
            const gensym_name = gensym(name);
1,067✔
4732
            if (ref) {
1,069✔
4733
                const value = scope.get(name);
920✔
4734
                scope.set(gensym_name, value);
920✔
4735
            } else {
1,069✔
4736
                const value = scope.get(name, { throwError: false });
147✔
4737
                // value is not in scope, but it's JavaScript object
147✔
4738
                if (typeof value !== 'undefined') {
147✔
4739
                    scope.set(gensym_name, value);
7✔
4740
                }
7✔
4741
            }
147✔
4742
            // keep names so they can be restored after evaluation
1,067✔
4743
            // if there are free symbols as output
1,067✔
4744
            // kind of hack
1,067✔
4745
            names.push({
1,067✔
4746
                name, gensym: gensym_name
1,067✔
4747
            });
1,067✔
4748
            gensyms[name] = gensym_name;
1,067✔
4749
            // we need to check if name is a string, because it can be
1,067✔
4750
            // gensym from nested syntax-rules
1,067✔
4751
            if (typeof name === 'string' && name.match(/\./)) {
1,069✔
4752
                const [first, ...rest] = name.split('.').filter(Boolean);
12✔
4753
                // save JavaScript dot notation for Env::get
12✔
4754
                if (gensyms[first]) {
12✔
4755
                    hidden_prop(gensym_name, '__object__', [gensyms[first], ...rest]);
2✔
4756
                }
2✔
4757
            }
12✔
4758
        }
1,069✔
4759
        return gensyms[name];
1,360✔
4760
    }
1,362✔
4761
    function transform_ellipsis_expr(expr, bindings, state, next = () => {}) {
498✔
4762
        const { nested } = state;
1,453✔
4763
        log({ bindings, expr });
1,453✔
4764
        if (Array.isArray(expr) && !expr.length) {
1,453!
4765
            return expr;
×
4766
        }
×
4767
        if (expr instanceof LSymbol) {
1,453✔
4768
            let name = expr.valueOf();
272✔
4769
            if (is_gensym(expr) && !bindings[name]) {
272!
4770
               // name = expr.literal();
×
4771
            }
×
4772
            log('[t 1');
272✔
4773
            if (bindings[name]) {
272✔
4774
                if (is_pair(bindings[name])) {
187✔
4775
                    const { car, cdr } = bindings[name];
33✔
4776
                    if (nested) {
33✔
4777
                        const { car: caar, cdr: cadr } = car;
33✔
4778
                        if (!is_nil(cadr)) {
33✔
4779
                            next(name, new Pair(cadr, nil));
19✔
4780
                        }
19✔
4781
                        return caar;
33✔
4782
                    }
33✔
4783
                    if (!is_nil(cdr)) {
×
4784
                        next(name, cdr);
×
4785
                    }
×
4786
                    return car;
×
4787
                } else if (bindings[name] instanceof Array) {
187✔
4788
                    next(name, bindings[name].slice(1));
154✔
4789
                    return bindings[name][0];
154✔
4790
                }
154✔
4791
            }
187✔
4792
            return transform(expr);
85✔
4793
        }
85✔
4794
        const is_array = Array.isArray(expr);
1,181✔
4795
        if (is_pair(expr) || is_array) {
1,453✔
4796
            const first = is_array ? expr[0] : expr.car;
1,053✔
4797
            const second = is_array ? expr[1] : is_pair(expr.cdr) && expr.cdr.car;
1,053✔
4798
            if (first instanceof LSymbol &&
1,053✔
4799
                LSymbol.is(second, ellipsis_symbol)) {
1,053✔
4800
                let rest = is_array ? expr.slice(2) : expr.cdr.cdr;
748✔
4801
                log('[t 2');
748✔
4802
                const name = first.valueOf();
748✔
4803
                const item = bindings[name];
748✔
4804
                if (item === null) {
748!
4805
                    return;
×
4806
                } else if (name in bindings) {
748✔
4807
                    log({ name, binding: bindings[name] });
748✔
4808
                    if (is_pair(item)) {
748✔
4809
                        log('[t 2 Pair ' + nested);
698✔
4810
                        const { car, cdr } = item;
698✔
4811
                        const rest_expr = is_array ? expr.slice(2) : expr.cdr.cdr;
698!
4812
                        if (nested) {
698✔
4813
                            if (!is_nil(cdr)) {
59✔
4814
                                log('|| next 1');
34✔
4815
                                next(name, cdr);
34✔
4816
                            }
34✔
4817
                            if ((is_array && rest_expr.length) || (!is_nil(rest_expr) && !is_array)) {
59!
4818
                                const rest = transform_ellipsis_expr(rest_expr, bindings, state, next);
5✔
4819
                                if (is_array) {
5!
4820
                                    return car.concat(rest);
×
4821
                                } else if (is_pair(car)) {
5✔
4822
                                    return car.append(rest);
5✔
4823
                                } else {
5!
4824
                                    log('UNKNOWN');
×
4825
                                }
×
4826
                            }
5✔
4827
                            return car;
54✔
4828
                        } else if (is_pair(car)) {
698✔
4829
                            if (!is_nil(car.cdr)) {
636✔
4830
                                log('|| next 2');
398✔
4831
                                next(name, new Pair(car.cdr, cdr));
398✔
4832
                            }
398✔
4833
                            // wrap with Value to handle undefined
636✔
4834
                            return new Value(car.car);
636✔
4835
                        } else if (is_nil(cdr)) {
639✔
4836
                            return car;
2✔
4837
                        } else {
3✔
4838
                            const last_pair = expr.last_pair();
1✔
4839
                            if (last_pair.cdr instanceof LSymbol) {
1✔
4840
                                log('|| next 3');
1✔
4841
                                next(name, item.last_pair());
1✔
4842
                                return car;
1✔
4843
                            }
1✔
4844
                        }
1✔
4845
                    } else if (item instanceof Array) {
748✔
4846
                        log('[t 2 Array ' + nested);
47✔
4847
                        if (nested) {
47!
4848
                            next(name, item.slice(1));
×
4849
                            return Pair.fromArray(item);
×
4850
                        } else {
47✔
4851
                            const rest = item.slice(1);
47✔
4852
                            if (rest.length) {
47✔
4853
                                next(name, rest);
23✔
4854
                            }
23✔
4855
                            return item[0];
47✔
4856
                        }
47✔
4857
                    } else {
50✔
4858
                        return item;
3✔
4859
                    }
3✔
4860
                }
748✔
4861
            }
748✔
4862
            log('[t 3 recur ', expr);
305✔
4863
            const rest_expr = is_array ? expr.slice(1) : expr.cdr;
1,053!
4864
            const head = transform_ellipsis_expr(first, bindings, state, next);
1,053✔
4865
            const rest = transform_ellipsis_expr(rest_expr, bindings, state, next);
1,053✔
4866
            log( { head, rest });
1,053✔
4867
            if (is_array) {
1,053!
4868
                return [head].concat(rest);
×
4869
            }
×
4870
            return new Pair(
303✔
4871
                head,
303✔
4872
                rest
303✔
4873
            );
303✔
4874
        }
303✔
4875
        return expr;
128✔
4876
    }
1,453✔
4877
    function have_binding(binding, skip_nulls) {
498✔
4878
        const values = Object.values(binding);
1,239✔
4879
        const symbols = Object.getOwnPropertySymbols(binding);
1,239✔
4880
        if (symbols.length) {
1,239✔
4881
            values.push(...symbols.map(x => binding[x]));
52✔
4882
        }
52✔
4883
        return values.length && values.every(x => {
1,239✔
4884
            if (x === null) {
1,093✔
4885
                return !skip_nulls;
80✔
4886
            }
80✔
4887
            return is_pair(x) || is_nil(x) || (Array.isArray(x) && x.length);
1,093✔
4888
        });
1,239✔
4889
    }
1,239✔
4890
    function get_names(object) {
498✔
4891
        return Object.keys(object).concat(Object.getOwnPropertySymbols(object));
415✔
4892
    }
415✔
4893
    /* eslint-disable complexity */
498✔
4894
    function traverse(expr, { disabled } = {}) {
498✔
4895
        log('traverse>> ', expr);
7,485✔
4896
        const is_array = Array.isArray(expr);
7,485✔
4897
        if (is_array && expr.length === 0) {
7,485!
4898
            return expr;
×
4899
        }
×
4900
        if (is_pair(expr) || is_array) {
7,485✔
4901
            log('>> 0');
3,897✔
4902
            const first = is_array ? expr[0] : expr.car;
3,897✔
4903
            let second, rest_second;
3,897✔
4904
            if (is_array) {
3,897✔
4905
                second = expr[1];
1✔
4906
                rest_second = expr.slice(2);
1✔
4907
            } else if (is_pair(expr.cdr)) {
3,897✔
4908
                second = expr.cdr.car;
2,696✔
4909
                rest_second = expr.cdr.cdr;
2,696✔
4910
            }
2,696✔
4911
            log({ first, second, rest_second });
3,897✔
4912
            // escape ellispsis from R7RS e.g. (... ...)
3,897✔
4913
            if (!disabled && is_pair(first) &&
3,897✔
4914
                LSymbol.is(first.car, ellipsis_symbol)) {
3,897✔
4915
                return new Pair(
6✔
4916
                    first.cdr.car,
6✔
4917
                    traverse(expr.cdr)
6✔
4918
                );
6✔
4919
            }
6✔
4920
            if (second && LSymbol.is(second, ellipsis_symbol) && !disabled) {
3,897✔
4921
                log('>> 1');
470✔
4922
                const symbols = bindings['...'].symbols;
470✔
4923
                // skip expand list of pattern was (x y ... z)
470✔
4924
                // and code was (x z) so y == null
470✔
4925
                const values = Object.values(symbols);
470✔
4926
                if (values.length && values.every(x => x === null)) {
470✔
4927
                    log('>>> 1 (a)');
55✔
4928
                    return traverse(rest_second, { disabled });
55✔
4929
                }
55✔
4930
                var keys = get_names(symbols);
415✔
4931
                // case of list as first argument ((x . y) ...) or (x ... ...)
415✔
4932
                // we need to recursively process the list
415✔
4933
                // if we have pattern (_ (x y z ...) ...) and code (foo (1 2) (1 2))
415✔
4934
                // x an y will be arrays of [1 1] and [2 2] and z will be array
415✔
4935
                // of rest, x will also have it's own mapping to 1 and y to 2
415✔
4936
                // in case of usage outside of ellipsis list e.g.: (x y)
415✔
4937
                var is_spread = first instanceof LSymbol &&
470✔
4938
                    LSymbol.is(rest_second.car, ellipsis_symbol);
470✔
4939
                if (is_pair(first) || is_spread) {
470✔
4940
                    log('>>> 1 (b)');
89✔
4941
                    // lists is free ellipsis on pairs ((???) ...)
89✔
4942
                    // TODO: will this work in every case? Do we need to handle
89✔
4943
                    // nesting here?
89✔
4944
                    if (is_nil(bindings['...'].lists[0])) {
89✔
4945
                        if (!is_spread) {
14✔
4946
                            return traverse(rest_second, { disabled });
14✔
4947
                        }
14✔
4948
                        log(rest_second);
×
4949
                        return nil;
×
4950
                    }
×
4951
                    var new_expr = first;
75✔
4952
                    if (is_spread) {
89✔
4953
                        log('>>> 1 (c)'); // TODO: array
6✔
4954
                        new_expr = new Pair(
6✔
4955
                            first,
6✔
4956
                            new Pair(
6✔
4957
                                second,
6✔
4958
                                nil
6✔
4959
                            )
6✔
4960
                        );
6✔
4961
                    }
6✔
4962
                    log('>> 2');
75✔
4963
                    let result;
75✔
4964
                    if (keys.length) {
75✔
4965
                        log('>> 2 (a)');
75✔
4966
                        let bind = { ...symbols };
75✔
4967
                        result = is_array ? [] : nil;
75!
4968
                        while (true) {
75✔
4969
                            log({ bind });
227✔
4970
                            if (!have_binding(bind)) {
227✔
4971
                                break;
74✔
4972
                            }
74✔
4973
                            const new_bind = {};
153✔
4974
                            const next = (key, value) => {
153✔
4975
                                // ellipsis decide if what should be the next value
207✔
4976
                                // there are two cases ((a . b) ...) and (a ...)
207✔
4977
                                new_bind[key] = value;
207✔
4978
                            };
153✔
4979
                            let car = transform_ellipsis_expr(
153✔
4980
                                new_expr,
153✔
4981
                                bind,
153✔
4982
                                { nested: true },
153✔
4983
                                next
153✔
4984
                            );
153✔
4985
                            // undefined can be null caused by null binding
153✔
4986
                            // on empty ellipsis
153✔
4987
                            if (car !== undefined) {
227✔
4988
                                if (car instanceof Value) {
152!
4989
                                    car = car.valueOf();
×
4990
                                }
×
4991
                                if (is_spread) {
152✔
4992
                                    if (is_array) {
18!
4993
                                        if (Array.isArray(car)) {
×
4994
                                            result.push(...car);
×
4995
                                        } else {
×
4996
                                            log('ZONK {1}');
×
4997
                                        }
×
4998
                                    } else {
18✔
4999
                                        if (is_nil(result)) {
18✔
5000
                                            result = car;
6✔
5001
                                        } else {
18✔
5002
                                            result = result.append(car);
12✔
5003
                                        }
12✔
5004
                                    }
18✔
5005
                                } else if (is_array) {
152!
5006
                                    result.push(car);
×
5007
                                } else {
134✔
5008
                                    result = new Pair(
134✔
5009
                                        car,
134✔
5010
                                        result
134✔
5011
                                    );
134✔
5012
                                }
134✔
5013
                            }
152✔
5014
                            bind = new_bind;
152✔
5015
                        }
152✔
5016
                        if (!is_nil(result) && !is_spread && !is_array) {
75✔
5017
                            result = result.reverse();
68✔
5018
                        }
68✔
5019
                        // case of (list) ... (rest code)
74✔
5020
                        if (is_array) {
75!
5021
                            if (rest_second) {
×
5022
                                log({ rest_second, expr });
×
5023
                                const rest = traverse(rest_second, { disabled });
×
5024
                                return result.concat(rest);
×
5025
                            }
×
5026
                            return result;
×
5027
                        }
×
5028
                        if (!is_nil(expr.cdr.cdr) &&
75✔
5029
                            !LSymbol.is(expr.cdr.cdr.car, ellipsis_symbol)) {
75✔
5030
                            const rest = traverse(expr.cdr.cdr, { disabled });
2✔
5031
                            return result.append(rest);
2✔
5032
                        }
2✔
5033
                        return result;
72✔
5034
                    } else {
89!
5035
                        log('>> 3');
×
5036
                        let car = transform_ellipsis_expr(first, symbols, {
×
5037
                            nested: true
×
5038
                        });
×
5039
                        if (car) {
×
5040
                            if (car instanceof Value) {
×
5041
                                car = car.valueOf();
×
5042
                            }
×
5043
                            return new Pair(
×
5044
                                car,
×
5045
                                nil
×
5046
                            );
×
5047
                        }
×
5048
                        return nil;
×
5049
                    }
×
5050
                } else if (first instanceof LSymbol) {
470✔
5051
                    log('>> 4');
326✔
5052
                    if (LSymbol.is(rest_second.car, ellipsis_symbol)) {
326!
5053
                        // case (x ... ...)
×
5054
                        log('>> 4 (a)');
×
5055
                    } else {
326✔
5056
                        log('>> 4 (b)');
326✔
5057
                    }
326✔
5058
                    // case: (x ...)
326✔
5059
                    let name = first.__name__;
326✔
5060
                    let bind = { [name]: symbols[name] };
326✔
5061
                    log({bind});
326✔
5062
                    const is_null = symbols[name] === null;
326✔
5063
                    let result = is_array ? [] : nil;
326✔
5064
                    while (true) {
326✔
5065
                        if (!have_binding(bind, true)) {
1,012✔
5066
                            log({ bind });
326✔
5067
                            break;
326✔
5068
                        }
326✔
5069
                        const new_bind = {};
686✔
5070
                        const next = (key, value) => {
686✔
5071
                            new_bind[key] = value;
422✔
5072
                        };
686✔
5073
                        let value = transform_ellipsis_expr(
686✔
5074
                            expr,
686✔
5075
                            bind,
686✔
5076
                            { nested: false },
686✔
5077
                            next
686✔
5078
                        );
686✔
5079
                        log({ value });
686✔
5080
                        if (typeof value !== 'undefined') {
686✔
5081
                            if (value instanceof Value) {
686✔
5082
                                value = value.valueOf();
636✔
5083
                            }
636✔
5084
                            if (is_array) {
686✔
5085
                                result.push(value);
3✔
5086
                            } else {
686✔
5087
                                result = new Pair(
683✔
5088
                                    value,
683✔
5089
                                    result
683✔
5090
                                );
683✔
5091
                            }
683✔
5092
                        }
686✔
5093
                        bind = new_bind;
686✔
5094
                    }
686✔
5095
                    if (!is_nil(result) && !is_array) {
326✔
5096
                        result = result.reverse();
263✔
5097
                    }
263✔
5098
                    // case if (x ... y ...) second spread is not processed
326✔
5099
                    // and (??? . x) last symbol
326✔
5100
                    // by ellipsis transformation
326✔
5101
                    if (is_pair(expr.cdr)) {
326✔
5102
                        if (is_pair(expr.cdr.cdr) ||
325✔
5103
                            expr.cdr.cdr instanceof LSymbol) {
325✔
5104
                            const node = traverse(expr.cdr.cdr, { disabled });
67✔
5105
                            log({node});
67✔
5106
                            if (is_null) {
67✔
5107
                                return node;
12✔
5108
                            }
12✔
5109
                            if (is_nil(result)) {
67✔
5110
                                result = node;
3✔
5111
                            } else {
67✔
5112
                                result.append(node);
52✔
5113
                            }
52✔
5114
                            log({result, node});
55✔
5115
                        }
55✔
5116
                    }
325✔
5117
                    log('<<<< 2');
314✔
5118
                    log({ result });
314✔
5119
                    return result;
314✔
5120
                }
314✔
5121
            }
470✔
5122
            const head = traverse(first, { disabled });
3,421✔
5123
            let rest;
3,421✔
5124
            let is_syntax;
3,421✔
5125
            if ((first instanceof LSymbol)) {
3,897✔
5126
                const value = scope.get(first, { throwError: false });
2,113✔
5127
                is_syntax = value instanceof Macro &&
2,113✔
5128
                    value.__name__ === 'syntax-rules';
2,113✔
5129
            }
2,113✔
5130
            if (is_syntax) {
3,897✔
5131
                if (expr.cdr.car instanceof LSymbol) {
31✔
5132
                    rest = new Pair(
9✔
5133
                        traverse(expr.cdr.car, { disabled }),
9✔
5134
                        new Pair(
9✔
5135
                            expr.cdr.cdr.car,
9✔
5136
                            traverse(expr.cdr.cdr.cdr, { disabled })
9✔
5137
                        )
9✔
5138
                    );
9✔
5139
                } else {
31✔
5140
                    rest = new Pair(
22✔
5141
                        expr.cdr.car,
22✔
5142
                        traverse(expr.cdr.cdr, { disabled })
22✔
5143
                    );
22✔
5144
                }
22✔
5145
                log('REST >>>> ', rest);
30✔
5146
            } else {
3,897✔
5147
                rest = traverse(expr.cdr, { disabled });
3,382✔
5148
            }
3,382✔
5149
            log({
3,408✔
5150
                a: true,
3,408✔
5151
                car: to_string(expr.car),
3,408✔
5152
                cdr: to_string(expr.cdr),
3,408✔
5153
                head: to_string(head),
3,408✔
5154
                rest: to_string(rest)
3,408✔
5155
            });
3,408✔
5156
            return new Pair(
3,408✔
5157
                head,
3,408✔
5158
                rest
3,408✔
5159
            );
3,408✔
5160
        }
3,408✔
5161
        if (expr instanceof LSymbol) {
7,485✔
5162
            if (disabled && LSymbol.is(expr, ellipsis_symbol)) {
2,233!
5163
                return expr;
×
5164
            }
×
5165
            const symbols = Object.keys(bindings['...'].symbols);
2,233✔
5166
            const name = expr.literal(); // TODO: slow
2,233✔
5167
            if (symbols.includes(name)) {
2,233✔
5168
                const msg = `missing ellipsis symbol next to name \`${name}'`;
1✔
5169
                throw new Error(`syntax-rules: ${msg}`);
1✔
5170
            }
1✔
5171
            const value = transform(expr);
2,232✔
5172
            if (typeof value !== 'undefined') {
2,233✔
5173
                return value;
2,231✔
5174
            }
2,231✔
5175
        }
2,233✔
5176
        return expr;
1,355✔
5177
    }
7,485✔
5178
    return traverse(expr, {});
498✔
5179
}
498✔
5180
// ----------------------------------------------------------------------
1✔
5181
// :: Check for nullish values
1✔
5182
// ----------------------------------------------------------------------
1✔
5183
function is_null(value) {
11,623,776✔
5184
    return is_undef(value) || is_nil(value) || value === null;
11,623,776✔
5185
}
11,623,776✔
5186
// ----------------------------------------------------------------------
1✔
5187
function is_nil(value) {
24,944,054✔
5188
    return value === nil;
24,944,054✔
5189
}
24,944,054✔
5190
// ----------------------------------------------------------------------
1✔
5191
function is_function(o) {
326,968,167✔
5192
    return typeof o === 'function' && typeof o.bind === 'function';
326,968,167✔
5193
}
326,968,167✔
5194
// ----------------------------------------------------------------------------
1✔
5195
function is_directive(token) {
88,984✔
5196
    return directives.includes(token);
88,984✔
5197
}
88,984✔
5198
// ----------------------------------------------------------------------------
1✔
5199
function is_false(o) {
1,368,644✔
5200
    return o === false || o === null;
1,368,644✔
5201
}
1,368,644✔
5202
// ----------------------------------------------------------------------------
1✔
5203
function is_string(o) {
17,855,957✔
5204
    return typeof o === 'string';
17,855,957✔
5205
}
17,855,957✔
5206
// ----------------------------------------------------------------------
1✔
5207
function is_lambda(obj) {
5,814,243✔
5208
    return obj && obj[__lambda__];
5,814,243✔
5209
}
5,814,243✔
5210
// ----------------------------------------------------------------------
1✔
5211
function is_method(obj) {
1,257,053✔
5212
    return obj && obj[__method__];
1,257,053✔
5213
}
1,257,053✔
5214
// ----------------------------------------------------------------------
1✔
5215
function is_raw_lambda(fn) {
5,534,337✔
5216
    return is_lambda(fn) && !fn[__prototype__] &&
5,534,337✔
5217
        !is_method(fn) && !is_port_method(fn);
5,534,337✔
5218
}
5,534,337✔
5219
// ----------------------------------------------------------------------
1✔
5220
function is_native_function(fn) {
364,796✔
5221
    var native = Symbol.for('__native__');
364,796✔
5222
    return is_function(fn) &&
364,796✔
5223
        fn.toString().match(/\{\s*\[native code\]\s*\}/) &&
364,796✔
5224
        ((fn.name.match(/^bound /) && fn[native] === true) ||
47,000!
5225
         (!fn.name.match(/^bound /) && !fn[native]));
364,796✔
5226
}
364,796✔
5227
// ----------------------------------------------------------------------------
1✔
5228
function is_prototype(obj) {
644,266✔
5229
    return obj &&
644,266✔
5230
        typeof obj === 'object' &&
644,266✔
5231
        obj.hasOwnProperty &&
644,266✔
5232
        obj.hasOwnProperty("constructor") &&
644,266✔
5233
        typeof obj.constructor === "function" &&
644,266✔
5234
        obj.constructor.prototype === obj;
644,266✔
5235
}
644,266✔
5236
// ----------------------------------------------------------------------
1✔
5237
function is_continuation(o) {
27✔
5238
    return o instanceof Continuation;
27✔
5239
}
27✔
5240
// ----------------------------------------------------------------------
1✔
5241
function is_context(o) {
1,379,875✔
5242
    return o instanceof LambdaContext;
1,379,875✔
5243
}
1,379,875✔
5244
// ----------------------------------------------------------------------
1✔
5245
function is_parameter(o) {
152✔
5246
    return o instanceof Parameter;
152✔
5247
}
152✔
5248
// ----------------------------------------------------------------------
1✔
5249
function is_pair(o) {
570,693,233✔
5250
    return o instanceof Pair;
570,693,233✔
5251
}
570,693,233✔
5252
// ----------------------------------------------------------------------
1✔
5253
function is_env(o) {
11,547,124✔
5254
    return o instanceof Environment;
11,547,124✔
5255
}
11,547,124✔
5256
// ----------------------------------------------------------------------
1✔
5257
function is_callable(o) {
1,057✔
5258
    return is_function(o) || is_continuation(o) || is_parameter(o) || is_macro(o);s
1,057!
5259
}
1,057✔
5260
// ----------------------------------------------------------------------
1✔
5261
function is_macro(o) {
2✔
5262
    return o instanceof Macro || o instanceof SyntaxParameter;
2✔
5263
}
2✔
5264
// ----------------------------------------------------------------------
1✔
5265
function is_promise(o) {
266,645,466✔
5266
    if (o instanceof QuotedPromise) {
266,645,466✔
5267
        return false;
316✔
5268
    }
316✔
5269
    if (o instanceof Promise) {
266,645,466✔
5270
        return true;
28,329✔
5271
    }
28,329✔
5272
    return !!o && is_function(o.then);
266,645,466✔
5273
}
266,645,466✔
5274
// ----------------------------------------------------------------------
1✔
5275
function is_undef(value) {
11,623,854✔
5276
    return typeof value === 'undefined';
11,623,854✔
5277
}
11,623,854✔
5278
// -------------------------------------------------------------------------
1✔
5279
function get_proto(obj) {
40✔
5280
    return Object.getPrototypeOf(obj);
40✔
5281
}
40✔
5282
// -------------------------------------------------------------------------
1✔
5283
function is_iterator(obj, symbol) {
80✔
5284
    if (has_own_symbol(obj, symbol) || has_own_symbol(get_proto(obj), symbol)) {
80✔
5285
        return is_function(obj[symbol]);
40✔
5286
    }
40✔
5287
}
80✔
5288
// -------------------------------------------------------------------------
1✔
5289
function is_instance(obj) {
2,906✔
5290
    if (!obj) {
2,906✔
5291
        return false;
116✔
5292
    }
116✔
5293
    if (typeof obj !== 'object') {
2,906✔
5294
        return false;
355✔
5295
    }
355✔
5296
    // __instance__ is read only for instances
2,435✔
5297
    if (obj.__instance__) {
2,906✔
5298
        obj.__instance__ = false;
9✔
5299
        return obj.__instance__;
9✔
5300
    }
9✔
5301
    return false;
2,426✔
5302
}
2,906✔
5303
// -------------------------------------------------------------------------
1✔
5304
function self_evaluated(obj) {
1,657,163✔
5305
    var type = typeof obj;
1,657,163✔
5306
    return ['string', 'function'].includes(type) ||
1,657,163✔
5307
        typeof obj === 'symbol' ||
1,657,163✔
5308
        obj instanceof QuotedPromise ||
1,657,163✔
5309
        obj instanceof LSymbol ||
1,657,163✔
5310
        obj instanceof LNumber ||
1,657,163✔
5311
        obj instanceof LString ||
1,657,163✔
5312
        obj instanceof RegExp;
1,657,163✔
5313
}
1,657,163✔
5314
// -------------------------------------------------------------------------
1✔
5315
function is_native(obj) {
355✔
5316
    return obj && (obj instanceof LNumber ||
355✔
5317
                   obj instanceof LString ||
327✔
5318
                   obj instanceof LCharacter);
355✔
5319
}
355✔
5320
// -------------------------------------------------------------------------
1✔
5321
function has_own_symbol(obj, symbol) {
120✔
5322
    if (obj === null) {
120!
5323
        return false;
×
5324
    }
×
5325
    return typeof obj === 'object' &&
120✔
5326
        symbol in Object.getOwnPropertySymbols(obj);
120✔
5327
}
120✔
5328
// ----------------------------------------------------------------------
1✔
5329
// :: Function utilities
1✔
5330
// ----------------------------------------------------------------------
1✔
5331
function box(object) {
15,863,026✔
5332
    // We only need to box lips data and arrays. Object don't need
15,863,026✔
5333
    // to be boxed, but values from objects will be boxed when accessed.
15,863,026✔
5334
    switch (typeof object) {
15,863,026✔
5335
        case 'string':
15,863,026✔
5336
            return LString(object);
159,447✔
5337
        case 'bigint':
15,863,026!
5338
            return LNumber(object);
×
5339
        case 'number':
15,863,026✔
5340
            if (Number.isNaN(object)) {
87,118!
5341
                return nan;
×
5342
            } else {
87,118✔
5343
                return LNumber(object);
87,118✔
5344
            }
87,118✔
5345
    }
15,863,026✔
5346
    return object;
15,616,461✔
5347
}
15,863,026✔
5348
// ----------------------------------------------------------------------
1✔
5349
function map_object(object, fn) {
107✔
5350
    const props = Object.getOwnPropertyNames(object);
107✔
5351
    const symbols = Object.getOwnPropertySymbols(object);
107✔
5352
    const result = {};
107✔
5353
    props.concat(symbols).forEach(key => {
107✔
5354
        const value = fn(object[key]);
413✔
5355
        result[key] = value;
413✔
5356
    });
107✔
5357
    return result;
107✔
5358
}
107✔
5359
// ----------------------------------------------------------------------
1✔
5360
function unbox(object) {
382,510✔
5361
    var lips_type = [LString, LNumber, LCharacter].some(x => object instanceof x);
382,510✔
5362
    if (lips_type) {
382,510✔
5363
        return object.valueOf();
108,243✔
5364
    }
108,243✔
5365
    if (object instanceof Array) {
382,510✔
5366
        return object.map(unbox);
109✔
5367
    }
109✔
5368
    if (object instanceof QuotedPromise) {
382,510✔
5369
        delete object.then;
4✔
5370
    }
4✔
5371
    if (is_plain_object(object)) {
382,510✔
5372
        return map_object(object, unbox);
107✔
5373
    }
107✔
5374
    return object;
274,051✔
5375
}
382,510✔
5376
// ----------------------------------------------------------------------
1✔
5377
function patch_value(value, context) {
14,769,442✔
5378
    if (is_pair(value)) {
14,769,442✔
5379
        value.mark_cycles();
1,193,536✔
5380
        return quote(value);
1,193,536✔
5381
    }
1,193,536✔
5382
    if (is_function(value)) {
14,769,442✔
5383
        // original function can be restored using unbind function
5,598,995✔
5384
        // only real JS function require to be bound
5,598,995✔
5385
        if (context) {
5,598,995✔
5386
            return bind(value, context);
126,239✔
5387
        }
126,239✔
5388
    }
5,598,995✔
5389
    return box(value);
13,449,667✔
5390
}
14,769,442✔
5391
// ----------------------------------------------------------------------
1✔
5392
// :: Function gets original function that was binded with props
1✔
5393
// ----------------------------------------------------------------------
1✔
5394
function unbind(obj) {
658,368✔
5395
    if (is_bound(obj)) {
658,368✔
5396
        return obj[__fn__];
23,312✔
5397
    }
23,312✔
5398
    return obj;
635,056✔
5399
}
658,368✔
5400
// ----------------------------------------------------------------------
1✔
5401
// :: Function binds with context that can be optionally unbind
1✔
5402
// :: get original function with unbind
1✔
5403
// ----------------------------------------------------------------------
1✔
5404
function bind(fn, context) {
126,239✔
5405
    if (fn[Symbol.for('__bound__')]) {
126,239!
5406
        return fn;
×
5407
    }
×
5408
    const bound = fn.bind(context);
126,239✔
5409
    const props = Object.getOwnPropertyNames(fn);
126,239✔
5410
    for (var prop of props) {
126,239✔
5411
        if (filter_fn_names(prop)) {
371,085✔
5412
            try {
41,687✔
5413
                bound[prop] = fn[prop];
41,687✔
5414
            } catch (e) {
41,687!
5415
                // ignore error from express.js while accessing bodyParser
×
5416
            }
×
5417
        }
41,687✔
5418
    }
371,085✔
5419
    hidden_prop(bound, '__fn__', fn);
126,239✔
5420
    hidden_prop(bound, '__context__', context);
126,239✔
5421
    hidden_prop(bound, '__bound__', true);
126,239✔
5422
    if (is_native_function(fn)) {
126,239✔
5423
        hidden_prop(bound, '__native__', true);
47,000✔
5424
    }
47,000✔
5425
    if (is_plain_object(context) && is_lambda(fn)) {
126,239✔
5426
        hidden_prop(bound, '__method__', true);
17✔
5427
    }
17✔
5428
    bound.valueOf = function() {
126,239✔
5429
        return fn;
×
5430
    };
126,239✔
5431
    return bound;
126,239✔
5432
}
126,239✔
5433
// ----------------------------------------------------------------------
1✔
5434
// Function used to check if function should not get unboxed arguments,
1✔
5435
// so you can call Object.getPrototypeOf for lips data types
1✔
5436
// this is case, see dir function and #73
1✔
5437
// ----------------------------------------------------------------------
1✔
5438
function is_object_bound(obj) {
123,523✔
5439
    return is_bound(obj) && obj[Symbol.for('__context__')] === Object;
123,523✔
5440
}
123,523✔
5441
// ----------------------------------------------------------------------
1✔
5442
function is_bound(obj) {
3,551,111✔
5443
    return !!(is_function(obj) && obj[__fn__]);
3,551,111✔
5444
}
3,551,111✔
5445
// ----------------------------------------------------------------------
1✔
5446
function lips_context(obj) {
123,241✔
5447
    if (is_function(obj)) {
123,241✔
5448
        var context = obj[__context__];
123,241✔
5449
        if (context && (context === lips ||
123,241✔
5450
                        (context.constructor &&
122,991✔
5451
                         context.constructor.__class__))) {
123,241✔
5452
            return true;
71,562✔
5453
        }
71,562✔
5454
    }
123,241✔
5455
    return false;
51,679✔
5456
}
123,241✔
5457
// ----------------------------------------------------------------------
1✔
5458
function is_port(obj) {
1,328,615✔
5459
    return obj instanceof InputPort || obj instanceof OutputPort;
1,328,615✔
5460
}
1,328,615✔
5461
// ----------------------------------------------------------------------
1✔
5462
function is_port_method(obj) {
1,328,615✔
5463
    if (is_function(obj)) {
1,328,615✔
5464
        if (is_port(obj[__context__])) {
1,328,615✔
5465
            return true;
52✔
5466
        }
52✔
5467
    }
1,328,615✔
5468
    return false;
1,328,563✔
5469
}
1,328,615✔
5470
// ----------------------------------------------------------------------
1✔
5471
// Hidden props
1✔
5472
// ----------------------------------------------------------------------
1✔
5473
const __context__ = Symbol.for('__context__');
1✔
5474
const __fn__ = Symbol.for('__fn__');
1✔
5475
const __data__ = Symbol.for('__data__');
1✔
5476
const __ref__ = Symbol.for('__ref__');
1✔
5477
const __cycles__ = Symbol.for('__cycles__');
1✔
5478
const __class__ = Symbol.for('__class__');
1✔
5479
const __method__ = Symbol.for('__method__');
1✔
5480
const __prototype__ = Symbol.for('__prototype__');
1✔
5481
const __lambda__ = Symbol.for('__lambda__');
1✔
5482
// ----------------------------------------------------------------------
1✔
5483
// :: Function bind fn with context but it also move all props
1✔
5484
// :: mostly used for Object function
1✔
5485
// ----------------------------------------------------------------------
1✔
5486
var exluded_names = ['name', 'length', 'caller', 'callee', 'arguments', 'prototype'];
1✔
5487
function filter_fn_names(name) {
371,085✔
5488
    return !exluded_names.includes(name);
371,085✔
5489
}
371,085✔
5490
// ----------------------------------------------------------------------
1✔
5491
function enumerable(object, name, value) {
691,476✔
5492
    Object.defineProperty(object, name, {
691,476✔
5493
        value,
691,476✔
5494
        enumerable: true
691,476✔
5495
    });
691,476✔
5496
}
691,476✔
5497
// ----------------------------------------------------------------------
1✔
5498
function hidden_prop(obj, name, value) {
1,722,898✔
5499
    Object.defineProperty(obj, Symbol.for(name), {
1,722,898✔
5500
        get: () => value,
1,722,898✔
5501
        set: () => {},
1,722,898✔
5502
        configurable: false,
1,722,898✔
5503
        enumerable: false
1,722,898✔
5504
    });
1,722,898✔
5505
}
1,722,898✔
5506
// ----------------------------------------------------------------------
1✔
5507
function set_fn_length(fn, length) {
72,571✔
5508
    try {
72,571✔
5509
        Object.defineProperty(fn, 'length', {
72,571✔
5510
            get: function() {
72,571✔
5511
                return length;
15✔
5512
            }
15✔
5513
        });
72,571✔
5514
        return fn;
72,571✔
5515
    } catch (e) {
72,571!
5516
        // hack that create function with specific length should work for browsers
×
5517
        // that don't support Object.defineProperty like old IE
×
5518
        var args = new Array(length).fill(0).map((_, i) => 'a' + i).join(',');
×
5519
        var wrapper = new Function(`f`, `return function(${args}) {
×
5520
                return f.apply(this, arguments);
×
5521
            };`);
×
5522
        return wrapper(fn);
×
5523
    }
×
5524
}
72,571✔
5525
// ----------------------------------------------------------------------
1✔
5526
// :: function that return macro for let, let* and letrec
1✔
5527
// ----------------------------------------------------------------------
1✔
5528
function let_macro(symbol) {
4✔
5529
    var name;
4✔
5530
    switch (symbol) {
4✔
5531
        case Symbol.for('letrec'):
4✔
5532
            name = 'letrec';
2✔
5533
            break;
2✔
5534
        case Symbol.for('let'):
4✔
5535
            name = 'let';
1✔
5536
            break;
1✔
5537
        case Symbol.for('let*'):
4✔
5538
            name = 'let*';
1✔
5539
            break;
1✔
5540
        default:
4!
5541
            throw new Error('Invalid let_macro value');
×
5542
    }
4✔
5543
    return Macro.defmacro(name, function(code, options) {
4✔
5544
        let { dynamic_env } = options;
459,600✔
5545
        const { error, macro_expand, use_dynamic } = options
459,600✔
5546
        var args;
459,600✔
5547
        // named let:
459,600✔
5548
        // (let loop ((x 10)) (iter (- x 1))) -> (letrec ((loop (lambda (x) ...
459,600✔
5549
        if (code.car instanceof LSymbol) {
459,600✔
5550
            if (!(is_pair(code.cdr.car) || is_nil(code.cdr.car))) {
870!
5551
                throw new Error('let require list of pairs');
×
5552
            }
×
5553
            var params;
870✔
5554
            if (is_nil(code.cdr.car)) {
870!
5555
                args = nil;
×
5556
                params = nil;
×
5557
            } else {
870✔
5558
                params = code.cdr.car.map(pair => pair.car);
870✔
5559
                args = code.cdr.car.map(pair => pair.cdr.car);
870✔
5560
            }
870✔
5561
            return new Pair(
870✔
5562
                Pair.fromArray([
870✔
5563
                    LSymbol('letrec'),
870✔
5564
                    [[code.car, Pair(
870✔
5565
                        LSymbol('lambda'),
870✔
5566
                        Pair(params, code.cdr.cdr))]],
870✔
5567
                    code.car]),
870✔
5568
                args);
870✔
5569
        } else if (macro_expand) {
459,600✔
5570
            // Macro.defmacro are special macros that should return lips code
1✔
5571
            // here we use evaluate, so we need to check special flag set by
1✔
5572
            // macroexpand to prevent evaluation of code in normal let
1✔
5573
            return;
1✔
5574
        }
1✔
5575
        var self = this;
458,729✔
5576
        args = global_env.get('list->array')(code.car);
458,729✔
5577
        var env = self.inherit(name);
458,729✔
5578
        var values, var_body_env;
458,729✔
5579
        if (name === 'let*') {
459,600✔
5580
            var_body_env = env;
165,539✔
5581
        } else if (name === 'let') {
459,600✔
5582
            values = []; // collect potential promises
292,292✔
5583
        }
292,292✔
5584
        var i = 0;
458,729✔
5585
        function exec() {
458,729✔
5586
            var output = hygienic_begin([env], code.cdr);
458,715✔
5587
            return evaluate(output, {
458,715✔
5588
                env,
458,715✔
5589
                dynamic_env: env,
458,715✔
5590
                use_dynamic,
458,715✔
5591
                error
458,715✔
5592
            });
458,715✔
5593
        }
458,715✔
5594
        function check_duplicates(name) {
458,729✔
5595
            if (name in env.__env__) {
426,367!
5596
                throw new Error(`Duplicated let variable ${name}`);
×
5597
            }
×
5598
        }
426,367✔
5599
        return (function loop() {
458,729✔
5600
            var pair = args[i++];
1,462,812✔
5601
            dynamic_env = name === 'let*' ? env : self;
1,462,812✔
5602
            if (!pair) {
1,462,812✔
5603
                if (values && values.length) {
458,716✔
5604
                    var v = values.map(x => x.value);
292,250✔
5605
                    // resolve all promises
292,250✔
5606
                    var promises = v.filter(is_promise);
292,250✔
5607
                    if (promises.length) {
292,250✔
5608
                        return promise_all(v).then((arr) => {
1,894✔
5609
                            for (var i = 0, len = arr.length; i < len; ++i) {
1,893✔
5610
                                const name = values[i].name;
2,106✔
5611
                                check_duplicates(name);
2,106✔
5612
                                env.set(name, arr[i]);
2,106✔
5613
                            }
2,106✔
5614
                        }).then(exec);
1,894✔
5615
                    } else {
292,250✔
5616
                        for (let { name, value } of values) {
290,356✔
5617
                            check_duplicates(name);
424,261✔
5618
                            env.set(name, value);
424,261✔
5619
                        }
424,261✔
5620
                    }
290,356✔
5621
                }
292,250✔
5622
                return exec();
456,822✔
5623
            } else {
1,462,812✔
5624
                if (name === 'let') {
1,004,096✔
5625
                    var_body_env = self;
426,383✔
5626
                } else if (name === 'letrec') {
1,004,096✔
5627
                    var_body_env = env;
1,033✔
5628
                }
1,033✔
5629
                var value = evaluate(pair.cdr.car, {
1,004,096✔
5630
                    env: var_body_env,
1,004,096✔
5631
                    dynamic_env,
1,004,096✔
5632
                    use_dynamic,
1,004,096✔
5633
                    error
1,004,096✔
5634
                });
1,004,096✔
5635
                if (name === 'let*') {
1,004,096✔
5636
                    var_body_env = env = var_body_env.inherit('let*[' + i + ']');
576,679✔
5637
                }
576,679✔
5638
                if (values) {
1,004,096✔
5639
                    values.push({ name: pair.car, value });
426,371✔
5640
                    return loop();
426,371✔
5641
                } else {
1,004,096✔
5642
                    return unpromise(value, function(value) {
577,712✔
5643
                        env.set(pair.car, value);
577,712✔
5644
                        return loop();
577,712✔
5645
                    });
577,712✔
5646
                }
577,712✔
5647
            }
1,004,096✔
5648
        })();
458,729✔
5649
    });
4✔
5650
}
4✔
5651
// -------------------------------------------------------------------------
1✔
5652
function parallel(name, fn) {
1✔
5653
    return new Macro(name, function(code, { use_dynamic, error } = {}) {
1✔
5654
        const env = this;
×
5655
        const dynamic_env = this;
×
5656
        const results = [];
×
5657
        let node = code;
×
5658
        while (is_pair(node)) {
×
5659
            results.push(evaluate(node.car, { env, dynamic_env, use_dynamic, error }));
×
5660
            node = node.cdr;
×
5661
        }
×
5662
        var havePromises = results.filter(is_promise).length;
×
5663
        if (havePromises) {
×
5664
            return promise_all(results).then(fn.bind(this));
×
5665
        } else {
×
5666
            return fn.call(this, results);
×
5667
        }
×
5668
    });
1✔
5669
}
1✔
5670
// -------------------------------------------------------------------------
1✔
5671
function guard_math_call(fn, ...args) {
165,746✔
5672
    args.forEach(arg => {
165,746✔
5673
        typecheck('', arg, 'number');
401,639✔
5674
    });
165,746✔
5675
    return fn(...args);
165,746✔
5676
}
165,746✔
5677
// ----------------------------------------------------------------------
1✔
5678
function pipe(...fns) {
1✔
5679
    fns.forEach((fn, i) => {
1✔
5680
        typecheck('pipe', fn, 'function', i + 1);
2✔
5681
    });
1✔
5682
    return (...args) => {
1✔
5683
        return fns.reduce((args, f) => [f.apply(this, args)], args)[0];
40✔
5684
    };
1✔
5685
}
1✔
5686
// -------------------------------------------------------------------------
1✔
5687
function compose(...fns) {
1✔
5688
    fns.forEach((fn, i) => {
1✔
5689
        typecheck('compose', fn, 'function', i + 1);
2✔
5690
    });
1✔
5691
    return pipe(...fns.reverse());
1✔
5692
}
1✔
5693
// -------------------------------------------------------------------------
1✔
5694
// :: fold functions generator
1✔
5695
// -------------------------------------------------------------------------
1✔
5696
function fold(name, fold) {
2✔
5697
    var self = this;
2✔
5698
    return function recur(fn, init, ...lists) {
2✔
5699
        typecheck(name, fn, 'function');
14✔
5700
        if (lists.some(is_null)) {
14✔
5701
            if (typeof init === 'number') {
2!
5702
                return LNumber(init);
×
5703
            }
×
5704
            return init;
2✔
5705
        } else {
14✔
5706
            return fold.call(self, recur, fn, init, ...lists);
12✔
5707
        }
12✔
5708
    };
2✔
5709
}
2✔
5710
// -------------------------------------------------------------------------
1✔
5711
function limit_math_op(n, fn) {
64,452✔
5712
    // + 1 so it include function in guard_math_call
64,452✔
5713
    return limit(n + 1, curry(guard_math_call, fn));
64,452✔
5714
}
64,452✔
5715
// -------------------------------------------------------------------------
1✔
5716
// :: some functional magic
1✔
5717
// -------------------------------------------------------------------------
1✔
5718
var single_math_op = curry(limit_math_op, 1);
1✔
5719
var binary_math_op = curry(limit_math_op, 2);
1✔
5720
// -------------------------------------------------------------------------
1✔
5721
function reduce_math_op(fn, init = null) {
2✔
5722
    return function(...args) {
2✔
5723
        if (init !== null) {
45,474✔
5724
            args = [init, ...args];
45,474✔
5725
        }
45,474✔
5726
        return args.reduce(binary_math_op(fn));
45,474✔
5727
    };
2✔
5728
}
2✔
5729
// -------------------------------------------------------------------------
1✔
5730
function curry(fn, ...init_args) {
64,476✔
5731
    typecheck('curry', fn, 'function');
64,476✔
5732
    var len = fn.length;
64,476✔
5733
    return function() {
64,476✔
5734
        var args = init_args.slice();
231,228✔
5735
        // HACK: we use IIFE here to get rid of the name of the function.
231,228✔
5736
        // The JavaScript is smart and add name property to a function
231,228✔
5737
        // if it's assigned to a variable, with IIFE we can get rid of it.
231,228✔
5738
        // we need this so the curried function display as #<procedure>
231,228✔
5739
        const curried = (() => {
231,228✔
5740
            return (...more_args) => {
231,228✔
5741
                args = args.concat(more_args);
231,228✔
5742
                if (args.length >= len) {
231,228✔
5743
                    return fn.apply(this, args);
231,228✔
5744
                } else {
231,228!
5745
                    return curried;
×
5746
                }
×
5747
            };
231,228✔
5748
        })()
231,228✔
5749
        return curried(...arguments);
231,228✔
5750
    };
64,476✔
5751
}
64,476✔
5752
// -------------------------------------------------------------------------
1✔
5753
// return function with limited number of arguments
1✔
5754
function limit(n, fn) {
64,452✔
5755
    typecheck('limit', fn, 'function', 2);
64,452✔
5756
    return function(...args) {
64,452✔
5757
        return fn(...args.slice(0, n));
165,746✔
5758
    };
64,452✔
5759
}
64,452✔
5760
// -------------------------------------------------------------------------
1✔
5761
// :: Character object representation
1✔
5762
// -------------------------------------------------------------------------
1✔
5763
function LCharacter(char) {
1,218✔
5764
    if (typeof this !== 'undefined' && !(this instanceof LCharacter) ||
1,218✔
5765
        typeof this === 'undefined') {
1,218✔
5766
        return new LCharacter(char);
609✔
5767
    }
609✔
5768
    if (char instanceof LString) {
1,218✔
5769
        char = char.valueOf();
118✔
5770
    }
118✔
5771
    var name;
609✔
5772
    if (Array.from(char).length > 1) {
1,218✔
5773
        // this is name
7✔
5774
        char = char.toLowerCase();
7✔
5775
        if (LCharacter.__names__[char]) {
7✔
5776
            name = char;
7✔
5777
            char = LCharacter.__names__[char];
7✔
5778
        } else {
7!
5779
            // this should never happen
×
5780
            // parser don't allow not defined named characters
×
5781
            throw new Error('Internal: Unknown named character');
×
5782
        }
×
5783
    } else {
1,218✔
5784
        name = LCharacter.__rev_names__[char];
602✔
5785
    }
602✔
5786
    enumerable(this, '__char__', char);
609✔
5787
    if (name) {
1,218✔
5788
        enumerable(this, '__name__', name);
125✔
5789
    }
125✔
5790
}
1,218✔
5791
LCharacter.is = function(character, value) {
1✔
5792
    return character instanceof LCharacter &&
3✔
5793
        character.__char__ === value;
3✔
5794
};
1✔
5795
LCharacter.__names__ = characters;
1✔
5796
LCharacter.__rev_names__ = {};
1✔
5797
Object.keys(LCharacter.__names__).forEach(key => {
1✔
5798
    var value = LCharacter.__names__[key];
41✔
5799
    LCharacter.__rev_names__[value] = key;
41✔
5800
});
1✔
5801
LCharacter.prototype.toUpperCase = function() {
1✔
5802
    return LCharacter(this.__char__.toUpperCase());
7✔
5803
};
1✔
5804
LCharacter.prototype.toLowerCase = function() {
1✔
5805
    return LCharacter(this.__char__.toLowerCase());
42✔
5806
};
1✔
5807
LCharacter.prototype.toString = function() {
1✔
5808
    return '#\\' + (this.__name__ || this.__char__);
25✔
5809
};
1✔
5810
LCharacter.prototype.valueOf = LCharacter.prototype.serialize = function() {
1✔
5811
    return this.__char__;
499✔
5812
};
1✔
5813
// -------------------------------------------------------------------------
1✔
5814
// :: String wrapper that handle copy and in place change
1✔
5815
// -------------------------------------------------------------------------
1✔
5816
function LString(string) {
414,614✔
5817
    if (typeof this !== 'undefined' && !(this instanceof LString) ||
414,614✔
5818
        typeof this === 'undefined') {
414,614✔
5819
        return new LString(string);
207,307✔
5820
    }
207,307✔
5821
    if (string instanceof Array) {
414,614!
5822
        this.__string__ = string.map((x, i) => {
×
5823
            typecheck('LString', x, 'character', i + 1);
×
5824
            return x.toString();
×
5825
        }).join('');
×
5826
    } else {
414,614✔
5827
        this.__string__ = string.valueOf();
207,307✔
5828
    }
207,307✔
5829
}
414,614✔
5830
{
1✔
5831
    const ignore = ['length', 'constructor'];
1✔
5832
    const _keys = Object.getOwnPropertyNames(String.prototype).filter(name => {
1✔
5833
        return !ignore.includes(name);
52✔
5834
    });
1✔
5835
    const wrap = (fn) => function(...args) {
1✔
5836
        return fn.apply(this.__string__, args);
727,547✔
5837
    };
1✔
5838
    for (let key of _keys) {
1✔
5839
        LString.prototype[key] = wrap(String.prototype[key]);
50✔
5840
    }
50✔
5841
}
1✔
5842
LString.prototype[Symbol.iterator] = function*() {
1✔
5843
    const chars = Array.from(this.__string__);
1✔
5844
    for (const char of chars) {
1✔
5845
        yield LCharacter(char);
5✔
5846
    }
5✔
5847
};
1✔
5848
LString.prototype.serialize = function() {
1✔
5849
    return this.valueOf();
1✔
5850
};
1✔
5851
LString.is = function(string, value) {
1✔
5852
    return string instanceof LString &&
3✔
5853
        string.__string__ === value;
3✔
5854
};
1✔
5855
LString.isString = function(x) {
1✔
5856
    return x instanceof LString || typeof x === 'string';
198,456✔
5857
};
1✔
5858
LString.prototype.freeze = function() {
1✔
5859
    const string = this.__string__;
47,396✔
5860
    delete this.__string__;
47,396✔
5861
    read_only(this, '__string__', string);
47,396✔
5862
};
1✔
5863
LString.prototype.get = function(n) {
1✔
5864
    typecheck('LString::get', n, 'number');
39✔
5865
    return Array.from(this.__string__)[n.valueOf()];
39✔
5866
};
1✔
5867
LString.prototype.cmp = function(string) {
1✔
5868
    typecheck('LString::cmp', string, 'string');
22,348✔
5869
    var a = this.valueOf();
22,348✔
5870
    var b = string.valueOf();
22,348✔
5871
    if (a < b) {
22,348✔
5872
        return -1;
6,828✔
5873
    } else if (a === b) {
22,348✔
5874
        return 0;
8,232✔
5875
    } else {
15,520✔
5876
        return 1;
7,288✔
5877
    }
7,288✔
5878
};
1✔
5879
LString.prototype.lower = function() {
1✔
5880
    return LString(this.__string__.toLowerCase());
28✔
5881
};
1✔
5882
LString.prototype.upper = function() {
1✔
5883
    return LString(this.__string__.toUpperCase());
×
5884
};
1✔
5885
LString.prototype.set = function(n, char) {
1✔
5886
    typecheck('LString::set', n, 'number');
1✔
5887
    typecheck('LString::set', char, ['string', 'character']);
1✔
5888
    n = n.valueOf();
1✔
5889
    if (char instanceof LCharacter) {
1✔
5890
        char = char.__char__;
1✔
5891
    }
1✔
5892
    var string = [];
1✔
5893
    if (n > 0) {
1!
5894
        string.push(this.__string__.substring(0, n));
×
5895
    }
×
5896
    string.push(char);
1✔
5897
    if (n < this.__string__.length - 1) {
1✔
5898
        string.push(this.__string__.substring(n + 1));
1✔
5899
    }
1✔
5900
    this.__string__ = string.join('');
1✔
5901
};
1✔
5902
Object.defineProperty(LString.prototype, "length", {
1✔
5903
    get: function() {
1✔
5904
        return this.__string__.length;
4✔
5905
    }
4✔
5906
});
1✔
5907
LString.prototype.clone = function() {
1✔
5908
    return LString(this.valueOf());
×
5909
};
1✔
5910
LString.prototype.fill = function(char) {
1✔
5911
    typecheck('LString::fill', char, ['string', 'character']);
×
5912
    if (char instanceof LCharacter) {
×
5913
        char = char.valueOf();
×
5914
    }
×
5915
    var len = this.__string__.length;
×
5916
    this.__string__ = char.repeat(len);
×
5917
};
1✔
5918
// -------------------------------------------------------------------------
1✔
5919
// :: Number wrapper that handle BigNumbers
1✔
5920
// -------------------------------------------------------------------------
1✔
5921
function LNumber(n, force = false) {
1,597,958✔
5922
    if (n instanceof LNumber) {
1,597,958✔
5923
        return n;
1,277,102✔
5924
    }
1,277,102✔
5925
    if (typeof this !== 'undefined' && !(this instanceof LNumber) ||
1,597,958✔
5926
        typeof this === 'undefined') {
1,597,958✔
5927
        return new LNumber(n, force);
160,325✔
5928
    }
160,325✔
5929
    if (typeof n === 'undefined') {
1,597,958!
5930
        throw new Error('Invalid LNumber constructor call');
×
5931
    }
×
5932
    var _type = LNumber.getType(n);
160,531✔
5933
    if (LNumber.types[_type]) {
1,597,958✔
5934
        return LNumber.types[_type](n, force);
4,160✔
5935
    }
4,160✔
5936
    var parsable = n instanceof Array && LString.isString(n[0]) &&
1,597,958✔
5937
        LNumber.isNumber(n[1]);
1,597,958✔
5938
    if (n instanceof LNumber) {
1,597,958!
5939
        return LNumber(n.value);
×
5940
    }
×
5941
    if (!LNumber.isNumber(n) && !parsable) {
1,597,958!
5942
        throw new Error(`You can't create LNumber from ${type(n)}`);
×
5943
    }
×
5944
    // prevent infinite loop https://github.com/indutny/bn.js/issues/186
156,371✔
5945
    if (n === null) {
1,597,958!
5946
        n = 0;
×
5947
    }
×
5948
    var value;
156,371✔
5949
    if (parsable) {
1,597,958✔
5950
        var [str, radix] = n;
4,641✔
5951
        if (str instanceof LString) {
4,641!
5952
            str = str.valueOf();
×
5953
        }
×
5954
        if (radix instanceof LNumber) {
4,641!
5955
            radix = radix.valueOf();
×
5956
        }
×
5957
        var sign = str.match(/^([+-])/);
4,641✔
5958
        var minus = false;
4,641✔
5959
        if (sign) {
4,641✔
5960
            str = str.replace(/^[+-]/, '');
552✔
5961
            if (sign[1] === '-') {
552✔
5962
                minus = true;
207✔
5963
            }
207✔
5964
        }
552✔
5965
    }
4,641✔
5966
    if (Number.isNaN(n)) {
1,597,958✔
5967
        return LFloat(n);
544✔
5968
    } else if (parsable && Number.isNaN(parseInt(str, radix))) {
1,597,958✔
5969
        return nan;
1✔
5970
    } else if (typeof BigInt !== 'undefined') {
155,827✔
5971
        if (typeof n !== 'bigint') {
155,826✔
5972
            if (parsable) {
155,263✔
5973
                let prefix;
4,640✔
5974
                // default number base (radix) supported by BigInt constructor
4,640✔
5975
                switch (radix) {
4,640✔
5976
                    case 8:
4,640✔
5977
                        prefix = '0o';
32✔
5978
                        break;
32✔
5979
                    case 16:
4,640✔
5980
                        prefix = '0x';
99✔
5981
                        break;
99✔
5982
                    case 2:
4,640✔
5983
                        prefix = '0b';
98✔
5984
                        break;
98✔
5985
                    case 10:
4,640✔
5986
                        prefix = '';
4,411✔
5987
                        break;
4,411✔
5988
                }
4,640✔
5989
                if (typeof prefix === 'undefined') {
4,640!
5990
                    // non standard radix we convert by hand
×
5991
                    var n_radix = BigInt(radix);
×
5992
                    value = [...str].map((x, i) => {
×
5993
                        return BigInt(parseInt(x, radix)) * pow(n_radix, BigInt(i));
×
5994
                    }).reduce((a, b) => a + b);
×
5995
                } else {
4,640✔
5996
                    value = BigInt(prefix + str);
4,640✔
5997
                }
4,640✔
5998
            } else {
155,263✔
5999
                value = BigInt(n);
150,623✔
6000
            }
150,623✔
6001
            if (minus) {
155,263✔
6002
                value *= BigInt(-1);
207✔
6003
            }
207✔
6004
        } else {
155,826✔
6005
            value = n;
563✔
6006
        }
563✔
6007
        return LBigInteger(value, true);
155,826✔
6008
    } else if (typeof BN !== 'undefined' && !(n instanceof BN)) {
155,826!
6009
        if (n instanceof Array) {
×
6010
            return LBigInteger(new BN(...n));
×
6011
        }
×
6012
        return LBigInteger(new BN(n));
×
6013
    } else if (parsable) {
×
6014
        this.constant(parseInt(str, radix), 'integer');
×
6015
    } else {
×
6016
        this.constant(n, 'integer');
×
6017
    }
×
6018
}
1,597,958✔
6019

1✔
6020
// -------------------------------------------------------------------------
1✔
6021
LNumber.prototype.constant = function(value, type) {
1✔
6022
    enumerable(this, '__value__', value);
338,124✔
6023
    enumerable(this, '__type__', type);
338,124✔
6024
};
1✔
6025
// -------------------------------------------------------------------------
1✔
6026
LNumber.types = {
1✔
6027
    float: function(n, force = false) {
1✔
6028
        return new LFloat(n, force);
2,372✔
6029
    },
1✔
6030
    complex: function(n, force = false) {
1✔
6031
        if (!LNumber.isComplex(n)) {
1,481!
6032
            n = { im: 0, re: n };
×
6033
        }
×
6034
        return new LComplex(n, force);
1,481✔
6035
    },
1✔
6036
    rational: function(n, force = false) {
1✔
6037
        if (!LNumber.isRational(n)) {
414✔
6038
            n = { num: n, denom: 1 };
23✔
6039
        }
23✔
6040
        return new LRational(n, force);
414✔
6041
    }
414✔
6042
};
1✔
6043
// -------------------------------------------------------------------------
1✔
6044
LNumber.prototype.serialize = function() {
1✔
6045
    return this.__value__;
1✔
6046
};
1✔
6047
// -------------------------------------------------------------------------
1✔
6048
LNumber.prototype.isNaN = function() {
1✔
6049
    return Number.isNaN(this.__value__);
669✔
6050
};
1✔
6051
// -------------------------------------------------------------------------
1✔
6052
LNumber.prototype.gcd = function(b) {
1✔
6053
    // ref: https://rosettacode.org/wiki/Greatest_common_divisor#JavaScript
83✔
6054
    var a = this.abs();
83✔
6055
    b = b.abs();
83✔
6056
    if (b.cmp(a) === 1) {
83✔
6057
        var temp = a;
72✔
6058
        a = b;
72✔
6059
        b = temp;
72✔
6060
    }
72✔
6061
    while (true) {
83✔
6062
        a = a.rem(b);
116✔
6063
        if (a.cmp(0) === 0) {
116✔
6064
            return b;
76✔
6065
        }
76✔
6066
        b = b.rem(a);
40✔
6067
        if (b.cmp(0) === 0) {
116✔
6068
            return a;
7✔
6069
        }
7✔
6070
    }
116✔
6071
};
1✔
6072
// -------------------------------------------------------------------------
1✔
6073
LNumber.isFloat = function isFloat(n) {
1✔
6074
    return n instanceof LFloat || (Number(n) === n && n % 1 !== 0);
160,849✔
6075
};
1✔
6076
// -------------------------------------------------------------------------
1✔
6077
LNumber.isNumber = function(n) {
1✔
6078
    return n instanceof LNumber ||
16,207,254✔
6079
        (LNumber.isNative(n) || LNumber.isBN(n));
16,207,254✔
6080
};
1✔
6081
// -------------------------------------------------------------------------
1✔
6082
LNumber.isComplex = function(n) {
1✔
6083
    if (!n) {
337,052✔
6084
        return false;
24,368✔
6085
    }
24,368✔
6086
    var ret = n instanceof LComplex ||
337,052✔
6087
        ((LNumber.isNumber(n.im) || LNumber.isRational(n.im) || Number.isNaN(n.im)) &&
311,620✔
6088
         (LNumber.isNumber(n.re) || LNumber.isRational(n.re) || Number.isNaN(n.re)));
337,052!
6089
    return ret;
337,052✔
6090
};
1✔
6091
// -------------------------------------------------------------------------
1✔
6092
LNumber.isRational = function(n) {
1✔
6093
    if (!n) {
465,545✔
6094
        return false;
329,878✔
6095
    }
329,878✔
6096
    return n instanceof LRational ||
465,545✔
6097
        (LNumber.isNumber(n.num) && LNumber.isNumber(n.denom));
465,545✔
6098
};
1✔
6099
// -------------------------------------------------------------------------
1✔
6100
LNumber.isInteger = function(n) {
1✔
6101
    if (!(LNumber.isNative(n) || n instanceof LNumber)) {
71!
6102
        return false;
×
6103
    }
×
6104
    if (LNumber.isFloat(n)) {
71✔
6105
        return false;
11✔
6106
    }
11✔
6107
    if (LNumber.isRational(n)) {
71✔
6108
        return false;
10✔
6109
    }
10✔
6110
    if (LNumber.isComplex(n)) {
71✔
6111
        return false;
33✔
6112
    }
33✔
6113
    return true;
17✔
6114
};
1✔
6115
// -------------------------------------------------------------------------
1✔
6116
LNumber.isNative = function(n) {
1✔
6117
    return typeof n === 'bigint' || typeof n === 'number';
16,090,571✔
6118
};
1✔
6119
// -------------------------------------------------------------------------
1✔
6120
LNumber.isBigInteger = function(n) {
1✔
6121
    return n instanceof LBigInteger || typeof n === 'bigint' ||
325,078!
6122
        LNumber.isBN(n);
325,078✔
6123
};
1✔
6124
// -------------------------------------------------------------------------
1✔
6125
LNumber.isBN = function(n) {
1✔
6126
    return typeof BN !== 'undefined' && n instanceof BN;
15,868,486!
6127
};
1✔
6128
// -------------------------------------------------------------------------
1✔
6129
LNumber.getArgsType = function(a, b) {
1✔
6130
    if (a instanceof LFloat || b instanceof LFloat) {
×
6131
        return LFloat;
×
6132
    }
×
6133
    if (a instanceof LBigInteger || b instanceof LBigInteger) {
×
6134
        return LBigInteger;
×
6135
    }
×
6136
    return LNumber;
×
6137
};
1✔
6138
// -------------------------------------------------------------------------
1✔
6139
LNumber.prototype.toString = function(radix) {
1✔
6140
    if (Number.isNaN(this.__value__)) {
23,482!
6141
        return '+nan.0';
×
6142
    }
×
6143
    if (radix >= 2 && radix < 36) {
23,482✔
6144
        return this.__value__.toString(radix);
26✔
6145
    }
26✔
6146
    return this.__value__.toString();
23,456✔
6147
};
1✔
6148
// -------------------------------------------------------------------------
1✔
6149
LNumber.prototype.asType = function(n) {
1✔
6150
    var _type = LNumber.getType(this);
155✔
6151
    return LNumber.types[_type] ? LNumber.types[_type](n) : LNumber(n);
155✔
6152
};
1✔
6153
// -------------------------------------------------------------------------
1✔
6154
LNumber.prototype.isBigNumber = function() {
1✔
6155
    return typeof this.__value__ === 'bigint' ||
21!
6156
        typeof BN !== 'undefined' && !(this.value instanceof BN);
21✔
6157
};
1✔
6158
// -------------------------------------------------------------------------
1✔
6159
['floor', 'ceil', 'round'].forEach(fn => {
1✔
6160
    LNumber.prototype[fn] = function() {
3✔
6161
        if (this.float || LNumber.isFloat(this.__value__)) {
12✔
6162
            return LNumber(Math[fn](this.__value__));
4✔
6163
        }
4✔
6164
        return LNumber(Math[fn](this.valueOf()));
8✔
6165
    };
3✔
6166
});
1✔
6167
// -------------------------------------------------------------------------
1✔
6168
LNumber.prototype.valueOf = function() {
1✔
6169
    if (LNumber.isNative(this.__value__)) {
71,377✔
6170
        return Number(this.__value__);
71,377✔
6171
    } else if (LNumber.isBN(this.__value__)) {
71,377!
UNCOV
6172
        return this.__value__.toNumber();
×
UNCOV
6173
    }
×
6174
};
1✔
6175
// -------------------------------------------------------------------------
1✔
6176
// Type coercion matrix
1✔
6177
// -------------------------------------------------------------------------
1✔
6178
const matrix = (function() {
1✔
6179
    var i = (a, b) => [a, b];
1✔
6180
    return {
1✔
6181
        bigint: {
1✔
6182
            bigint: i,
1✔
6183
            float: (a, b) => [LFloat(a.valueOf(), true), b],
1✔
6184
            rational: (a, b) => [{ num: a, denom: 1 }, b],
1✔
6185
            complex: (a, b) => [{ im: 0, re: a }, b]
1✔
6186
        },
1✔
6187
        integer: {
1✔
6188
            integer: i,
1✔
6189
            float: (a, b) => [LFloat(a.valueOf(), true), b],
1✔
6190
            rational: (a, b) => [{ num: a, denom: 1 }, b],
1✔
6191
            complex: (a, b) => [{ im: 0, re: a }, b]
1✔
6192
        },
1✔
6193
        float: {
1✔
6194
            bigint: (a, b) => [a, b && LFloat(b.valueOf(), true)],
1✔
6195
            integer: (a, b) => [a, b && LFloat(b.valueOf(), true)],
1✔
6196
            float: i,
1✔
6197
            rational: (a, b) => [a, b && LFloat(b.valueOf(), true)],
1✔
6198
            complex: (a, b) => [{ re: a, im: LFloat(0, true) }, b]
1✔
6199
        },
1✔
6200
        complex: {
1✔
6201
            bigint: complex('bigint'),
1✔
6202
            integer: complex('integer'),
1✔
6203
            float: complex('float'),
1✔
6204
            rational: complex('rational'),
1✔
6205
            complex: (a, b) => {
1✔
6206
                const [a_re, b_re] = LNumber.coerce(a.__re__, b.__re__);
272✔
6207
                const [a_im, b_im] = LNumber.coerce(a.__im__, b.__im__);
272✔
6208
                return [
272✔
6209
                    { im: a_im, re: a_re },
272✔
6210
                    { im: b_im, re: b_re }
272✔
6211
                ];
272✔
6212
            }
272✔
6213
        },
1✔
6214
        rational: {
1✔
6215
            bigint: (a, b) => [a, b && { num: b, denom: 1 }],
1✔
6216
            integer: (a, b) => [a, b && { num: b, denom: 1 }],
1✔
6217
            float: (a, b) => [LFloat(a.valueOf()), b],
1✔
6218
            rational: i,
1✔
6219
            complex: (a, b) => {
1✔
6220
                return [
38✔
6221
                    {
38✔
6222
                        im: coerce(a.__type__, b.__im__.__type__, 0)[0],
38✔
6223
                        re: coerce(a.__type__, b.__re__.__type__, a)[0]
38✔
6224
                    },
38✔
6225
                    {
38✔
6226
                        im: coerce(a.__type__, b.__im__.__type__, b.__im__)[0],
38✔
6227
                        re: coerce(a.__type__, b.__re__.__type__, b.__re__)[0]
38✔
6228
                    }
38✔
6229
                ];
38✔
6230
            }
38✔
6231
        }
1✔
6232
    };
1✔
6233
    function complex(type) {
1✔
6234
        return (a, b) => {
4✔
6235
            return [
312✔
6236
                {
312✔
6237
                    im: coerce(type, a.__im__.__type__, 0, a.__im__)[1],
312✔
6238
                    re: coerce(type, a.__re__.__type__, 0, a.__re__)[1]
312✔
6239
                },
312✔
6240
                {
312✔
6241
                    im: coerce(type, a.__im__.__type__, 0, 0)[1],
312✔
6242
                    re: coerce(type, b.__type__, 0, b)[1]
312✔
6243
                }
312✔
6244
            ];
312✔
6245
        };
4✔
6246
    }
4✔
6247
})();
1✔
6248
// -------------------------------------------------------------------------
1✔
6249
function coerce(type_a, type_b, a, b) {
1,400✔
6250
    return matrix[type_a][type_b](a, b);
1,400✔
6251
}
1,400✔
6252
// -------------------------------------------------------------------------
1✔
6253
LNumber.coerce = function(a, b) {
1✔
6254
    const a_type = LNumber.getType(a);
325,450✔
6255
    const b_type = LNumber.getType(b);
325,450✔
6256
    if (!matrix[a_type]) {
325,450!
UNCOV
6257
        throw new Error(`LNumber::coerce unknown lhs type ${a_type}`);
×
6258
    } else if (!matrix[a_type][b_type]) {
325,450!
UNCOV
6259
        throw new Error(`LNumber::coerce unknown rhs type ${b_type}`);
×
UNCOV
6260
    }
×
6261
    var tmp = matrix[a_type][b_type](a, b);
325,450✔
6262
    return tmp.map(n => LNumber(n, true));
325,450✔
6263
};
1✔
6264
// -------------------------------------------------------------------------
1✔
6265
LNumber.prototype.coerce = function(n) {
1✔
6266
    if (!(typeof n === 'number' || n instanceof LNumber)) {
324,686!
UNCOV
6267
        throw new Error(`LNumber: you can't coerce ${type(n)}`);
×
UNCOV
6268
    }
×
6269
    if (typeof n === 'number') {
324,686✔
6270
        n = LNumber(n);
6,720✔
6271
    }
6,720✔
6272
    return LNumber.coerce(this, n);
324,686✔
6273
};
1✔
6274
// -------------------------------------------------------------------------
1✔
6275
LNumber.getType = function(n) {
1✔
6276
    if (n instanceof LNumber) {
811,586✔
6277
        return n.__type__;
651,055✔
6278
    }
651,055✔
6279
    if (LNumber.isFloat(n)) {
811,586✔
6280
        return 'float';
2,288✔
6281
    }
2,288✔
6282
    if (LNumber.isComplex(n)) {
811,586✔
6283
        return 'complex';
1,481✔
6284
    }
1,481✔
6285
    if (LNumber.isRational(n)) {
811,586✔
6286
        return 'rational';
391✔
6287
    }
391✔
6288
    if (typeof n === 'number') {
811,586✔
6289
        return 'integer';
151,167✔
6290
    }
151,167✔
6291
    if ((typeof BigInt !== 'undefined' && typeof n !== 'bigint') ||
811,586✔
6292
        (typeof BN !== 'undefined' && !(n instanceof BN))) {
811,586!
6293
        return 'bigint';
4,641✔
6294
    }
4,641✔
6295
};
1✔
6296
// -------------------------------------------------------------------------
1✔
6297
LNumber.prototype.isFloat = function() {
1✔
6298
    return !!(LNumber.isFloat(this.__value__) || this.float);
10✔
6299
};
1✔
6300
// -------------------------------------------------------------------------
1✔
6301
var mapping = {
1✔
6302
    'add': '+',
1✔
6303
    'sub': '-',
1✔
6304
    'mul': '*',
1✔
6305
    'div': '/',
1✔
6306
    'rem': '%',
1✔
6307
    'or': '|',
1✔
6308
    'and': '&',
1✔
6309
    'neg': '~',
1✔
6310
    'shl': '>>',
1✔
6311
    'shr': '<<'
1✔
6312
};
1✔
6313
var rev_mapping = {};
1✔
6314
Object.keys(mapping).forEach((key) => {
1✔
6315
    rev_mapping[mapping[key]] = key;
10✔
6316
    LNumber.prototype[key] = function(n) {
10✔
6317
        return this.op(mapping[key], n);
171,014✔
6318
    };
10✔
6319
});
1✔
6320
// -------------------------------------------------------------------------
1✔
6321
LNumber._ops = {
1✔
6322
    '*': function(a, b) {
1✔
6323
        return a * b;
12,838✔
6324
    },
1✔
6325
    '+': function(a, b) {
1✔
6326
        return a + b;
89,141✔
6327
    },
1✔
6328
    '-': function(a, b) {
1✔
6329
        if (typeof b === 'undefined') {
66,825✔
6330
            return -a;
136✔
6331
        }
136✔
6332
        return a - b;
66,689✔
6333
    },
1✔
6334
    '/': function(a, b) {
1✔
6335
        return a / b;
1,545✔
6336
    },
1✔
6337
    '%': function(a, b) {
1✔
6338
        return a % b;
2,026✔
6339
    },
1✔
6340
    '|': function(a, b) {
1✔
UNCOV
6341
        return a | b;
×
6342
    },
1✔
6343
    '&': function(a, b) {
1✔
UNCOV
6344
        return a & b;
×
6345
    },
1✔
6346
    '~': function(a) {
1✔
UNCOV
6347
        return ~a;
×
6348
    },
1✔
6349
    '>>': function(a, b) {
1✔
6350
        return a >> b;
×
6351
    },
1✔
6352
    '<<': function(a, b) {
1✔
UNCOV
6353
        return a << b;
×
UNCOV
6354
    }
×
6355
};
1✔
6356
// -------------------------------------------------------------------------
1✔
6357
LNumber.prototype.op = function(op, n) {
1✔
6358
    if (typeof n === 'undefined') {
173,023✔
6359
        return LNumber(LNumber._ops[op](this.valueOf()));
136✔
6360
    }
136✔
6361
    if (typeof n === 'number') {
173,023✔
6362
        n = LNumber(n);
48,828✔
6363
    }
48,828✔
6364
    if (Number.isNaN(this.__value__) && !LNumber.isComplex(n) ||
173,023✔
6365
        !LNumber.isComplex(this) && Number.isNaN(n.__value__)) {
173,023✔
6366
        return LNumber(NaN);
283✔
6367
    }
283✔
6368
    const [a, b] = this.coerce(n);
172,604✔
6369
    if (a._op) {
172,604✔
6370
        return a._op(op, b);
172,604✔
6371
    }
172,604✔
6372
    return LNumber(LNumber._ops[op](a, b));
×
6373
};
1✔
6374
// -------------------------------------------------------------------------
1✔
6375
LNumber.prototype.sqrt = function() {
1✔
6376
    var value = this.valueOf();
×
6377
    if (this.cmp(0) < 0) {
×
UNCOV
6378
        var im = Math.sqrt(-value);
×
UNCOV
6379
        return LComplex({ re: 0, im });
×
UNCOV
6380
    }
×
6381
    return LNumber(Math.sqrt(value));
×
6382
};
1✔
6383
// -------------------------------------------------------------------------
1✔
6384
var pow = function(a, b) {
1✔
UNCOV
6385
    return Math.pow(a, b);
×
6386
};
1✔
6387
// -------------------------------------------------------------------------
1✔
6388
// use native exponential operator if possible (it's way faster)
1✔
6389
// -------------------------------------------------------------------------
1✔
6390
try {
1✔
6391
    var exp_op = new Function('a,b', 'return a ** b');
1✔
6392
    if (exp_op(2, 2) === 4) {
1✔
6393
        pow = exp_op;
1✔
6394
    }
1✔
6395
} catch (e) {
1!
UNCOV
6396
    // ignore
×
UNCOV
6397
}
×
6398
// -------------------------------------------------------------------------
1✔
6399
LNumber.prototype.pow = function(n) {
1✔
6400
    var value;
123✔
6401
    const [a, b] = this.coerce(n);
123✔
6402
    if (LNumber.isNative(a.__value__) && LNumber.isNative(b.__value__)) {
123✔
6403
        value = pow(a.__value__, b.__value__);
121✔
6404
    } else if (LNumber.isBN(a.__value__) && LNumber.isBN(b.__value__)) {
123!
UNCOV
6405
        value = this.__value__.pow(n.__value__);
×
6406
    } else if (a.pow) {
2✔
6407
        return a.pow(b);
2✔
6408
    }
2✔
6409
    return LNumber(value);
121✔
6410
};
1✔
6411
// -------------------------------------------------------------------------
1✔
6412
LNumber.prototype.abs = function() {
1✔
6413
    var value = this.__value__;
206✔
6414
    if (LNumber.isNative(this.__value__)) {
206✔
6415
        if (value < 0) {
206✔
6416
            value = -value;
39✔
6417
        }
39✔
6418
    } else if (LNumber.isBN(value)) {
206!
UNCOV
6419
        value.iabs();
×
UNCOV
6420
    }
×
6421
    return new LNumber(value);
206✔
6422
};
1✔
6423
// -------------------------------------------------------------------------
1✔
6424
LNumber.prototype.isOdd = function() {
1✔
6425
    if (LNumber.isNative(this.__value__)) {
21✔
6426
        if (this.isBigNumber()) {
21✔
6427
            return this.__value__ % BigInt(2) === BigInt(1);
21✔
6428
        }
21✔
6429
        if (this.__type__ === 'float') {
×
6430
            throw new Error('Invalid number float');
×
6431
        }
×
6432
        return this.__value__ % 2 === 1;
×
UNCOV
6433
    } else if (LNumber.isBN(this.__value__)) {
×
UNCOV
6434
        return this.__value__.isOdd();
×
UNCOV
6435
    }
×
UNCOV
6436
    throw new Error(`Invalid number ${this.__type__}`);
×
6437
};
1✔
6438
// -------------------------------------------------------------------------
1✔
6439
LNumber.prototype.isEven = function() {
1✔
6440
    return !this.isOdd();
5✔
6441
};
1✔
6442
// -------------------------------------------------------------------------
1✔
6443
LNumber.prototype.cmp = function(n) {
1✔
6444
    const [a, b] = this.coerce(n);
150,714✔
6445
    function cmp(a, b) {
150,714✔
6446
        if (a.__value__ < b.__value__) {
150,712✔
6447
            return -1;
50,279✔
6448
        } else if (a.__value__ === b.__value__) {
150,712✔
6449
            return 0;
40,987✔
6450
        } else {
100,433✔
6451
            return 1;
59,446✔
6452
        }
59,446✔
6453
    }
150,712✔
6454
    if (a.__type__ === 'bigint') {
150,714✔
6455
        if (LNumber.isNative(a.__value__)) {
146,921✔
6456
            return cmp(a, b);
146,921✔
6457
        } else if (LNumber.isBN(a.__value__)) {
146,921!
UNCOV
6458
            return this.__value__.cmp(b.__value__);
×
UNCOV
6459
        }
×
6460
    } else if (a instanceof LFloat) {
150,714✔
6461
        return cmp(a, b);
3,791✔
6462
    }
3,791✔
6463
};
1✔
6464
// -------------------------------------------------------------------------
1✔
6465
// :: COMPLEX TYPE
1✔
6466
// -------------------------------------------------------------------------
1✔
6467
function LComplex(n, force = false) {
4,857✔
6468
    if (typeof this !== 'undefined' && !(this instanceof LComplex) ||
4,857✔
6469
        typeof this === 'undefined') {
4,857✔
6470
        return new LComplex(n, force);
1,688✔
6471
    }
1,688✔
6472
    if (n instanceof LComplex) {
4,857!
6473
        return LComplex({ im: n.__im__, re: n.__re__ });
×
6474
    }
×
6475
    if (LNumber.isNumber(n) && force) {
4,857!
6476
        if (!force) {
×
6477
            return Number(n);
×
6478
        }
×
6479
    } else if (!LNumber.isComplex(n)) {
4,857!
UNCOV
6480
        const msg = `Invalid constructor call for LComplex expect &(:im <num> :re <num>) \
×
UNCOV
6481
object but got ${toString(n)}`;
×
UNCOV
6482
        throw new Error(msg);
×
UNCOV
6483
    }
×
6484
    var im = n.im instanceof LNumber ? n.im : LNumber(n.im);
4,857✔
6485
    var re = n.re instanceof LNumber ? n.re : LNumber(n.re);
4,857✔
6486
    this.constant(im, re);
4,857✔
6487
}
4,857✔
6488
// -------------------------------------------------------------------------
1✔
6489
LComplex.prototype = Object.create(LNumber.prototype);
1✔
6490
LComplex.prototype.constructor = LComplex;
1✔
6491
// -------------------------------------------------------------------------
1✔
6492
LComplex.prototype.constant = function(im, re) {
1✔
6493
    enumerable(this, '__im__', im);
3,169✔
6494
    enumerable(this, '__re__', re);
3,169✔
6495
    enumerable(this, '__type__', 'complex');
3,169✔
6496
};
1✔
6497
// -------------------------------------------------------------------------
1✔
6498
LComplex.prototype.abs = function() {
1✔
6499
    return LNumber(this.modulus());
11✔
6500
};
1✔
6501
// -------------------------------------------------------------------------
1✔
6502
LComplex.prototype.serialize = function() {
1✔
6503
    return {
1✔
6504
        re: this.__re__,
1✔
6505
        im: this.__im__
1✔
6506
    };
1✔
6507
};
1✔
6508
// -------------------------------------------------------------------------
1✔
6509
LComplex.prototype.toRational = function(n) {
1✔
6510
    let im = this.__im__, re = this.__re__;
6✔
6511
    if (LNumber.isFloat(this.__im__)) {
6✔
6512
        im = LFloat(this.__im__).toRational(n);
4✔
6513
    }
4✔
6514
    if (LNumber.isFloat(this.__re__)) {
6✔
6515
        re = LFloat(this.__re__).toRational(n);
4✔
6516
    }
4✔
6517
    return LComplex({ im, re });
6✔
6518
};
1✔
6519
// -------------------------------------------------------------------------
1✔
6520
LComplex.prototype.pow = function(n) {
1✔
6521
    const cmp = n.cmp(0);
38✔
6522
    if (n === 0) {
38!
UNCOV
6523
        return LNumber(1);
×
UNCOV
6524
    }
×
6525
    const angle = LNumber(Math.atan2(this.__im__.valueOf(), this.__re__.valueOf()));
38✔
6526
    const magnitude = LNumber(this.modulus());
38✔
6527
    if (LNumber.isComplex(n) && n.__im__.cmp(0) !== 0) {
38✔
6528
        // Complex exponent of a complex numbers
33✔
6529
        // equation taken from https://math.stackexchange.com/a/476998/31117
33✔
6530
        let p = n.mul(Math.log(magnitude.valueOf())).add(LComplex.i.mul(angle).mul(n));
33✔
6531
        if (!LNumber.isComplex(p)) {
33✔
6532
            return LFloat(Math.E).pow(p);
2✔
6533
        }
2✔
6534
        const e = LFloat(Math.E).pow(p.__re__.valueOf());
31✔
6535
        return LComplex({
31✔
6536
            re: e.mul(Math.cos(p.__im__.valueOf())),
31✔
6537
            im: e.mul(Math.sin(p.__im__.valueOf()))
31✔
6538
        });
31✔
6539
    }
31✔
6540
    const positive = n.__re__.cmp(0) > 0;
5✔
6541
    n = n.__re__.valueOf();
5✔
6542
    if (LNumber.isInteger(n) && positive) {
38✔
6543
        let result = this;
1✔
6544
        while (--n) {
1✔
6545
            result = result.mul(this);
1✔
6546
        }
1✔
6547
        return result;
1✔
6548
    }
1✔
6549
    // equation taken from Wikipedia:
4✔
6550
    // https://w.wiki/97V3#Integer_and_fractional_exponents
4✔
6551
    const r = magnitude.pow(n);
4✔
6552
    const a = angle.mul(n);
4✔
6553
    return LComplex({ re: r.mul(Math.cos(a)), im: r.mul(Math.sin(a)) });
4✔
6554
};
1✔
6555
// -------------------------------------------------------------------------
1✔
6556
LComplex.prototype.add = function(n) {
1✔
6557
    return this.complex_op('add', n, function(a_re, b_re, a_im, b_im) {
215✔
6558
        return {
215✔
6559
            re: a_re.add(b_re),
215✔
6560
            im: a_im.add(b_im)
215✔
6561
        };
215✔
6562
    });
215✔
6563
};
1✔
6564
// -------------------------------------------------------------------------
1✔
6565
// :: factor is used in / and modulus
1✔
6566
// -------------------------------------------------------------------------
1✔
6567
LComplex.prototype.factor = function() {
1✔
6568
    // fix rounding when calculating (/ 1.0 1/10+1/10i)
142✔
6569
    if (this.__im__ instanceof LFloat || this.__im__ instanceof LFloat) {
142✔
6570
        let { __re__: re, __im__: im } = this;
54✔
6571
        let x, y;
54✔
6572
        if (re instanceof LFloat) {
54✔
6573
            x = re.toRational().mul(re.toRational());
47✔
6574
        } else {
54✔
6575
            x = re.mul(re);
7✔
6576
        }
7✔
6577
        if (im instanceof LFloat) {
54✔
6578
            y = im.toRational().mul(im.toRational());
54✔
6579
        } else {
54!
UNCOV
6580
            y = im.mul(im);
×
UNCOV
6581
        }
×
6582
        return x.add(y);
54✔
6583
    } else {
142✔
6584
        return this.__re__.mul(this.__re__).add(this.__im__.mul(this.__im__));
88✔
6585
    }
88✔
6586
};
1✔
6587
// -------------------------------------------------------------------------
1✔
6588
LComplex.prototype.modulus = function() {
1✔
6589
    return this.factor().sqrt();
64✔
6590
};
1✔
6591
// -------------------------------------------------------------------------
1✔
6592
LComplex.prototype.conjugate = function() {
1✔
6593
    return LComplex({ re: this.__re__, im: this.__im__.sub() });
78✔
6594
};
1✔
6595
// -------------------------------------------------------------------------
1✔
6596
LComplex.prototype.sqrt = function() {
1✔
6597
    const r = this.modulus();
9✔
6598
    // code based ok Kawa Scheme source code (file DComplex.java)
9✔
6599
    // Copyright (c) 1997  Per M.A. Bothner.
9✔
6600
    // Released under MIT License
9✔
6601
    let re, im;
9✔
6602
    if (r.cmp(0) === 0) {
9!
UNCOV
6603
        re = im = r;
×
6604
    } else if (this.__re__.cmp(0) === 1) {
9✔
6605
        re = LFloat(0.5).mul(r.add(this.__re__)).sqrt();
4✔
6606
        im = this.__im__.div(re).div(2);
4✔
6607
    } else {
9✔
6608
        im = LFloat(0.5).mul(r.sub(this.__re__)).sqrt();
5✔
6609
        if (this.__im__.cmp(0) === -1) {
5!
UNCOV
6610
            im = im.sub();
×
UNCOV
6611
        }
×
6612
        re = this.__im__.div(im).div(2);
5✔
6613
    }
5✔
6614
    return LComplex({ im, re });
9✔
6615
};
1✔
6616
// -------------------------------------------------------------------------
1✔
6617
LComplex.prototype.div = function(n) {
1✔
6618
    if (LNumber.isNumber(n) && !LNumber.isComplex(n)) {
114✔
6619
        if (!(n instanceof LNumber)) {
33!
UNCOV
6620
            n = LNumber(n);
×
UNCOV
6621
        }
×
6622
        const re = this.__re__.div(n);
33✔
6623
        const im = this.__im__.div(n);
33✔
6624
        return LComplex({ re, im });
33✔
6625
    } else if (!LNumber.isComplex(n)) {
114!
UNCOV
6626
        throw new Error('[LComplex::div] Invalid value');
×
UNCOV
6627
    }
×
6628
    if (this.cmp(n) === 0) {
114✔
6629
        const [ a, b ] = this.coerce(n);
3✔
6630
        const ret = a.__im__.div(b.__im__);
3✔
6631
        return ret.coerce(b.__re__)[0];
3✔
6632
    }
3✔
6633
    const [ a, b ] = this.coerce(n);
78✔
6634
    const denom = b.factor();
78✔
6635
    const conj = b.conjugate();
78✔
6636
    const num = a.mul(conj);
78✔
6637
    if (!LNumber.isComplex(num)) {
114✔
6638
        return num.div(denom);
1✔
6639
    }
1✔
6640
    const re = num.__re__.op('/', denom);
77✔
6641
    const im = num.__im__.op('/', denom);
77✔
6642
    return LComplex({ re, im });
77✔
6643
};
1✔
6644
// -------------------------------------------------------------------------
1✔
6645
LComplex.prototype.sub = function(n) {
1✔
6646
    return this.complex_op('sub', n, function(a_re, b_re, a_im, b_im) {
37✔
6647
        return {
37✔
6648
            re: a_re.sub(b_re),
37✔
6649
            im: a_im.sub(b_im)
37✔
6650
        };
37✔
6651
    });
37✔
6652
};
1✔
6653
// -------------------------------------------------------------------------
1✔
6654
LComplex.prototype.mul = function(n) {
1✔
6655
    return this.complex_op('mul', n, function(a_re, b_re, a_im, b_im) {
266✔
6656
        var ret = {
266✔
6657
            re: a_re.mul(b_re).sub(a_im.mul(b_im)),
266✔
6658
            im: a_re.mul(b_im).add(b_re.mul(a_im))
266✔
6659
        };
266✔
6660
        return ret;
266✔
6661
    });
266✔
6662
};
1✔
6663
// -------------------------------------------------------------------------
1✔
6664
LComplex.prototype.complex_op = function(name, n, fn) {
1✔
6665
    const calc = (re, im) => {
518✔
6666
        var result = fn(this.__re__, re, this.__im__, im);
518✔
6667
        if ('im' in result && 're' in result) {
518✔
6668
            if (result.im.cmp(0) === 0) {
518✔
6669
                return result.re;
34✔
6670
            }
34✔
6671
            return LComplex(result, true);
484✔
6672
        }
484✔
UNCOV
6673
        return result;
×
6674
    };
518✔
6675
    if (typeof n === 'undefined') {
518✔
6676
        return calc();
2✔
6677
    }
2✔
6678
    if (LNumber.isNumber(n) && !LNumber.isComplex(n)) {
518✔
6679
        if (!(n instanceof LNumber)) {
155✔
6680
            n = LNumber(n);
34✔
6681
        }
34✔
6682
        const im = n.asType(0);
155✔
6683
        n = { __im__: im, __re__: n };
155✔
6684
    } else if (!LNumber.isComplex(n)) {
518!
UNCOV
6685
        throw new Error(`[LComplex::${name}] Invalid value`);
×
UNCOV
6686
    }
×
6687
    var re = n.__re__ instanceof LNumber ? n.__re__ : this.__re__.asType(n.__re__);
518!
6688
    var im = n.__im__ instanceof LNumber ? n.__im__ : this.__im__.asType(n.__im__);
518!
6689
    return calc(re, im);
518✔
6690
};
1✔
6691
// -------------------------------------------------------------------------
1✔
6692
LComplex._op = {
1✔
6693
    '+': 'add',
1✔
6694
    '-': 'sub',
1✔
6695
    '*': 'mul',
1✔
6696
    '/': 'div'
1✔
6697
};
1✔
6698
// -------------------------------------------------------------------------
1✔
6699
LComplex.prototype._op = function(op, n) {
1✔
6700
    const fn = LComplex._op[op];
212✔
6701
    return this[fn](n);
212✔
6702
};
1✔
6703
// -------------------------------------------------------------------------
1✔
6704
LComplex.prototype.cmp = function(n) {
1✔
6705
    const [a, b] = this.coerce(n);
489✔
6706
    const [re_a, re_b] = a.__re__.coerce(b.__re__);
489✔
6707
    const re_cmp = re_a.cmp(re_b);
489✔
6708
    if (re_cmp !== 0) {
489✔
6709
        return re_cmp;
363✔
6710
    } else {
489✔
6711
        const [im_a, im_b] = a.__im__.coerce(b.__im__);
126✔
6712
        return im_a.cmp(im_b);
126✔
6713
    }
126✔
6714
};
1✔
6715
// -------------------------------------------------------------------------
1✔
6716
LComplex.prototype.valueOf = function() {
1✔
6717
    return [this.__re__, this.__im__].map(x => x.valueOf());
45✔
6718
};
1✔
6719
// -------------------------------------------------------------------------
1✔
6720
LComplex.prototype.toString = function() {
1✔
6721
    var result;
138✔
6722
    if (this.__re__.cmp(0) !== 0) {
138✔
6723
        result = [to_string(this.__re__)];
115✔
6724
    } else {
138✔
6725
        result = [];
23✔
6726
    }
23✔
6727
    // NaN and inf already have sign
138✔
6728
    var im = this.__im__.valueOf();
138✔
6729
    var inf = [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY].includes(im);
138✔
6730
    var im_str = to_string(this.__im__);
138✔
6731
    if (!inf && !Number.isNaN(im)) {
138✔
6732
        var zero_check = this.__im__.cmp(0);
118✔
6733
        if (zero_check < 0 || (zero_check === 0 && this.__im__._minus)) {
118!
6734
            result.push('-');
38✔
6735
        } else {
118✔
6736
            result.push('+');
80✔
6737
        }
80✔
6738
        im_str = im_str.replace(/^-/, '');
118✔
6739
    }
118✔
6740
    result.push(im_str);
138✔
6741
    result.push('i');
138✔
6742
    return result.join('');
138✔
6743
};
1✔
6744
// -------------------------------------------------------------------------
1✔
6745
// :: FLOAT TYPE
1✔
6746
// -------------------------------------------------------------------------
1✔
6747
function LFloat(n) {
24,540✔
6748
    if (typeof this !== 'undefined' && !(this instanceof LFloat) ||
24,540✔
6749
        typeof this === 'undefined') {
24,540✔
6750
        return new LFloat(n);
11,084✔
6751
    }
11,084✔
6752
    if (!LNumber.isNumber(n)) {
24,540!
UNCOV
6753
        throw new Error('Invalid constructor call for LFloat');
×
UNCOV
6754
    }
×
6755
    if (n instanceof LNumber) {
24,540✔
6756
        return LFloat(n.valueOf());
408✔
6757
    }
408✔
6758
    if (typeof n === 'number') {
13,048✔
6759
        if (Object.is(n, -0)) {
13,048✔
6760
            Object.defineProperty(this, '_minus', {
89✔
6761
                value: true
89✔
6762
            });
89✔
6763
        }
89✔
6764
        this.constant(n, 'float');
13,048✔
6765
    }
13,048✔
6766
}
24,540✔
6767
// -------------------------------------------------------------------------
1✔
6768
LFloat.prototype = Object.create(LNumber.prototype);
1✔
6769
LFloat.prototype.constructor = LFloat;
1✔
6770
// -------------------------------------------------------------------------
1✔
6771
LFloat.prototype.toString = function(radix) {
1✔
6772
    if (this.__value__ === Number.NEGATIVE_INFINITY) {
161✔
6773
        return '-inf.0';
7✔
6774
    }
7✔
6775
    if (this.__value__ === Number.POSITIVE_INFINITY) {
161✔
6776
        return '+inf.0';
12✔
6777
    }
12✔
6778
    if (Number.isNaN(this.__value__)) {
161✔
6779
        return '+nan.0';
17✔
6780
    }
17✔
6781
    radix &&= radix.valueOf();
125✔
6782
    var str = this.__value__.toString(radix);
125✔
6783
    if (!str.match(/e[+-]?[0-9]+$/i)) {
161✔
6784
        // compatibility with other scheme implementation
122✔
6785
        // In JavaScript scientific notation starts from 6 zeros
122✔
6786
        // in Kawa and Gauche it starts from 3 zeros
122✔
6787
        const number = str.replace(/^-/, '');
122✔
6788
        const sign = this.__value__ < 0 ? '-' : '';
122✔
6789
        if (str.match(/^-?0\.0{3}/)) {
122✔
6790
            const exponent = number.match(/^[.0]+/g)[0].length - 1;
1✔
6791
            const value = number.replace(/^[.0]+/, '').replace(/^([0-9a-f])/i, '$1.');
1✔
6792
            return `${sign}${value}e-${exponent.toString(radix)}`;
1✔
6793
        }
1✔
6794
        // big numbers need decimal point shift to have on number
121✔
6795
        // before the decimal point
121✔
6796
        if (str.match(/^-?[0-9a-f]{7,}\.?/i)) {
122✔
6797
            const exponent = number.match(/^[0-9a-f]+/ig)[0].length - 1;
3✔
6798
            const value = number
3✔
6799
                  .replace(/\./, '')
3✔
6800
                  .replace(/^([0-9a-f])/i, '$1.')
3✔
6801
                  .replace(/0+$/, '')
3✔
6802
                  .replace(/\.$/, '.0');
3✔
6803
            return `${sign}${value}e+${exponent.toString(radix)}`;
3✔
6804
        }
3✔
6805
        if (!LNumber.isFloat(this.__value__)) {
122✔
6806
            var result = str + '.0';
58✔
6807
            return this._minus ? ('-' + result) : result;
58✔
6808
        }
58✔
6809
    }
122✔
6810
    return str.replace(/^([0-9]+)e/, '$1.0e');
63✔
6811
};
1✔
6812
// -------------------------------------------------------------------------
1✔
6813
LFloat.prototype._op = function(op, n) {
1✔
6814
    if (n instanceof LNumber) {
2,608✔
6815
        n = n.__value__;
2,608✔
6816
    }
2,608✔
6817
    const fn = LNumber._ops[op];
2,608✔
6818
    if (op === '/' && this.__value__ === 0 && n === 0) {
2,608!
UNCOV
6819
        return NaN;
×
UNCOV
6820
    }
×
6821
    return LFloat(fn(this.__value__, n), true);
2,608✔
6822
};
1✔
6823
// -------------------------------------------------------------------------
1✔
6824
// same approximation as in guile scheme
1✔
6825
LFloat.prototype.toRational = function(n = null) {
1✔
6826
    if (n === null) {
220✔
6827
        return toRational(this.__value__.valueOf());
220✔
6828
    }
220✔
UNCOV
6829
    return approxRatio(n.valueOf())(this.__value__.valueOf());
×
6830
};
1✔
6831
// -------------------------------------------------------------------------
1✔
6832
LFloat.prototype.sqrt = function() {
1✔
6833
    var value = this.valueOf();
12✔
6834
    if (this.cmp(0) < 0) {
12✔
6835
        var im = LFloat(Math.sqrt(-value));
1✔
6836
        return LComplex({ re: 0, im });
1✔
6837
    }
1✔
6838
    return LFloat(Math.sqrt(value));
11✔
6839
};
1✔
6840
// -------------------------------------------------------------------------
1✔
6841
LFloat.prototype.abs = function() {
1✔
6842
    var value = this.valueOf();
7✔
6843
    if (value < 0) {
7✔
6844
        value = -value;
3✔
6845
    }
3✔
6846
    return LFloat(value);
7✔
6847
};
1✔
6848
// -------------------------------------------------------------------------
1✔
6849
// ref: https://rosettacode.org/wiki/Convert_decimal_number_to_rational
1✔
6850
// -------------------------------------------------------------------------
1✔
6851
var toRational = approxRatio(1e-10);
1✔
6852
function approxRatio(eps) {
1✔
6853
    return function(n) {
1✔
6854
        const gcde = (e, x, y) => {
220✔
6855
                const _gcd = (a, b) => (b < e ? a : _gcd(b, a % b));
220✔
6856
                if (Number.isNaN(x) || Number.isNaN(y)) {
220✔
6857
                    return NaN;
4✔
6858
                }
4✔
6859
                return _gcd(Math.abs(x), Math.abs(y));
216✔
6860
            },
220✔
6861
            c = gcde(eps ? eps : (1 / 10000), 1, n);
220!
6862
        return LRational({ num: Math.floor(n / c), denom: Math.floor(1 / c) });
220✔
6863
    };
1✔
6864
}
1✔
6865
// -------------------------------------------------------------------------
1✔
6866
// :: Source: Kawa gnu.math.RatNum.java
1✔
6867
// :: This algorithm is by Alan Bawden. It has been transcribed
1✔
6868
// :: with permission from Kawa copyright M.A. Bothner.
1✔
6869
// :: which was transcribed from from C-Gambit, copyright Marc Feeley.
1✔
6870
// -------------------------------------------------------------------------
1✔
6871
function rationalize(x, y) {
×
6872
    var a = x.sub(y);
×
6873
    var b = x.add(y);
×
6874
    var result;
×
6875
    if (a.cmp(b) > 0) {
×
6876
        result = simplest_rational2(b, a);
×
6877
    } else if (b.cmp(a) <= 0) {
×
6878
        result = a;
×
6879
    } else if (a.cmp(0) > 0) {
×
6880
        result = simplest_rational2(a, b);
×
6881
    } else if (y.cmp(0) < 0) {
×
6882
        result = LNumber(simplest_rational2(b.sub(), a.sub())).sub();
×
6883
    } else {
×
6884
        result = LNumber(0);
×
6885
    }
×
6886
    if (LNumber.isFloat(y) || LNumber.isFloat(x)) {
×
UNCOV
6887
        return LFloat(result);
×
6888
    }
×
6889
    return result;
×
6890
}
×
6891
// -------------------------------------------------------------------------
1✔
6892
function simplest_rational2(x, y) {
×
6893
    var fx = LNumber(x).floor();
×
6894
    var fy = LNumber(y).floor();
×
6895
    if (x.cmp(fx) < 1) {
×
6896
        return fx;
×
6897
    } else if (fx.cmp(fy) === 0) {
×
6898
        var n = LNumber(1).div(y.sub(fy));
×
6899
        var d = LNumber(1).div(x.sub(fx));
×
6900
        return fx.add(LNumber(1).div(simplest_rational2(n, d)));
×
UNCOV
6901
    } else {
×
UNCOV
6902
        return fx.add(LNumber(1));
×
UNCOV
6903
    }
×
UNCOV
6904
}
×
6905
// -------------------------------------------------------------------------
1✔
6906
function LRational(n, force = false) {
3,382✔
6907
    if (typeof this !== 'undefined' && !(this instanceof LRational) ||
3,382✔
6908
        typeof this === 'undefined') {
3,382✔
6909
        return new LRational(n, force);
1,484✔
6910
    }
1,484✔
6911
    if (!LNumber.isRational(n)) {
3,382!
6912
        throw new Error('Invalid constructor call for LRational');
×
6913
    }
×
6914
    var num, denom;
1,898✔
6915
    if (n instanceof LRational) {
3,382!
UNCOV
6916
        num = LNumber(n.__num__);
×
UNCOV
6917
        denom = LNumber(n.__denom__);
×
6918
    } else {
3,382✔
6919
        num = LNumber(n.num);
1,898✔
6920
        denom = LNumber(n.denom);
1,898✔
6921
    }
1,898✔
6922
    if (!force && denom.cmp(0) !== 0) {
3,382✔
6923
        var is_integer = num.op('%', denom).cmp(0) === 0;
1,509✔
6924
        if (is_integer) {
1,509✔
6925
            return LNumber(num.div(denom));
236✔
6926
        }
236✔
6927
    }
1,509✔
6928
    this.constant(num, denom);
1,662✔
6929
}
3,382✔
6930
// -------------------------------------------------------------------------
1✔
6931
LRational.prototype = Object.create(LNumber.prototype);
1✔
6932
LRational.prototype.constructor = LRational;
1✔
6933
// -------------------------------------------------------------------------
1✔
6934
LRational.prototype.constant = function(num, denom) {
1✔
6935
    enumerable(this, '__num__', num);
1,662✔
6936
    enumerable(this, '__denom__', denom);
1,662✔
6937
    enumerable(this, '__type__', 'rational');
1,662✔
6938
};
1✔
6939
// -------------------------------------------------------------------------
1✔
6940
LRational.prototype.serialize = function() {
1✔
UNCOV
6941
    return {
×
UNCOV
6942
        num: this.__num__,
×
UNCOV
6943
        denom: this.__denom__
×
UNCOV
6944
    };
×
6945
};
1✔
6946
// -------------------------------------------------------------------------
1✔
6947
LRational.prototype.pow = function(n) {
1✔
6948
    if (LNumber.isRational(n)) {
4✔
6949
        // nth root
4✔
6950
        return LNumber(pow(this.valueOf(), n.valueOf()));
4✔
6951
    }
4✔
6952
    var cmp = n.cmp(0);
×
6953
    if (cmp === 0) {
×
6954
        return LNumber(1);
×
6955
    }
×
6956
    if (cmp === -1) {
×
6957
        n = n.sub();
×
6958
        var num = this.__denom__.pow(n);
×
6959
        var denom = this.__num__.pow(n);
×
6960
        return LRational({ num, denom });
×
6961
    }
×
6962
    var result = this;
×
6963
    n = n.valueOf();
×
6964
    while (n > 1) {
×
UNCOV
6965
        result = result.mul(this);
×
UNCOV
6966
        n--;
×
UNCOV
6967
    }
×
UNCOV
6968
    return result;
×
6969
};
1✔
6970
// -------------------------------------------------------------------------
1✔
6971
LRational.prototype.sqrt = function() {
1✔
6972
    const num = this.__num__.sqrt();
37✔
6973
    const denom = this.__denom__.sqrt();
37✔
6974
    if (num instanceof LFloat || denom instanceof LFloat) {
37✔
6975
        return num.div(denom);
11✔
6976
    }
11✔
6977
    return LRational({ num, denom });
26✔
6978
};
1✔
6979
// -------------------------------------------------------------------------
1✔
6980
LRational.prototype.quotient = function() {
1✔
6981
    const num = this.__num__;
×
6982
    const denom = this.__denom__;
×
6983
    if (LNumber.isNative(num) && LNumber.isNative(denom)) {
×
6984
        if (denom.cmp(0) === 0) {
×
6985
            throw new Error("quotient: division by zero");
×
6986
        }
×
6987
        const div = num / denom;
×
6988
        if (LNumber.isBigInteger(div)) {
×
6989
            return div;
×
6990
        }
×
6991
        if (div > 0) {
×
6992
            return Math.floor(div);
×
6993
        } else {
×
UNCOV
6994
            return Math.ceil(div);
×
UNCOV
6995
        }
×
UNCOV
6996
    }
×
UNCOV
6997
    throw new Error('quotient: Invalid argument');
×
6998
};
1✔
6999
// -------------------------------------------------------------------------
1✔
7000
LRational.prototype.abs = function() {
1✔
7001
    var num = this.__num__;
1✔
7002
    var denom = this.__denom__;
1✔
7003
    if (num.cmp(0) === -1) {
1✔
7004
        num = num.sub();
1✔
7005
    }
1✔
7006
    if (denom.cmp(0) !== 1) {
1!
UNCOV
7007
        denom = denom.sub();
×
UNCOV
7008
    }
×
7009
    return LRational({ num, denom });
1✔
7010
};
1✔
7011
// -------------------------------------------------------------------------
1✔
7012
LRational.prototype.cmp = function(n) {
1✔
7013
    return LNumber(this.valueOf(), true).cmp(n);
521✔
7014
};
1✔
7015
// -------------------------------------------------------------------------
1✔
7016
LRational.prototype.toString = function() {
1✔
7017
    var gcd = this.__num__.gcd(this.__denom__);
83✔
7018
    var num, denom;
83✔
7019
    if (gcd.cmp(1) !== 0) {
83✔
7020
        num = this.__num__.div(gcd);
4✔
7021
        if (num instanceof LRational) {
4!
7022
            num = LNumber(num.valueOf(true));
×
7023
        }
×
7024
        denom = this.__denom__.div(gcd);
4✔
7025
        if (denom instanceof LRational) {
4!
UNCOV
7026
            denom = LNumber(denom.valueOf(true));
×
UNCOV
7027
        }
×
7028
    } else {
83✔
7029
        num = this.__num__;
79✔
7030
        denom = this.__denom__;
79✔
7031
    }
79✔
7032
    const minus = this.cmp(0) < 0;
83✔
7033
    if (minus) {
83✔
7034
        if (num.abs().cmp(denom.abs()) === 0) {
17!
7035
            return num.toString();
×
UNCOV
7036
        }
×
7037
    } else if (num.cmp(denom) === 0) {
83!
UNCOV
7038
        return num.toString();
×
UNCOV
7039
    }
×
7040
    return num.toString() + '/' + denom.toString();
83✔
7041
};
1✔
7042
// -------------------------------------------------------------------------
1✔
7043
LRational.prototype.valueOf = function(exact) {
1✔
7044
    if (this.__denom__.cmp(0) === 0) {
1,033✔
7045
        if (this.__num__.cmp(0) < 0) {
2✔
7046
            return Number.NEGATIVE_INFINITY;
1✔
7047
        }
1✔
7048
        return Number.POSITIVE_INFINITY;
1✔
7049
    }
1✔
7050
    if (exact) {
1,033!
UNCOV
7051
        const num = this.__num__.__value__;
×
UNCOV
7052
        const denom = this.__denom__.__value__;
×
NEW
7053
        return LNumber(LNumber._ops['/'](num, denom));
×
UNCOV
7054
    }
×
7055
    return LFloat(this.__num__.valueOf()).div(this.__denom__.valueOf());
1,031✔
7056
};
1✔
7057
// -------------------------------------------------------------------------
1✔
7058
LRational.prototype._rem_quot = function() {
1✔
7059
    const num = this.__num__.__value__;
17✔
7060
    const denom = this.__denom__.__value__;
17✔
7061
    const quotient = LNumber._ops['/'](num, denom);
17✔
7062
    const remainder = LNumber._ops['%'](num, denom);
17✔
7063
    return { remainder, quotient };
17✔
7064
};
1✔
7065
// -------------------------------------------------------------------------
1✔
7066
LRational.prototype.floor = function() {
1✔
7067
    const num = this.__num__.__value__;
17✔
7068
    const denom = this.__denom__.__value__;
17✔
7069
    if (LNumber.isNative(num) && LNumber.isNative(denom)) {
17✔
7070
        const { remainder, quotient } = this._rem_quot();
17✔
7071
        if (quotient < 0 && remainder !== 0) {
17✔
7072
            if (LNumber.isBigInteger(quotient)) {
2✔
7073
                return LNumber(quotient - 1n);
2✔
7074
            }
2✔
NEW
7075
            return LNumber(quotient - 1);
×
NEW
7076
        }
×
7077
        return LNumber(quotient);
15✔
7078
    }
15✔
NEW
7079
    return LNumber(Math.floor(this.valueOf()));
×
7080
};
1✔
7081
// -------------------------------------------------------------------------
1✔
7082
LRational.prototype.round = function() {
1✔
NEW
7083
    const num = this.__num__.__value__;
×
NEW
7084
    const denom = this.__denom__.__value__;
×
NEW
7085
    if (LNumber.isNative(num) && LNumber.isNative(denom)) {
×
NEW
7086
        const { quotient } = this._rem_quot();
×
NEW
7087
        return LNumber(quotient);
×
NEW
7088
    }
×
NEW
7089
    return LNumber(Math.round(this.valueOf()));
×
7090
};
1✔
7091
// -------------------------------------------------------------------------
1✔
7092
LRational.prototype.ceil = function() {
1✔
NEW
7093
    const num = this.__num__.__value__;
×
NEW
7094
    const denom = this.__denom__.__value__;
×
NEW
7095
    if (LNumber.isNative(num) && LNumber.isNative(denom)) {
×
NEW
7096
        const { remainder, quotient } = this._rem_quot();
×
NEW
7097
        if (quotient > 0 && remainder !== 0) {
×
NEW
7098
            if (LNumber.isBigInteger(quotient)) {
×
NEW
7099
                return LNumber(quotient + 1n);
×
NEW
7100
            }
×
NEW
7101
            return LNumber(quotient + 1);
×
NEW
7102
        }
×
NEW
7103
        return LNumber(quotient);
×
NEW
7104
    }
×
NEW
7105
    return LNumber(Math.ceil(this.valueOf()));
×
7106
};
1✔
7107
// -------------------------------------------------------------------------
1✔
7108
LRational.prototype.mul = function(n) {
1✔
7109
    if (!(n instanceof LNumber)) {
360✔
7110
        n = LNumber(n); // handle (--> 1/2 (mul 2))
18✔
7111
    }
18✔
7112
    if (LNumber.isRational(n)) {
360✔
7113
        var num = this.__num__.mul(n.__num__);
283✔
7114
        var denom = this.__denom__.mul(n.__denom__);
283✔
7115
        return LRational({ num, denom });
283✔
7116
    }
283✔
7117
    const [a, b] = LNumber.coerce(this, n);
77✔
7118
    return a.mul(b);
77✔
7119
};
1✔
7120
// -------------------------------------------------------------------------
1✔
7121
LRational.prototype.div = function(n) {
1✔
7122
    if (!(n instanceof LNumber)) {
65!
7123
        n = LNumber(n); // handle (--> 1/2 (div 2))
×
UNCOV
7124
    }
×
7125
    if (LNumber.isRational(n)) {
65✔
7126
        var num = this.__num__.mul(n.__denom__);
39✔
7127
        var denom = this.__denom__.mul(n.__num__);
39✔
7128
        return LRational({ num, denom });
39✔
7129
    }
39✔
7130
    const [a, b] = LNumber.coerce(this, n);
26✔
7131
    const ret = a.div(b);
26✔
7132
    return ret;
26✔
7133
};
1✔
7134
// -------------------------------------------------------------------------
1✔
7135
LRational.prototype._op = function(op, n) {
1✔
7136
    return this[rev_mapping[op]](n);
187✔
7137
};
1✔
7138
// -------------------------------------------------------------------------
1✔
7139
LRational.prototype.sub = function(n) {
1✔
7140
    if (typeof n === 'undefined') {
80✔
7141
        return this.mul(-1);
18✔
7142
    }
18✔
7143
    if (!(n instanceof LNumber)) {
80!
7144
        n = LNumber(n); // handle (--> 1/2 (sub 1))
×
7145
    }
×
7146
    if (LNumber.isRational(n)) {
80✔
7147
        var num = n.__num__.sub();
21✔
7148
        var denom = n.__denom__;
21✔
7149
        return this.add(LRational({ num, denom }));
21✔
7150
    }
21✔
7151
    if (!(n instanceof LNumber)) {
80!
UNCOV
7152
        n = LNumber(n).sub();
×
7153
    } else {
80✔
7154
        n = n.sub();
41✔
7155
    }
41✔
7156
    const [a, b] = LNumber.coerce(this, n);
41✔
7157
    return a.add(b);
41✔
7158
};
1✔
7159
// -------------------------------------------------------------------------
1✔
7160
LRational.prototype.add = function(n) {
1✔
7161
    if (!(n instanceof LNumber)) {
348✔
7162
        n = LNumber(n); // handle (--> 1/2 (add 1))
1✔
7163
    }
1✔
7164
    if (LNumber.isRational(n)) {
348✔
7165
        const a_denom = this.__denom__;
254✔
7166
        const b_denom = n.__denom__;
254✔
7167
        const a_num = this.__num__;
254✔
7168
        const b_num = n.__num__;
254✔
7169
        let denom, num;
254✔
7170
        if (a_denom !== b_denom) {
254✔
7171
            num = b_denom.mul(a_num).add(b_num.mul(a_denom));
254✔
7172
            denom = a_denom.mul(b_denom);
254✔
7173
        } else {
254!
UNCOV
7174
            num = a_num.add(b_num);
×
UNCOV
7175
            denom = a_denom;
×
UNCOV
7176
        }
×
7177
        return LRational({ num, denom });
254✔
7178
    }
254✔
7179
    if (LNumber.isFloat(n)) {
348✔
7180
        return LFloat(this.valueOf()).add(n);
18✔
7181
    }
18✔
7182
    const [a, b] = LNumber.coerce(this, n);
76✔
7183
    return a.add(b);
76✔
7184
};
1✔
7185
// -------------------------------------------------------------------------
1✔
7186
function LBigInteger(n, native) {
650,152✔
7187
    if (typeof this !== 'undefined' && !(this instanceof LBigInteger) ||
650,152✔
7188
        typeof this === 'undefined') {
650,152✔
7189
        return new LBigInteger(n, native);
325,076✔
7190
    }
325,076✔
7191
    if (n instanceof LBigInteger) {
650,152!
UNCOV
7192
        return LBigInteger(n.__value__, n._native);
×
UNCOV
7193
    }
×
7194
    if (!LNumber.isBigInteger(n)) {
650,152!
UNCOV
7195
        throw new Error('Invalid constructor call for LBigInteger');
×
7196
    }
×
7197
    this.constant(n, 'bigint');
325,076✔
7198
    Object.defineProperty(this, '_native', {
325,076✔
7199
        value: native
325,076✔
7200
    });
325,076✔
7201
}
650,152✔
7202
// -------------------------------------------------------------------------
1✔
7203
LBigInteger.prototype = Object.create(LNumber.prototype);
1✔
7204
LBigInteger.prototype.constructor = LBigInteger;
1✔
7205
// -------------------------------------------------------------------------
1✔
7206
LBigInteger.bn_op = {
1✔
7207
    '+': 'iadd',
1✔
7208
    '-': 'isub',
1✔
7209
    '*': 'imul',
1✔
7210
    '/': 'idiv',
1✔
7211
    '%': 'imod',
1✔
7212
    '|': 'ior',
1✔
7213
    '&': 'iand',
1✔
7214
    '~': 'inot',
1✔
7215
    '<<': 'ishrn',
1✔
7216
    '>>': 'ishln'
1✔
7217
};
1✔
7218
LBigInteger.prototype.serialize = function() {
1✔
7219
    return this.__value__.toString();
3✔
7220
};
1✔
7221
// -------------------------------------------------------------------------
1✔
7222
LBigInteger.prototype._op = function(op, n) {
1✔
7223
    if (typeof n === 'undefined') {
169,597!
UNCOV
7224
        if (LNumber.isBN(this.__value__)) {
×
UNCOV
7225
            op = LBigInteger.bn_op[op];
×
UNCOV
7226
            return LBigInteger(this.__value__.clone()[op](), false);
×
UNCOV
7227
        }
×
UNCOV
7228
        return LBigInteger(LNumber._ops[op](this.__value__), true);
×
UNCOV
7229
    }
×
7230
    if (LNumber.isBN(this.__value__) && LNumber.isBN(n.__value__)) {
169,597!
UNCOV
7231
        op = LBigInteger.bn_op[op];
×
UNCOV
7232
        return LBigInteger(this.__value__.clone()[op](n), false);
×
UNCOV
7233
    }
×
7234
    const ret = LNumber._ops[op](this.__value__, n.__value__);
169,597✔
7235
    if (op === '/') {
169,597✔
7236
        var is_integer = this.op('%', n).cmp(0) === 0;
346✔
7237
        if (is_integer) {
346✔
7238
            return LNumber(ret);
267✔
7239
        }
267✔
7240
        return LRational({ num: this, denom: n });
79✔
7241
    }
79✔
7242
    // use native calculation because it's real bigint value
169,250✔
7243
    return LBigInteger(ret, true);
169,250✔
7244
};
1✔
7245
// -------------------------------------------------------------------------
1✔
7246
LBigInteger.prototype.sqrt = function() {
1✔
7247
    var value;
108✔
7248
    var minus = this.cmp(0) < 0;
108✔
7249
    if (LNumber.isNative(this.__value__)) {
108✔
7250
        value = LNumber(Math.sqrt(minus ? -this.valueOf() : this.valueOf()));
108✔
7251
    } else if (LNumber.isBN(this.__value__)) {
108!
UNCOV
7252
        value = minus ? this.__value__.neg().sqrt() : this.__value__.sqrt();
×
UNCOV
7253
    }
×
7254
    if (minus) {
108✔
7255
        return LComplex({ re: 0, im: value });
2✔
7256
    }
2✔
7257
    return value;
106✔
7258
};
1✔
7259
// -------------------------------------------------------------------------
1✔
7260
LNumber.NaN = LNumber(NaN);
1✔
7261
LComplex.i = LComplex({ im: 1, re: 0 });
1✔
7262
// -------------------------------------------------------------------------
1✔
7263
// :: Port abstraction - read should be a function that return next line
1✔
7264
// -------------------------------------------------------------------------
1✔
7265
function InputPort(read, env = global_env) {
4✔
7266
    if (typeof this !== 'undefined' && !(this instanceof InputPort) ||
4✔
7267
        typeof this === 'undefined') {
4✔
7268
        return new InputPort(read);
2✔
7269
    }
2✔
7270
    typecheck('InputPort', read, 'function');
2✔
7271
    read_only(this, '__type__', text_port);
2✔
7272
    var parser;
2✔
7273
    Object.defineProperty(this, '__parser__', {
2✔
7274
        enumerable: true,
2✔
7275
        get: function() {
2✔
7276
            return parser;
20✔
7277
        },
2✔
7278
        set: function(value) {
2✔
UNCOV
7279
            typecheck('InputPort::__parser__', value, 'parser');
×
UNCOV
7280
            parser = value;
×
UNCOV
7281
        }
×
7282
    });
2✔
7283
    this._read = read;
2✔
7284
    this._with_parser = this._with_init_parser.bind(this, async () => {
2✔
7285
        if (!this.char_ready()) {
7✔
7286
            const line = await this._read();
3✔
7287
            parser = new Parser({ env });
3✔
7288
            parser.prepare(line);
3✔
7289
        }
3✔
7290
        return this.__parser__;
7✔
7291
    });
2✔
7292
    this.char_ready = function() {
2✔
7293
        return !!this.__parser__ && this.__parser__.__lexer__.peek() !== eof;
7✔
7294
    };
2✔
7295
    this._make_defaults();
2✔
7296
}
4✔
7297
InputPort.prototype._make_defaults = function() {
1✔
7298
    this.read = this._with_parser((parser) => {
192✔
7299
        return parser.read_object();
25✔
7300
    });
192✔
7301
    this.read_line = this._with_parser((parser) => {
192✔
7302
        return parser.__lexer__.read_line();
7✔
7303
    });
192✔
7304
    this.read_char = this._with_parser((parser) => {
192✔
7305
        return parser.__lexer__.read_char();
207✔
7306
    });
192✔
7307
    this.read_string = this._with_parser((parser, number) => {
192✔
7308
        if (!LNumber.isInteger(number)) {
3!
UNCOV
7309
            const type = LNumber.getType(number);
×
UNCOV
7310
            typeErrorMessage('read-string', type, 'integer');
×
UNCOV
7311
        }
×
7312
        return parser.__lexer__.read_string(number.valueOf());
3✔
7313
    });
192✔
7314
    this.peek_char = this._with_parser((parser) => {
192✔
7315
        return parser.__lexer__.peek_char();
30✔
7316
    });
192✔
7317
};
1✔
7318
InputPort.prototype._with_init_parser = function(make_parser, fn) {
1✔
7319
    var self = this;
960✔
7320
    return async function(...args) {
960✔
7321
        var parser = await make_parser.call(self);
272✔
7322
        return fn(parser, ...args);
272✔
7323
    };
960✔
7324
};
1✔
7325
InputPort.prototype.is_open = function() {
1✔
7326
    return this._with_parser !== null;
4✔
7327
};
1✔
7328
InputPort.prototype.close = function() {
1✔
7329
    this.__parser__ = null;
10✔
7330
    // make content garbage collected, we assign null,
10✔
7331
    // because the value is in prototype
10✔
7332
    this._with_parser = null;
10✔
7333
    ['read', 'close', 'read_char', 'peek-char', 'read_line'].forEach(name => {
10✔
7334
        this[name] = function() {
50✔
7335
            throw new Error('input-port: port is closed');
3✔
7336
        };
50✔
7337
    });
10✔
7338
    this.char_ready = function() {
10✔
UNCOV
7339
        return false;
×
7340
    };
10✔
7341
};
1✔
7342
InputPort.prototype.toString = function() {
1✔
7343
    return '#<input-port>';
1✔
7344
};
1✔
7345
// -------------------------------------------------------------------------
1✔
7346
function OutputPort(write) {
2✔
7347
    if (typeof this !== 'undefined' && !(this instanceof OutputPort) ||
2✔
7348
        typeof this === 'undefined') {
2!
UNCOV
7349
        return new OutputPort(write);
×
UNCOV
7350
    }
×
7351
    typecheck('OutputPort', write, 'function');
2✔
7352
    read_only(this, '__type__', text_port);
2✔
7353
    this.write = write;
2✔
7354
}
2✔
7355
OutputPort.prototype.is_open = function() {
1✔
7356
    return this._closed !== true;
4✔
7357
};
1✔
7358
OutputPort.prototype.close = function() {
1✔
7359
    Object.defineProperty(this, '_closed', {
11✔
7360
        get: () => true,
11✔
7361
        set: () => {},
11✔
7362
        configurable: false,
11✔
7363
        enumerable: false
11✔
7364
    });
11✔
7365
    this.write = function() {
11✔
7366
        throw new Error('output-port: port is closed');
4✔
7367
    };
11✔
7368
};
1✔
7369
OutputPort.prototype.flush = function() {
1✔
UNCOV
7370
    // do nothing
×
7371
};
1✔
7372
OutputPort.prototype.toString = function() {
1✔
UNCOV
7373
    return '#<output-port>';
×
7374
};
1✔
7375
// -------------------------------------------------------------------------
1✔
7376
class BufferedOutputPort extends OutputPort {
1✔
7377
    constructor(fn) {
1✔
7378
        super((...args) => this._write(...args));
2✔
7379
        typecheck('BufferedOutputPort', fn, 'function');
2✔
7380
        read_only(this, '_fn', fn, { hidden: true });
2✔
7381
        read_only(this, '_buffer', [], { hidden: true });
2✔
7382
    }
2✔
7383
    flush() {
1✔
7384
        if (this._buffer.length) {
1✔
7385
            this._fn(this._buffer.join(''));
1✔
7386
            this._buffer.length = 0;
1✔
7387
        }
1✔
7388
    }
1✔
7389
    _write(...args) {
1✔
7390
        if (args.length) {
1✔
7391
            args.forEach(arg => {
1✔
7392
                this._buffer.push(arg);
1✔
7393
            });
1✔
7394
            const last_value = this._buffer[this._buffer.length - 1];
1✔
7395
            if (last_value.match(/\n$/)) {
1✔
7396
                this._buffer[this._buffer.length - 1] = last_value.replace(/\n$/, '');
1✔
7397
                this.flush();
1✔
7398
            }
1✔
7399
        }
1✔
7400
    }
1✔
7401
}
1✔
7402
// -------------------------------------------------------------------------
1✔
7403
function OutputStringPort(toString) {
23✔
7404
    if (typeof this !== 'undefined' && !(this instanceof OutputStringPort) ||
23✔
7405
        typeof this === 'undefined') {
23!
UNCOV
7406
        return new OutputStringPort(toString);
×
UNCOV
7407
    }
×
7408
    typecheck('OutputStringPort', toString, 'function');
23✔
7409
    read_only(this, '__type__', text_port);
23✔
7410
    read_only(this, '__buffer__', []);
23✔
7411
    this.write = (x) => {
23✔
7412
        if (!LString.isString(x)) {
34!
UNCOV
7413
            x = toString(x);
×
7414
        } else {
34✔
7415
            x = x.valueOf();
34✔
7416
        }
34✔
7417
        this.__buffer__.push(x);
34✔
7418
    };
23✔
7419
}
23✔
7420
OutputStringPort.prototype = Object.create(OutputPort.prototype);
1✔
7421
OutputStringPort.prototype.constructor = OutputStringPort;
1✔
7422
OutputStringPort.prototype.toString = function() {
1✔
UNCOV
7423
    return '#<output-port (string)>';
×
7424
};
1✔
7425
OutputStringPort.prototype.valueOf = function() {
1✔
7426
    return this.__buffer__.map(x => x.valueOf()).join('');
22✔
7427
};
1✔
7428
// -------------------------------------------------------------------------
1✔
7429
function OutputFilePort(filename, fd) {
14✔
7430
    if (typeof this !== 'undefined' && !(this instanceof OutputFilePort) ||
14✔
7431
        typeof this === 'undefined') {
14✔
7432
        return new OutputFilePort(filename, fd);
7✔
7433
    }
7✔
7434
    typecheck('OutputFilePort', filename, 'string');
7✔
7435
    read_only(this, '__filename__', filename);
7✔
7436
    read_only(this, '_fd', fd.valueOf(), { hidden: true });
7✔
7437
    read_only(this, '__type__', text_port);
7✔
7438
    this.write = (x) => {
7✔
7439
        if (!LString.isString(x)) {
18!
UNCOV
7440
            x = to_string(x);
×
7441
        } else {
18✔
7442
            x = x.valueOf();
18✔
7443
        }
18✔
7444
        this.fs().write(this._fd, x, function(err) {
18✔
7445
            if (err) {
18!
UNCOV
7446
                throw err;
×
UNCOV
7447
            }
×
7448
        });
18✔
7449
    };
7✔
7450
}
14✔
7451
OutputFilePort.prototype = Object.create(OutputPort.prototype);
1✔
7452
OutputFilePort.prototype.constructor = OutputFilePort;
1✔
7453
OutputFilePort.prototype.fs = function() {
1✔
7454
    if (!this._fs) {
26✔
7455
        this._fs = this.internal('fs');
8✔
7456
    }
8✔
7457
    return this._fs;
26✔
7458
};
1✔
7459
OutputFilePort.prototype.internal = function(name) {
1✔
7460
    return user_env.get('**internal-env**').get(name);
9✔
7461
};
1✔
7462
OutputFilePort.prototype.close = function() {
1✔
7463
    return new Promise((resolve, reject) => {
8✔
7464
        this.fs().close(this._fd, (err) => {
8✔
7465
            if (err) {
8!
UNCOV
7466
                reject(err);
×
7467
            } else {
8✔
7468
                read_only(this, '_fd', null, { hidden: true });
8✔
7469
                OutputPort.prototype.close.call(this);
8✔
7470
                resolve();
8✔
7471
            }
8✔
7472
        });
8✔
7473
    });
8✔
7474
};
1✔
7475
OutputFilePort.prototype.toString = function() {
1✔
UNCOV
7476
    return `#<output-port ${this.__filename__}>`;
×
7477
};
1✔
7478
// -------------------------------------------------------------------------
1✔
7479
function InputStringPort(string, env = global_env) {
24✔
7480
    if (typeof this !== 'undefined' && !(this instanceof InputStringPort) ||
24✔
7481
        typeof this === 'undefined') {
24!
UNCOV
7482
        return new InputStringPort(string);
×
UNCOV
7483
    }
×
7484
    typecheck('InputStringPort', string, 'string');
24✔
7485
    string = string.valueOf();
24✔
7486
    this._with_parser = this._with_init_parser.bind(this, () => {
24✔
7487
        if (!this.__parser__) {
46✔
7488
            this.__parser__ = new Parser({ env });
18✔
7489
            this.__parser__.prepare(string);
18✔
7490
        }
18✔
7491
        return this.__parser__;
46✔
7492
    });
24✔
7493
    read_only(this, '__type__', text_port);
24✔
7494
    this._make_defaults();
24✔
7495
}
24✔
7496
InputStringPort.prototype.char_ready = function() {
1✔
UNCOV
7497
    return true;
×
7498
};
1✔
7499
InputStringPort.prototype = Object.create(InputPort.prototype);
1✔
7500
InputStringPort.prototype.constructor = InputStringPort;
1✔
7501
InputStringPort.prototype.toString = function() {
1✔
7502
    return `#<input-port (string)>`;
2✔
7503
};
1✔
7504
// -------------------------------------------------------------------------
1✔
7505
function ParserInputPort(parser, env = global_env) {
166✔
7506
    if (typeof this !== 'undefined' && !(this instanceof ParserInputPort) ||
166✔
7507
        typeof this === 'undefined') {
166!
UNCOV
7508
        return new ParserInputPort(parser, env);
×
UNCOV
7509
    }
×
7510
    this._with_parser = this._with_init_parser.bind(this, () => {
166✔
7511
        return parser;
219✔
7512
    });
166✔
7513
    read_only(this, '__type__', text_port);
166✔
7514
    this._make_defaults();
166✔
7515
}
166✔
7516

1✔
7517
ParserInputPort.prototype.char_ready = function() {
1✔
UNCOV
7518
    return true;
×
7519
};
1✔
7520
ParserInputPort.prototype = Object.create(InputPort.prototype);
1✔
7521
ParserInputPort.prototype.constructor = ParserInputPort;
1✔
7522
ParserInputPort.prototype.toString = function() {
1✔
UNCOV
7523
    return `#<input-port (parser)>`;
×
7524
};
1✔
7525
// -------------------------------------------------------------------------
1✔
7526
function InputByteVectorPort(bytevectors) {
9✔
7527
    if (typeof this !== 'undefined' && !(this instanceof InputByteVectorPort) ||
9✔
7528
        typeof this === 'undefined') {
9!
UNCOV
7529
        return new InputByteVectorPort(bytevectors);
×
UNCOV
7530
    }
×
7531
    typecheck('InputByteVectorPort', bytevectors, 'uint8array');
9✔
7532
    read_only(this, '__vector__', bytevectors);
9✔
7533
    read_only(this, '__type__', binary_port);
9✔
7534
    var index = 0;
9✔
7535
    Object.defineProperty(this, '__index__', {
9✔
7536
        enumerable: true,
9✔
7537
        get: function() {
9✔
7538
            return index;
292✔
7539
        },
9✔
7540
        set: function(value) {
9✔
7541
            typecheck('InputByteVectorPort::__index__', value, 'number');
46✔
7542
            if (value instanceof LNumber) {
46!
UNCOV
7543
                value = value.valueOf();
×
UNCOV
7544
            }
×
7545
            if (typeof value === 'bigint') {
46!
UNCOV
7546
                value = Number(value);
×
UNCOV
7547
            }
×
7548
            if (Math.floor(value) !== value) {
46!
UNCOV
7549
                throw new Error('InputByteVectorPort::__index__ value is ' +
×
UNCOV
7550
                                'not integer');
×
UNCOV
7551
            }
×
7552
            index = value;
46✔
7553
        }
46✔
7554
    });
9✔
7555
}
9✔
7556
InputByteVectorPort.prototype = Object.create(InputPort.prototype);
1✔
7557
InputByteVectorPort.prototype.constructor = InputByteVectorPort;
1✔
7558
InputByteVectorPort.prototype.toString = function() {
1✔
7559
    return `#<input-port (bytevector)>`;
1✔
7560
};
1✔
7561
InputByteVectorPort.prototype.close = function() {
1✔
7562
    read_only(this, '__vector__', nil);
4✔
7563
    const err = function() {
4✔
7564
        throw new Error('Input-binary-port: port is closed');
1✔
7565
    };
4✔
7566
    ['read_u8', 'close', 'peek_u8', 'read_u8_vector'].forEach(name => {
4✔
7567
        this[name] = err;
16✔
7568
    });
4✔
7569
    this.u8_ready = this.char_ready = function() {
4✔
7570
        return false;
2✔
7571
    };
4✔
7572
};
1✔
7573
InputByteVectorPort.prototype.u8_ready = function() {
1✔
7574
    return true;
×
7575
};
1✔
7576
InputByteVectorPort.prototype.peek_u8 = function() {
1✔
7577
    if (this.__index__ >= this.__vector__.length) {
99✔
7578
        return eof;
4✔
7579
    }
4✔
7580
    return this.__vector__[this.__index__];
95✔
7581
};
1✔
7582
InputByteVectorPort.prototype.skip = function() {
1✔
7583
    if (this.__index__ <= this.__vector__.length) {
46✔
7584
        ++this.__index__;
46✔
7585
    }
46✔
7586
};
1✔
7587
InputByteVectorPort.prototype.read_u8 = function() {
1✔
7588
    const byte = this.peek_u8();
46✔
7589
    this.skip();
46✔
7590
    return byte;
46✔
7591
};
1✔
7592
InputByteVectorPort.prototype.read_u8_vector = function(len) {
1✔
7593
    if (typeof len === 'undefined') {
3✔
7594
        len = this.__vector__.length;
1✔
7595
    } else if (len > this.__index__ + this.__vector__.length) {
3✔
7596
        len = this.__index__ + this.__vector__.length;
1✔
7597
    }
1✔
7598
    if (this.peek_u8() === eof) {
3!
UNCOV
7599
        return eof;
×
UNCOV
7600
    }
×
7601
    return this.__vector__.slice(this.__index__, len);
3✔
7602
};
1✔
7603
// -------------------------------------------------------------------------
1✔
7604
function OutputByteVectorPort() {
1✔
7605
    if (typeof this !== 'undefined' && !(this instanceof OutputByteVectorPort) ||
1✔
7606
        typeof this === 'undefined') {
1!
UNCOV
7607
        return new OutputByteVectorPort();
×
UNCOV
7608
    }
×
7609
    read_only(this, '__type__', binary_port);
1✔
7610
    read_only(this, '_buffer', [], { hidden: true });
1✔
7611
    this.write = function(x) {
1✔
7612
        typecheck('write', x, ['number', 'uint8array']);
7✔
7613
        if (LNumber.isNumber(x)) {
7✔
7614
            this._buffer.push(x.valueOf());
6✔
7615
        } else {
7✔
7616
            this._buffer.push(...Array.from(x));
1✔
7617
        }
1✔
7618
    };
1✔
7619
    Object.defineProperty(this, '__buffer__', {
1✔
7620
        enumerable: true,
1✔
7621
        get: function() {
1✔
7622
            return Uint8Array.from(this._buffer);
2✔
7623
        }
2✔
7624
    });
1✔
7625
}
1✔
7626
OutputByteVectorPort.prototype = Object.create(OutputPort.prototype);
1✔
7627
OutputByteVectorPort.prototype.constructor = OutputByteVectorPort;
1✔
7628
OutputByteVectorPort.prototype.close = function() {
1✔
7629
    OutputPort.prototype.close.call(this);
1✔
7630
    read_only(this, '_buffer', null, { hidden: true });
1✔
7631
};
1✔
7632
OutputByteVectorPort.prototype._close_guard = function() {
1✔
UNCOV
7633
    if (this._closed) {
×
UNCOV
7634
        throw new Error('output-port: binary port is closed');
×
UNCOV
7635
    }
×
7636
};
1✔
7637
OutputByteVectorPort.prototype.write_u8 = function(byte) {
1✔
7638
    typecheck('OutputByteVectorPort::write_u8', byte, 'number');
7✔
7639
    this.write(byte);
7✔
7640
};
1✔
7641
OutputByteVectorPort.prototype.write_u8_vector = function(vector) {
1✔
7642
    typecheck('OutputByteVectorPort::write_u8_vector', vector, 'uint8array');
1✔
7643
    this.write(vector);
1✔
7644
};
1✔
7645
OutputByteVectorPort.prototype.toString = function() {
1✔
UNCOV
7646
    return '#<output-port (bytevector)>';
×
7647
};
1✔
7648
OutputByteVectorPort.prototype.valueOf = function() {
1✔
7649
    return this.__buffer__;
2✔
7650
};
1✔
7651
// -------------------------------------------------------------------------
1✔
7652
function InputFilePort(content, filename) {
13✔
7653
    if (typeof this !== 'undefined' && !(this instanceof InputFilePort) ||
13✔
7654
        typeof this === 'undefined') {
13!
UNCOV
7655
        return new InputFilePort(content, filename);
×
UNCOV
7656
    }
×
7657
    InputStringPort.call(this, content);
13✔
7658
    typecheck('InputFilePort', filename, 'string');
13✔
7659
    read_only(this, '__filename__', filename);
13✔
7660
}
13✔
7661
InputFilePort.prototype = Object.create(InputStringPort.prototype);
1✔
7662
InputFilePort.prototype.constructor = InputFilePort;
1✔
7663
InputFilePort.prototype.toString = function() {
1✔
7664
    return `#<input-port (${this.__filename__})>`;
2✔
7665
};
1✔
7666
// -------------------------------------------------------------------------
1✔
7667
function InputBinaryFilePort(content, filename) {
3✔
7668
    if (typeof this !== 'undefined' && !(this instanceof InputBinaryFilePort) ||
3✔
7669
        typeof this === 'undefined') {
3!
UNCOV
7670
        return new InputBinaryFilePort(content, filename);
×
UNCOV
7671
    }
×
7672
    InputByteVectorPort.call(this, content);
3✔
7673
    typecheck('InputBinaryFilePort', filename, 'string');
3✔
7674
    read_only(this, '__filename__', filename);
3✔
7675
}
3✔
7676
InputBinaryFilePort.prototype = Object.create(InputByteVectorPort.prototype);
1✔
7677
InputBinaryFilePort.prototype.constructor = InputBinaryFilePort;
1✔
7678
InputBinaryFilePort.prototype.toString = function() {
1✔
7679
    return `#<input-binary-port (${this.__filename__})>`;
1✔
7680
};
1✔
7681
// -------------------------------------------------------------------------
1✔
7682
function OutputBinaryFilePort(filename, fd) {
2✔
7683
    if (typeof this !== 'undefined' && !(this instanceof OutputBinaryFilePort) ||
2✔
7684
        typeof this === 'undefined') {
2✔
7685
        return new OutputBinaryFilePort(filename, fd);
1✔
7686
    }
1✔
7687
    typecheck('OutputBinaryFilePort', filename, 'string');
1✔
7688
    read_only(this, '__filename__', filename);
1✔
7689
    read_only(this, '_fd', fd.valueOf(), { hidden: true });
1✔
7690
    read_only(this, '__type__', binary_port);
1✔
7691
    let fs;
1✔
7692
    this.write = (x) => {
1✔
7693
        typecheck('write', x, ['number', 'uint8array']);
1✔
7694
        let buffer;
1✔
7695
        if (!fs) {
1✔
7696
            fs = this.internal('fs');
1✔
7697
        }
1✔
7698
        if (LNumber.isNumber(x)) {
1!
7699
            buffer = new Uint8Array([x.valueOf()]);
×
7700
        } else {
1✔
7701
            buffer = new Uint8Array(Array.from(x));
1✔
7702
        }
1✔
7703
        return new Promise((resolve, reject) => {
1✔
7704
            fs.write(this._fd, buffer, function(err) {
1✔
7705
                if (err) {
1!
UNCOV
7706
                    reject(err);
×
7707
                } else {
1✔
7708
                    resolve();
1✔
7709
                }
1✔
7710
            });
1✔
7711
        });
1✔
7712
    };
1✔
7713
}
2✔
7714
OutputBinaryFilePort.prototype = Object.create(OutputFilePort.prototype);
1✔
7715
OutputBinaryFilePort.prototype.constructor = OutputBinaryFilePort;
1✔
7716
OutputBinaryFilePort.prototype.write_u8 = function(byte) {
1✔
7717
    typecheck('OutputByteVectorPort::write_u8', byte, 'number');
1✔
7718
    this.write(byte);
1✔
7719
};
1✔
7720
OutputBinaryFilePort.prototype.write_u8_vector = function(vector) {
1✔
7721
    typecheck('OutputByteVectorPort::write_u8_vector', vector, 'uint8array');
1✔
7722
    this.write(vector);
1✔
7723
};
1✔
7724
// -------------------------------------------------------------------------
1✔
7725
const binary_port = Symbol.for('binary');
1✔
7726
const text_port = Symbol.for('text');
1✔
7727
var eof = new EOF();
1✔
7728
function EOF() {}
1✔
7729
EOF.prototype.toString = function() {
1✔
UNCOV
7730
    return '#<eof>';
×
7731
};
1✔
7732
// -------------------------------------------------------------------------
1✔
7733
// Simpler way to create interpreter with interaction-environment
1✔
7734
// -------------------------------------------------------------------------
1✔
UNCOV
7735
function Interpreter(name, { stderr, stdin, stdout, command_line = null, ...obj } = {}) {
×
UNCOV
7736
    if (typeof this !== 'undefined' && !(this instanceof Interpreter) ||
×
UNCOV
7737
        typeof this === 'undefined') {
×
UNCOV
7738
        return new Interpreter(name, { stdin, stdout, stderr, command_line, ...obj });
×
UNCOV
7739
    }
×
UNCOV
7740
    if (typeof name === 'undefined') {
×
UNCOV
7741
        name = 'anonymous';
×
UNCOV
7742
    }
×
7743
    this.__env__ = user_env.inherit(name, obj);
×
UNCOV
7744
    this.__parser__ = new Parser({  env: this.__env__ });
×
UNCOV
7745
    this.__env__.set('parent.frame', doc('parent.frame', () => {
×
UNCOV
7746
        return this.__env__;
×
UNCOV
7747
    }, global_env.__env__['parent.frame'].__doc__));
×
UNCOV
7748
    const defaults_name = '**interaction-environment-defaults**';
×
UNCOV
7749
    this.set(defaults_name, get_props(obj).concat(defaults_name));
×
7750
    var inter = internal_env.inherit(`internal-${name}`);
×
UNCOV
7751
    if (is_port(stdin)) {
×
UNCOV
7752
        inter.set('stdin', stdin);
×
UNCOV
7753
    }
×
UNCOV
7754
    if (is_port(stderr)) {
×
UNCOV
7755
        inter.set('stderr', stderr);
×
UNCOV
7756
    }
×
UNCOV
7757
    if (is_port(stdout)) {
×
UNCOV
7758
        inter.set('stdout', stdout);
×
UNCOV
7759
    }
×
UNCOV
7760
    inter.set('command-line', command_line);
×
UNCOV
7761
    set_interaction_env(this.__env__, inter);
×
UNCOV
7762
}
×
7763
// -------------------------------------------------------------------------
1✔
7764
Interpreter.prototype.exec = async function(arg, options = {}) {
1✔
UNCOV
7765
    let {
×
UNCOV
7766
        use_dynamic = false,
×
UNCOV
7767
        dynamic_env,
×
UNCOV
7768
        env
×
UNCOV
7769
    } = options;
×
UNCOV
7770
    typecheck('Interpreter::exec', arg, ['string', 'array'], 1);
×
UNCOV
7771
    typecheck('Interpreter::exec', use_dynamic, 'boolean', 2);
×
UNCOV
7772
    // simple solution to overwrite this variable in each interpreter
×
UNCOV
7773
    // before evaluation of user code
×
7774
    if (!env) {
×
UNCOV
7775
        env = this.__env__;
×
UNCOV
7776
    }
×
UNCOV
7777
    if (!dynamic_env) {
×
UNCOV
7778
        dynamic_env = env;
×
7779
    }
×
7780
    global_env.set('**interaction-environment**', this.__env__);
×
7781
    if (Array.isArray(arg)) {
×
7782
        return exec(arg, { env, dynamic_env, use_dynamic });
×
7783
    } else {
×
7784
        try {
×
7785
            this.__parser__.prepare(arg);
×
7786
            return await exec(this.__parser__, { env, dynamic_env, use_dynamic });
×
7787
        } catch(e) {
×
7788
            if (!e.message?.includes('at line')) {
×
7789
                const location = ` at line ${this.__parser__.get_line() + 1}`;
×
7790
                e.message += location;
×
7791
            }
×
7792
            throw e;
×
7793
        }
×
7794
    }
×
7795
};
1✔
7796
// -------------------------------------------------------------------------
1✔
7797
Interpreter.prototype.get = function(value) {
1✔
7798
    const result = this.__env__.get(value);
×
7799
    if (is_function(result)) {
×
7800
        const context = new LambdaContext({
×
7801
            env: this.__env__
×
7802
        });
×
7803
        return result.bind(context);
×
7804
    }
×
7805
    return result;
×
7806
};
1✔
7807
// -------------------------------------------------------------------------
1✔
7808
Interpreter.prototype.set = function(name, value) {
1✔
7809
    return this.__env__.set(name, value);
×
7810
};
1✔
7811
// -------------------------------------------------------------------------
1✔
7812
Interpreter.prototype.constant = function(name, value) {
1✔
7813
    return this.__env__.constant(name, value);
×
7814
};
1✔
7815
// -------------------------------------------------------------------------
1✔
7816
// Lips Exception used in error function
1✔
7817
// -------------------------------------------------------------------------
1✔
7818
function LipsError(message, args) {
3✔
7819
    this.name = 'LipsError';
3✔
7820
    this.message = message;
3✔
7821
    this.args = args;
3✔
7822
    this.stack = (new Error()).stack;
3✔
7823
}
3✔
7824
LipsError.prototype = new Error();
1✔
7825
LipsError.prototype.constructor = LipsError;
1✔
7826
// -------------------------------------------------------------------------
1✔
7827
// :: Fake exception to handle try catch to break the execution
1✔
7828
// :: of body expression #163
1✔
7829
// -------------------------------------------------------------------------
1✔
7830
class IgnoreException extends Error { }
1✔
7831
// -------------------------------------------------------------------------
1✔
7832
// :: Environment constructor (parent and name arguments are optional)
1✔
7833
// -------------------------------------------------------------------------
1✔
7834
function Environment(obj, parent, name) {
8,145,893✔
7835
    if (arguments.length === 1) {
8,145,893!
7836
        if (typeof arguments[0] === 'object') {
×
7837
            obj = arguments[0];
×
7838
            parent = null;
×
UNCOV
7839
        } else if (typeof arguments[0] === 'string') {
×
UNCOV
7840
            obj = {};
×
UNCOV
7841
            parent = null;
×
7842
            name = arguments[0];
×
7843
        }
×
7844
    }
×
7845
    this.__docs__ = new Map();
8,145,893✔
7846
    this.__env__ = obj;
8,145,893✔
7847
    this.__parent__ = parent;
8,145,893✔
7848
    this.__name__ = name || 'anonymous';
8,145,893!
7849
}
8,145,893✔
7850
// -------------------------------------------------------------------------
1✔
7851
Environment.prototype.list = function() {
1✔
UNCOV
7852
    return get_props(this.__env__);
×
7853
};
1✔
7854
// -------------------------------------------------------------------------
1✔
7855
Environment.prototype.fs = function() {
1✔
UNCOV
7856
    return this.get('**fs**');
×
7857
};
1✔
7858
// -------------------------------------------------------------------------
1✔
7859
Environment.prototype.unset = function(name) {
1✔
UNCOV
7860
    if (name instanceof LSymbol) {
×
UNCOV
7861
        name = name.valueOf();
×
UNCOV
7862
    }
×
UNCOV
7863
    if (name instanceof LString) {
×
UNCOV
7864
        name = name.valueOf();
×
UNCOV
7865
    }
×
UNCOV
7866
    delete this.__env__[name];
×
7867
};
1✔
7868
// -------------------------------------------------------------------------
1✔
7869
Environment.prototype.inherit = function(name, obj = {}) {
1✔
7870
    if (typeof name === "object") {
8,061,598!
UNCOV
7871
        obj = name;
×
UNCOV
7872
    }
×
7873
    if (!name || typeof name === "object") {
8,061,598!
UNCOV
7874
        name = 'child of ' + (this.__name__ || 'unknown');
×
UNCOV
7875
    }
×
7876
    return new Environment(obj || {}, this, name);
8,061,598!
7877
};
1✔
7878
// -------------------------------------------------------------------------
1✔
7879
// :: Lookup function for variable doc strings
1✔
7880
// -------------------------------------------------------------------------
1✔
7881
Environment.prototype.doc = function(name, value = null, dump = false) {
1✔
7882
    if (name instanceof LSymbol) {
16!
7883
        name = name.__name__;
×
7884
    }
×
7885
    if (name instanceof LString) {
16!
7886
        name = name.valueOf();
×
7887
    }
×
7888
    if (value) {
16✔
7889
        if (!dump) {
16✔
7890
            value = trim_lines(value);
2✔
7891
        }
2✔
7892
        this.__docs__.set(name, value);
16✔
7893
        return this;
16✔
7894
    }
16✔
UNCOV
7895
    if (this.__docs__.has(name)) {
×
7896
        return this.__docs__.get(name);
×
UNCOV
7897
    }
×
UNCOV
7898
    if (this.__parent__) {
×
UNCOV
7899
        return this.__parent__.doc(name);
×
7900
    }
×
7901
};
1✔
7902
// -------------------------------------------------------------------------
1✔
7903
// :: Function creates frame environment for usage in functions
1✔
7904
// :: frames are used to it's easier to find environments of the functions
1✔
7905
// :: in scope chain, they are dummy environments just for lookup
1✔
7906
// -------------------------------------------------------------------------
1✔
7907
Environment.prototype.new_frame = function(fn, args) {
1✔
7908
    var frame = this.inherit('__frame__');
5,624,827✔
7909
    frame.set('parent.frame', doc('parent.frame', function(n = 1) {
5,624,827✔
7910
        n = n.valueOf();
19✔
7911
        var scope = frame.__parent__;
19✔
7912
        if (!is_env(scope)) {
19!
UNCOV
7913
            return nil;
×
UNCOV
7914
        }
×
7915
        if (n <= 0) {
19✔
7916
            return scope;
14✔
7917
        }
14✔
7918
        var parent_frame = scope.get('parent.frame');
5✔
7919
        return parent_frame(n - 1);
5✔
7920
    }, global_env.__env__['parent.frame'].__doc__));
5,624,827✔
7921
    args.callee = fn;
5,624,827✔
7922
    frame.set('arguments', args);
5,624,827✔
7923
    return frame;
5,624,827✔
7924
};
1✔
7925
// -------------------------------------------------------------------------
1✔
7926
Environment.prototype._lookup = function(symbol) {
1✔
7927
    if (symbol instanceof LSymbol) {
133,560,816✔
7928
        symbol = symbol.__name__;
9✔
7929
    }
9✔
7930
    if (symbol instanceof LString) {
133,560,816!
7931
        symbol = symbol.valueOf();
×
UNCOV
7932
    }
×
7933
    if (this.__env__.hasOwnProperty(symbol)) {
133,560,816✔
7934
        return Value(this.__env__[symbol]);
14,683,225✔
7935
    }
14,683,225✔
7936
    if (this.__parent__) {
133,560,816✔
7937
        return this.__parent__._lookup(symbol);
112,287,472✔
7938
    }
112,287,472✔
7939
};
1✔
7940
// -------------------------------------------------------------------------
1✔
7941
Environment.prototype.toString = function() {
1✔
7942
    return '#<environment:' + this.__name__ + '>';
×
7943
};
1✔
7944
// -------------------------------------------------------------------------
1✔
7945
Environment.prototype.clone = function() {
1✔
UNCOV
7946
    // duplicate refs
×
UNCOV
7947
    var env = {};
×
UNCOV
7948
    // TODO: duplicated Symbols
×
UNCOV
7949
    Object.keys(this.__env__).forEach(key => {
×
UNCOV
7950
        env[key] = this.__env__[key];
×
UNCOV
7951
    });
×
UNCOV
7952
    return new Environment(env, this.__parent__, this.__name__);
×
7953
};
1✔
7954
// -------------------------------------------------------------------------
1✔
7955
Environment.prototype.merge = function(env, name = 'merge') {
1✔
7956
    typecheck('Environment::merge', env, 'environment');
495✔
7957
    return this.inherit(name, env.__env__);
495✔
7958
};
1✔
7959
// -------------------------------------------------------------------------
1✔
7960
// Value returned in lookup if found value in env and in promise_all
1✔
7961
// -------------------------------------------------------------------------
1✔
7962
function Value(value) {
29,367,087✔
7963
    if (typeof this !== 'undefined' && !(this instanceof Value) ||
29,367,087✔
7964
        typeof this === 'undefined') {
29,367,087✔
7965
        return new Value(value);
14,683,225✔
7966
    }
14,683,225✔
7967
    this.value = value;
14,683,862✔
7968
}
29,367,087✔
7969
// -------------------------------------------------------------------------
1✔
7970
Value.isUndefined = function(x) {
1✔
7971
    return x instanceof Value && typeof x.value === 'undefined';
14,549,390✔
7972
};
1✔
7973
// -------------------------------------------------------------------------
1✔
7974
Value.prototype.valueOf = function() {
1✔
7975
    return this.value;
14,681,897✔
7976
};
1✔
7977
// -------------------------------------------------------------------------
1✔
7978
// :: Different object than value used as object for (values)
1✔
7979
// -------------------------------------------------------------------------
1✔
7980
function Values(values) {
61✔
7981
    if (!values.length) {
61✔
7982
        return;
1✔
7983
    }
1✔
7984
    if (values.length === 1) {
61✔
7985
        return values[0];
14✔
7986
    }
14✔
7987
    if (typeof this !== 'undefined' && !(this instanceof Values) ||
61✔
7988
        typeof this === 'undefined') {
61✔
7989
        return new Values(values);
23✔
7990
    }
23✔
7991
    this.__values__ = values;
23✔
7992
}
61✔
7993
Values.prototype.toString = function() {
1✔
7994
    return this.__values__.map(x => to_string(x)).join('\n');
×
7995
};
1✔
7996
Values.prototype.valueOf = function() {
1✔
7997
    return this.__values__;
19✔
7998
};
1✔
7999
// -------------------------------------------------------------------------
1✔
8000
Environment.prototype.get = function(symbol, options = {}) {
1✔
8001
    // we keep original environment as context for bind
21,026,669✔
8002
    // so print will get user stdout
21,026,669✔
8003
    typecheck('Environment::get', symbol, ['symbol', 'string']);
21,026,669✔
8004
    const { throwError = true } = options;
21,026,669✔
8005
    var name = symbol;
21,026,669✔
8006
    if (name instanceof LSymbol || name instanceof LString) {
21,026,669✔
8007
        name = name.valueOf();
10,121,325✔
8008
    }
10,121,325✔
8009
    var value = this._lookup(name);
21,026,669✔
8010
    if (value instanceof Value) {
21,026,669✔
8011
        if (Value.isUndefined(value)) {
14,549,390✔
8012
            return undefined;
1,965✔
8013
        }
1,965✔
8014
        return patch_value(value.valueOf());
14,547,425✔
8015
    }
14,547,425✔
8016
    var parts;
6,477,279✔
8017
    if (symbol instanceof LSymbol && symbol[LSymbol.object]) {
21,026,669✔
8018
        // dot notation symbols from syntax-rules that are gensyms
9✔
8019
        parts = symbol[LSymbol.object];
9✔
8020
    } else if (typeof name === 'string') {
21,026,669✔
8021
        parts = name.split('.').filter(Boolean);
246,666✔
8022
    }
246,666✔
8023
    if (parts && parts.length > 0) {
21,026,669✔
8024
        var [first, ...rest] = parts;
246,675✔
8025
        value = this._lookup(first);
246,675✔
8026
        if (rest.length) {
246,675✔
8027
            try {
136,313✔
8028
                if (value instanceof Value) {
136,313✔
8029
                    value = value.valueOf();
133,835✔
8030
                } else {
136,313✔
8031
                    value = get(root, first);
2,478✔
8032
                    if (is_function(value)) {
2,478✔
8033
                        value = unbind(value);
2,323✔
8034
                    }
2,323✔
8035
                }
2,478✔
8036
                if (typeof value !== 'undefined') {
136,313✔
8037
                    // object accessor
136,303✔
8038
                    return get(value, ...rest);
136,303✔
8039
                }
136,303✔
8040
            } catch (e) {
136,313!
UNCOV
8041
                throw e;
×
UNCOV
8042
            }
×
8043
        } else if (value instanceof Value) {
246,675!
UNCOV
8044
            return patch_value(value.valueOf());
×
UNCOV
8045
        }
×
8046
        value = get(root, name);
110,372✔
8047
    }
110,372✔
8048
    if (typeof value !== 'undefined') {
21,026,669✔
8049
        return value;
354✔
8050
    }
354✔
8051
    if (throwError) {
21,026,669✔
8052
        throw new Error("Unbound variable `" + name.toString() + "'");
17✔
8053
    }
17✔
8054
};
1✔
8055
// -------------------------------------------------------------------------
1✔
8056
Environment.prototype.set = function(name, value, doc = null) {
1✔
8057
    typecheck('Environment::set', name, ['string', 'symbol']);
15,567,770✔
8058
    if (LNumber.isNumber(value)) {
15,567,770✔
8059
        value = LNumber(value);
315,518✔
8060
    }
315,518✔
8061
    if (name instanceof LSymbol) {
15,567,770✔
8062
        name = name.__name__;
2,864,866✔
8063
    }
2,864,866✔
8064
    if (name instanceof LString) {
15,567,770✔
8065
        name = name.valueOf();
12✔
8066
    }
12✔
8067
    this.__env__[name] = value;
15,567,770✔
8068
    if (doc) {
15,567,770✔
8069
        this.doc(name, doc, true);
14✔
8070
    }
14✔
8071
    return this;
15,567,770✔
8072
};
1✔
8073
// -------------------------------------------------------------------------
1✔
8074
// For internal use only
1✔
8075
// -------------------------------------------------------------------------
1✔
8076
Environment.prototype.constant = function(name, value) {
1✔
8077
    if (this.__env__.hasOwnProperty(name)) {
1!
UNCOV
8078
        throw new Error(`Environment::constant: ${name} already exists`);
×
UNCOV
8079
    }
×
8080
    if (arguments.length === 1 && is_plain_object(arguments[0])) {
1!
UNCOV
8081
        var obj = arguments[0];
×
UNCOV
8082
        Object.keys(obj).forEach(key => {
×
UNCOV
8083
            this.constant(name, obj[key]);
×
UNCOV
8084
        });
×
8085
    } else {
1✔
8086
        enumerable(this.__env__, name, value);
1✔
8087
    }
1✔
8088
    return this;
1✔
8089
};
1✔
8090
// -------------------------------------------------------------------------
1✔
8091
Environment.prototype.has = function(name) {
1✔
8092
    return this.__env__.hasOwnProperty(name);
160,085✔
8093
};
1✔
8094
// -------------------------------------------------------------------------
1✔
8095
Environment.prototype.ref = function(name) {
1✔
8096
    var env = this;
74,618✔
8097
    while (true) {
74,618✔
8098
        if (!env) {
161,439✔
8099
            break;
1,354✔
8100
        }
1,354✔
8101
        if (env.has(name)) {
161,439✔
8102
            return env;
73,264✔
8103
        }
73,264✔
8104
        env = env.__parent__;
86,821✔
8105
    }
86,821✔
8106
};
1✔
8107
// -------------------------------------------------------------------------
1✔
8108
Environment.prototype.parents = function() {
1✔
UNCOV
8109
    var env = this;
×
UNCOV
8110
    var result = [];
×
UNCOV
8111
    while (env) {
×
UNCOV
8112
        result.unshift(env);
×
UNCOV
8113
        env = env.__parent__;
×
UNCOV
8114
    }
×
UNCOV
8115
    return result;
×
8116
};
1✔
8117
// -------------------------------------------------------------------------
1✔
8118
// :: Quote function used to pause evaluation from Macro
1✔
8119
// -------------------------------------------------------------------------
1✔
8120
function quote(value) {
2,817,672✔
8121
    if (is_promise(value)) {
2,817,672!
8122
        return value.then(quote);
×
8123
    }
×
8124
    if (is_pair(value) || value instanceof LSymbol) {
2,817,672✔
8125
        value[__data__] = true;
2,037,558✔
8126
    }
2,037,558✔
8127
    return value;
2,817,672✔
8128
}
2,817,672✔
8129
// -------------------------------------------------------------------------
1✔
8130
// :: Unquote is used for multiple backticks and unquote
1✔
8131
// -------------------------------------------------------------------------
1✔
UNCOV
8132
function Unquote(value, count, max) {
×
UNCOV
8133
    this.value = value;
×
UNCOV
8134
    this.count = count;
×
UNCOV
8135
    this.max = max;
×
UNCOV
8136
}
×
8137
Unquote.prototype.toString = function() {
1✔
UNCOV
8138
    return '#<unquote[' + this.count + '] ' + this.value + '>';
×
8139
};
1✔
8140
// -------------------------------------------------------------------------------
1✔
8141
const native_lambda = _parse(tokenize(`(lambda ()
1✔
8142
                                        "[native code]"
1✔
8143
                                        (throw "Invalid Invocation"))`))[0];
1✔
8144

1✔
8145
// -------------------------------------------------------------------------------
1✔
8146
var get = doc('get', function get(object, ...args) {
1✔
8147
    var value;
332,217✔
8148
    var len = args.length;
332,217✔
8149
    while (args.length) {
332,217✔
8150
        // if arg is symbol someone probably want to get __fn__ from binded function
332,260✔
8151
        if (is_function(object) && typeof args[0] !== 'symbol') {
332,260✔
8152
            object = unbind(object);
2,389✔
8153
        }
2,389✔
8154
        var arg = args.shift();
332,260✔
8155
        var name = unbox(arg);
332,260✔
8156
        // the value was set to false to prevent resolving
332,260✔
8157
        // by Real Promises #153
332,260✔
8158
        if (name === 'then' && object instanceof QuotedPromise) {
332,260✔
8159
            value = QuotedPromise.prototype.then;
4✔
8160
        } else if (name === '__code__' && is_function(object) &&
332,260!
8161
                   typeof object.__code__ === 'undefined') {
332,256!
UNCOV
8162
            value = native_lambda;
×
8163
        } else {
332,256✔
8164
            value = object[name];
332,256✔
8165
        }
332,256✔
8166
        if (typeof value === 'undefined') {
332,260✔
8167
            if (args.length) {
110,252!
UNCOV
8168
                throw new Error(`Try to get ${args[0]} from undefined`);
×
UNCOV
8169
            }
×
8170
            return value;
110,252✔
8171
        } else {
332,260✔
8172
            var context;
222,008✔
8173
            if (args.length - 1 < len) {
222,008✔
8174
                context = object;
222,008✔
8175
            }
222,008✔
8176
            value = patch_value(value, context);
222,008✔
8177
        }
222,008✔
8178
        object = value;
222,008✔
8179
    }
222,008✔
8180
    return value;
221,965✔
8181
}, `(. obj . args)
1✔
8182
    (get obj . args)
1✔
8183

1✔
8184
    This function uses an object as a base and keeps using arguments to get the
1✔
8185
    property of JavaScript object. Arguments need to be a strings.
1✔
8186
    e.g. \`(. console "log")\` if you use any function inside LIPS it
1✔
8187
    will be weakly bound (can be rebound), so you can call this log function
1✔
8188
    without problem unlike in JavaScript when you use
1✔
8189
    \`var log = console.log\`.
1✔
8190
    \`get\` is an alias because . doesn't work everywhere, e.g. you can't
1✔
8191
    pass it as an argument.`);
1✔
8192
// -------------------------------------------------------------------------
1✔
8193
// Function gets internal protected data
1✔
8194
// -------------------------------------------------------------------------
1✔
8195
function get_internal(env) {
373✔
8196
    return interaction(env, '**internal-env**');
373✔
8197
}
373✔
8198
// -------------------------------------------------------------------------
1✔
8199
function internal(env, name) {
207✔
8200
    const internal_env = get_internal(env);
207✔
8201
    return internal_env.get(name);
207✔
8202
}
207✔
8203
// -------------------------------------------------------------------------
1✔
8204
// Get variable from interaction environment
1✔
8205
// -------------------------------------------------------------------------
1✔
8206
function interaction(env, name) {
373✔
8207
    var interaction_env = env.get('**interaction-environment**');
373✔
8208
    return interaction_env.get(name);
373✔
8209
}
373✔
8210
// -------------------------------------------------------------------------
1✔
8211
var internal_env = new Environment({
1✔
8212
    stdout: new BufferedOutputPort(function(...args) {
1✔
8213
        console.log(...args);
1✔
8214
    }),
1✔
8215
    // ------------------------------------------------------------------
1✔
8216
    stderr: new BufferedOutputPort(function(...args) {
1✔
UNCOV
8217
        console.error(...args);
×
8218
    }),
1✔
8219
    'command-line': [],
1✔
8220
    // ------------------------------------------------------------------
1✔
8221
    stdin: InputPort(function() {
1✔
UNCOV
8222
        return Promise.resolve(prompt(''));
×
8223
    }),
1✔
8224
    // those will be compiled by babel regex plugin
1✔
8225
    'letter-unicode-regex': /\p{L}/u,
1✔
8226
    'numeral-unicode-regex': /\p{N}/u,
1✔
8227
    'space-unicode-regex': /\s/u
1✔
8228
}, undefined, 'internal');
1✔
8229
// ----------------------------------------------------------------------
1✔
8230
const nan = LNumber(NaN);
1✔
8231
const constants = {
1✔
8232
    '#t': true,
1✔
8233
    '#f': false,
1✔
8234
    '#true': true,
1✔
8235
    '#false': false,
1✔
8236
    '+inf.0': Number.POSITIVE_INFINITY,
1✔
8237
    '-inf.0': Number.NEGATIVE_INFINITY,
1✔
8238
    '+nan.0': nan,
1✔
8239
    '-nan.0': nan,
1✔
8240
    ...parsable_contants
1✔
8241
};
1✔
8242
// -------------------------------------------------------------------------
1✔
8243
var global_env = new Environment({
1✔
8244
    eof,
1✔
8245
    undefined, // undefined as parser constant breaks most of the unit tests
1✔
8246
    // ---------------------------------------------------------------------
1✔
8247
    'peek-char': doc('peek-char', function(port = null) {
1✔
8248
        if (port === null) {
30✔
8249
            port = internal(this, 'stdin');
11✔
8250
        }
11✔
8251
        typecheck_text_port('peek-char', port, 'input-port');
30✔
8252
        return port.peek_char();
30✔
8253
    }, `(peek-char port)
1✔
8254

1✔
8255
        This function reads and returns a character from the string
1✔
8256
        port, or, if there is no more data in the string port, it
1✔
8257
        returns an EOF.`),
1✔
8258
    // ------------------------------------------------------------------
1✔
8259
    'read-line': doc('read-line', function(port = null) {
1✔
8260
        if (port === null) {
7✔
8261
            port = internal(this, 'stdin');
1✔
8262
        }
1✔
8263
        typecheck_text_port('read-line', port, 'input-port');
7✔
8264
        return port.read_line();
7✔
8265
    }, `(read-line port)
1✔
8266

1✔
8267
        This function reads and returns the next line from the input
1✔
8268
        port.`),
1✔
8269
    // ------------------------------------------------------------------
1✔
8270
    'read-char': doc('read-char', function(port = null) {
1✔
8271
        if (port === null) {
207✔
8272
            port = internal(this, 'stdin');
193✔
8273
        }
193✔
8274
        typecheck_text_port('read-char', port, 'input-port');
207✔
8275
        return port.read_char();
207✔
8276
    }, `(read-char port)
1✔
8277

1✔
8278
        This function reads and returns the next character from the
1✔
8279
        input port.`),
1✔
8280
    // ------------------------------------------------------------------
1✔
8281
    read: doc('read', async function read(arg = null) {
1✔
8282
        const { env } = this;
28✔
8283
        var port;
28✔
8284
        if (arg === null) {
28✔
8285
            port = internal(env, 'stdin');
1✔
8286
        } else {
28✔
8287
            port = arg;
27✔
8288
        }
27✔
8289
        typecheck_text_port('read', port, 'input-port');
28✔
8290
        return port.read.call(env);
28✔
8291
    }, `(read [port])
1✔
8292

1✔
8293
        This function, if called with a port, it will parse the next
1✔
8294
        item from the port. If called without an input, it will read
1✔
8295
        a string from standard input (using the browser's prompt or
1✔
8296
        a user defined input method) and parse it. This function can be
1✔
8297
        used together with \`eval\` to evaluate code from port.`),
1✔
8298
    // ------------------------------------------------------------------
1✔
8299
    pprint: doc('pprint', function pprint(arg) {
1✔
UNCOV
8300
        if (is_pair(arg)) {
×
UNCOV
8301
            arg = new lips.Formatter(arg.toString(true)).break().format();
×
UNCOV
8302
            global_env.get('display').call(global_env, arg);
×
UNCOV
8303
        } else {
×
UNCOV
8304
            global_env.get('write').call(global_env, arg);
×
UNCOV
8305
        }
×
UNCOV
8306
        global_env.get('newline').call(global_env);
×
8307
    }, `(pprint expression)
1✔
8308

1✔
8309
        This function will pretty print its input to stdout. If it is called
1✔
8310
        with a non-list, it will just call the print function on its
1✔
8311
        input.`),
1✔
8312
    // ------------------------------------------------------------------
1✔
8313
    print: doc('print', function print(...args) {
1✔
UNCOV
8314
        const display = global_env.get('display');
×
UNCOV
8315
        const newline = global_env.get('newline');
×
UNCOV
8316
        const { use_dynamic } = this;
×
UNCOV
8317
        const env = global_env;
×
UNCOV
8318
        const dynamic_env = global_env;
×
UNCOV
8319
        args.forEach(arg => {
×
UNCOV
8320
            call_function(display, [arg], { env, dynamic_env, use_dynamic });
×
UNCOV
8321
            call_function(newline, [], { env, dynamic_env, use_dynamic });
×
UNCOV
8322
        });
×
8323
    }, `(print . args)
1✔
8324

1✔
8325
        This function converts each input into a string and prints
1✔
8326
        the result to the standard output (by default it's the
1✔
8327
        console but it can be defined in user code). This function
1✔
8328
        calls \`(newline)\` after printing each input.`),
1✔
8329
    // ------------------------------------------------------------------
1✔
8330
    format: doc('format', function format(str, ...args) {
1✔
8331
        typecheck('format', str, 'string');
170✔
8332
        const re = /(~[as%~])/g;
170✔
8333
        let m = str.match(/(~[as])/g);
170✔
8334
        if (m && m.length > args.length) {
170!
UNCOV
8335
            throw new Error('Not enough arguments');
×
UNCOV
8336
        }
×
8337
        var i = 0;
170✔
8338
        var repr = global_env.get('repr');
170✔
8339
        str = str.replace(re, (x) => {
170✔
8340
            const chr = x[1];
228✔
8341
            if (chr === '~') {
228!
UNCOV
8342
                return '~';
×
8343
            } else if (chr === '%') {
228!
8344
                return '\n';
×
8345
            } else {
228✔
8346
                const arg = args[i++];
228✔
8347
                if (chr === 'a') {
228✔
8348
                    return repr(arg);
228✔
8349
                } else {
228!
8350
                    return repr(arg, true);
×
UNCOV
8351
                }
×
8352
            }
228✔
8353
        });
170✔
8354
        m = str.match(/~([\S])/);
170✔
8355
        if (m) {
170!
UNCOV
8356
            throw new Error(`format: Unrecognized escape sequence ${m[1]}`);
×
UNCOV
8357
        }
×
8358
        return str;
170✔
8359
    }, `(format string n1 n2 ...)
1✔
8360

1✔
8361
        This function accepts a string template and replaces any
1✔
8362
        escape sequences in its inputs:
1✔
8363

1✔
8364
        * ~a value as if printed with \`display\`
1✔
8365
        * ~s value as if printed with \`write\`
1✔
8366
        * ~% newline character
1✔
8367
        * ~~ literal tilde '~'
1✔
8368

1✔
8369
        If there are missing inputs or other escape characters it
1✔
8370
        will error.`),
1✔
8371
    // ------------------------------------------------------------------
1✔
8372
    newline: doc('newline', function newline(port = null) {
1✔
8373
        const display = global_env.get('display');
1✔
8374
        const { use_dynamic } = this;
1✔
8375
        const env = global_env;
1✔
8376
        const dynamic_env = global_env;
1✔
8377
        call_function(display, ['\n', port], { env, dynamic_env, use_dynamic });
1✔
8378
    }, `(newline [port])
1✔
8379

1✔
8380
        Write newline character to standard output or given port`),
1✔
8381
    // ------------------------------------------------------------------
1✔
8382
    display: doc('display', function display(arg, port = null) {
1✔
8383
        if (port === null) {
55✔
8384
            port = internal(this, 'stdout');
1✔
8385
        } else {
55✔
8386
            typecheck('display', port, 'output-port');
54✔
8387
        }
54✔
8388
        let value = arg;
55✔
8389
        if (!(port instanceof OutputBinaryFilePort)) {
55✔
8390
            value = global_env.get('repr')(arg);
55✔
8391
        }
55✔
8392
        port.write.call(global_env, value);
55✔
8393
    }, `(display string [port])
1✔
8394

1✔
8395
        This function outputs the string to the standard output or
1✔
8396
        the port if given. No newline.`),
1✔
8397
    // ------------------------------------------------------------------
1✔
8398
    'display-error': doc('display-error', function error(...args) {
1✔
UNCOV
8399
        const port = internal(this, 'stderr');
×
8400
        const repr = global_env.get('repr');
×
8401
        const value = args.map(repr).join(' ');
×
UNCOV
8402
        port.write.call(global_env, value);
×
UNCOV
8403
        global_env.get('newline').call(this, port);
×
8404
    }, `(display-error . args)
1✔
8405

1✔
8406
        Display an error message on stderr.`),
1✔
8407
    // ------------------------------------------------------------------
1✔
8408
    '%foldcase-string': doc(
1✔
8409
        '%foldcase-string',
1✔
8410
        foldcase_string,
1✔
8411
        `(%foldcase-string string)
1✔
8412

1✔
8413
         Same as string-foldcase but without typechecking`),
1✔
8414
    // ------------------------------------------------------------------
1✔
8415
    '%same-functions': doc('%same-functions', function(a, b) {
1✔
8416
        if (!is_function(a)) {
2!
UNCOV
8417
            return false;
×
UNCOV
8418
        }
×
8419
        if (!is_function(b)) {
2!
UNCOV
8420
            return false;
×
UNCOV
8421
        }
×
8422
        return unbind(a) === unbind(b);
2✔
8423
    }, `(%same-functions a b)
1✔
8424

1✔
8425
        A helper function that checks if the two input functions are
1✔
8426
        the same.`),
1✔
8427
    // ------------------------------------------------------------------
1✔
8428
    help: doc(new Macro('help', function(code, { dynamic_env, use_dynamic, error }) {
1✔
UNCOV
8429
        var symbol;
×
UNCOV
8430
        if (code.car instanceof LSymbol) {
×
UNCOV
8431
            symbol = code.car;
×
UNCOV
8432
        } else if (is_pair(code.car) && code.car.car instanceof LSymbol) {
×
UNCOV
8433
            symbol = code.car.car;
×
UNCOV
8434
        } else {
×
UNCOV
8435
            var env = this;
×
UNCOV
8436
            dynamic_env = this;
×
UNCOV
8437
            var ret = evaluate(code.car, { env, error, dynamic_env, use_dynamic });
×
UNCOV
8438
            if (ret && ret.__doc__) {
×
UNCOV
8439
                return ret.__doc__;
×
UNCOV
8440
            }
×
UNCOV
8441
            return;
×
UNCOV
8442
        }
×
8443
        var __doc__;
×
8444
        var value = this.get(symbol);
×
8445
        __doc__ = value && value.__doc__;
×
8446
        if (__doc__) {
×
8447
            return __doc__;
×
UNCOV
8448
        }
×
UNCOV
8449
        var ref = this.ref(symbol);
×
UNCOV
8450
        if (ref) {
×
UNCOV
8451
            __doc__ = ref.doc(symbol);
×
UNCOV
8452
            if (__doc__) {
×
UNCOV
8453
                return __doc__;
×
UNCOV
8454
            }
×
UNCOV
8455
        }
×
8456
    }), `(help object)
1✔
8457

1✔
8458
         This macro returns documentation for a function, macro, or a variable.`),
1✔
8459
    // ------------------------------------------------------------------
1✔
8460
    cons: doc('cons', function cons(car, cdr) {
1✔
8461
        return new Pair(car, cdr);
294✔
8462
    }, `(cons left right)
1✔
8463

1✔
8464
        This function returns a new list with the first appended
1✔
8465
        before the second. If the second is not a list cons will
1✔
8466
        return a dotted pair.`),
1✔
8467
    // ------------------------------------------------------------------
1✔
8468
    car: doc('car', function car(list) {
1✔
8469
        typecheck('car', list, 'pair');
215,769✔
8470
        return list.car;
215,769✔
8471
    }, `(car pair)
1✔
8472

1✔
8473
        This function returns the car (item 1) of the list.`),
1✔
8474
    // ------------------------------------------------------------------
1✔
8475
    cdr: doc('cdr', function cdr(list) {
1✔
8476
        typecheck('cdr', list, 'pair');
160,144✔
8477
        return list.cdr;
160,144✔
8478
    }, `(cdr pair)
1✔
8479

1✔
8480
        This function returns the cdr (all but first) of the list.`),
1✔
8481
    // ------------------------------------------------------------------
1✔
8482
    'set!': doc(new Macro('set!', function(code, { use_dynamic, ...rest } = {}) {
1✔
8483
        const dynamic_env = this;
73,349✔
8484
        const env = this;
73,349✔
8485
        let ref;
73,349✔
8486
        const eval_args = { ...rest, env: this, dynamic_env, use_dynamic };
73,349✔
8487
        let value = evaluate(code.cdr.car, eval_args);
73,349✔
8488
        value = resolve_promises(value);
73,349✔
8489
        function set(object, key, value) {
73,349✔
8490
            if (is_promise(object)) {
1,028!
8491
                return object.then(key => set(object, key, value));
×
8492
            }
×
8493
            if (is_promise(key)) {
1,028!
8494
                return key.then(key => set(object, key, value));
×
8495
            }
×
8496
            if (is_promise(value)) {
1,028!
8497
                return value.then(value => set(object, key, value));
×
8498
            }
×
8499
            env.get('set-obj!').call(env, object, key, value);
1,028✔
8500
            return value;
1,028✔
8501
        }
1,028✔
8502
        if (is_pair(code.car) && LSymbol.is(code.car.car, '.')) {
73,349!
UNCOV
8503
            var second = code.car.cdr.car;
×
UNCOV
8504
            var third = code.car.cdr.cdr.car;
×
UNCOV
8505
            var object = evaluate(second, eval_args);
×
UNCOV
8506
            var key = evaluate(third, eval_args);
×
UNCOV
8507
            return set(object, key, value);
×
UNCOV
8508
        }
×
8509
        if (!(code.car instanceof LSymbol)) {
73,349!
UNCOV
8510
            throw new Error('set! first argument need to be a symbol or ' +
×
UNCOV
8511
                            'dot accessor that evaluate to object.');
×
UNCOV
8512
        }
×
8513
        var symbol = code.car.valueOf();
73,334✔
8514
        ref = this.ref(code.car.__name__);
73,334✔
8515
        // we don't return value because we only care about sync of set value
73,334✔
8516
        // when value is a promise
73,334✔
8517
        return unpromise(value, value => {
73,334✔
8518
            if (!ref) {
73,334✔
8519
                // case (set! fn.toString (lambda () "xxx"))
1,030✔
8520
                var parts = symbol.split('.');
1,030✔
8521
                if (parts.length > 1) {
1,030✔
8522
                    var key = parts.pop();
1,029✔
8523
                    var name = parts.join('.');
1,029✔
8524
                    var obj = this.get(name, { throwError: false });
1,029✔
8525
                    if (obj) {
1,029✔
8526
                        set(obj, key, value);
1,028✔
8527
                        return;
1,028✔
8528
                    }
1,028✔
8529
                }
1,029✔
8530
                throw new Error('Unbound variable `' + symbol + '\'');
2✔
8531
            }
2✔
8532
            ref.set(symbol, value);
72,304✔
8533
        });
73,334✔
8534
    }), `(set! name value)
1✔
8535

1✔
8536
         Macro that can be used to set the value of the variable or slot (mutate it).
1✔
8537
         set! searches the scope chain until it finds first non empty slot and sets it.`),
1✔
8538
    // ------------------------------------------------------------------
1✔
8539
    'unset!': doc(new Macro('set!', function(code) {
1✔
8540
        if (!(code.car instanceof LSymbol)) {
1!
8541
            throw new Error('unset! first argument need to be a symbol or ' +
×
8542
                            'dot accessor that evaluate to object.');
×
UNCOV
8543
        }
×
8544
        const symbol = code.car;
1✔
8545
        var ref = this.ref(symbol);
1✔
8546
        if (ref) {
1✔
8547
            delete ref.__env__[symbol.__name__];
1✔
8548
        }
1✔
8549
    }), `(unset! name)
1✔
8550

1✔
8551
         Function to delete the specified name from environment.
1✔
8552
         Trying to access the name afterwards will error.`),
1✔
8553
    // ------------------------------------------------------------------
1✔
8554
    'set-car!': doc('set-car!', function(slot, value) {
1✔
8555
        typecheck('set-car!', slot, 'pair');
6✔
8556
        slot.car = value;
6✔
8557
    }, `(set-car! obj value)
1✔
8558

1✔
8559
         Function that sets the car (first item) of the list/pair to specified value.
1✔
8560
         The old value is lost.`),
1✔
8561
    // ------------------------------------------------------------------
1✔
8562
    'set-cdr!': doc('set-cdr!', function(slot, value) {
1✔
8563
        typecheck('set-cdr!', slot, 'pair');
7✔
8564
        slot.cdr = value;
7✔
8565
    }, `(set-cdr! obj value)
1✔
8566

1✔
8567
         Function that sets the cdr (tail) of the list/pair to specified value.
1✔
8568
         It will destroy the list. The old tail is lost.`),
1✔
8569
    // ------------------------------------------------------------------
1✔
8570
    'empty?': doc('empty?', function(x) {
1✔
UNCOV
8571
        return typeof x === 'undefined' || is_nil(x);
×
8572
    }, `(empty? object)
1✔
8573

1✔
8574
         Function that returns #t if value is nil (an empty list) or undefined.`),
1✔
8575
    // ------------------------------------------------------------------
1✔
8576
    gensym: doc(
1✔
8577
        'gensym',
1✔
8578
        gensym,
1✔
8579
        `(gensym)
1✔
8580

1✔
8581
         Generates a unique symbol that is not bound anywhere,
1✔
8582
         to use with macros as meta name.`),
1✔
8583
    // ------------------------------------------------------------------
1✔
8584
    load: doc('load', function load(file, env) {
1✔
8585
        typecheck('load', file, 'string');
4✔
8586
        var g_env = this;
4✔
8587
        if (g_env.__name__ === '__frame__') {
4✔
8588
            g_env = g_env.__parent__;
4✔
8589
        }
4✔
8590
        if (!(env instanceof Environment)) {
4✔
8591
            if (g_env === global_env) {
4✔
8592
                // this is used for let-env + load
3✔
8593
                // this may be obsolete when there is env arg
3✔
8594
                env = g_env;
3✔
8595
            } else {
4✔
8596
                env = this.get('**interaction-environment**');
1✔
8597
            }
1✔
8598
        }
4✔
8599
        const package_name = '@lips';
4✔
8600
        const has_package = file.startsWith(package_name);
4✔
8601
        // TODO: move **module-path** to internal env
4✔
8602
        const PATH = '**module-path**';
4✔
8603
        var module_path = global_env.get(PATH, { throwError: false });
4✔
8604
        file = file.valueOf();
4✔
8605
        if (!file.match(/.[^.]+$/)) {
4!
UNCOV
8606
            file += '.scm';
×
UNCOV
8607
        }
×
8608
        const IS_BIN = file.match(/\.xcb$/);
4✔
8609
        function run(code) {
4✔
8610
            if (IS_BIN) {
4✔
8611
                code = unserialize_bin(code);
1✔
8612
            } else {
4✔
8613
                if (type(code) === 'buffer') {
3✔
8614
                    code = code.toString();
3✔
8615
                }
3✔
8616
                code = code.replace(/^(#!.*)/, function(_, shebang) {
3✔
UNCOV
8617
                    if (is_directive(shebang)) {
×
UNCOV
8618
                        return shebang;
×
UNCOV
8619
                    }
×
UNCOV
8620
                    return '';
×
8621
                });
3✔
8622
                if (code.match(/^\{/)) {
3!
UNCOV
8623
                    code = unserialize(code);
×
UNCOV
8624
                }
×
8625
            }
3✔
8626
            return exec(code, { env });
4✔
8627
        }
4✔
8628
        function fetch(file) {
4✔
UNCOV
8629
            return root.fetch(file)
×
UNCOV
8630
                .then(res => IS_BIN ? res.arrayBuffer() : res.text())
×
UNCOV
8631
                .then((code) => {
×
UNCOV
8632
                    if (IS_BIN) {
×
UNCOV
8633
                        code = new Uint8Array(code);
×
UNCOV
8634
                    }
×
UNCOV
8635
                    return code;
×
UNCOV
8636
                });
×
UNCOV
8637
        }
×
8638
        function get_root_dir() {
4✔
8639
            const __dirname = global_env.get('__dirname');
4✔
8640
            return __dirname.replace(/[^/]+$/, '');
4✔
8641
        }
4✔
8642
        if (is_node()) {
4✔
8643
            return new Promise(async (resolve, reject) => {
4✔
8644
                try {
4✔
8645
                    await node_ready;
4✔
8646
                    const path = nodeRequire('path');
4✔
8647
                    const fs = nodeRequire('fs');
4✔
8648
                    let cwd;
4✔
8649
                    const root_dir = get_root_dir();
4✔
8650
                    if (has_package) {
4✔
8651
                        file = file.replace(package_name, root_dir);
4✔
8652
                    }
4✔
8653
                    if (module_path) {
4!
UNCOV
8654
                        module_path = module_path.valueOf();
×
UNCOV
8655
                        if (!file.startsWith('/')) {
×
UNCOV
8656
                            file = path.join(module_path, file);
×
UNCOV
8657
                        }
×
8658
                    } else if (!file.startsWith('/')) {
4!
UNCOV
8659
                        const cmd = g_env.get('command-line', { throwError: false });
×
UNCOV
8660
                        let args;
×
8661
                        if (cmd) {
×
8662
                            args = await cmd();
×
8663
                        }
×
8664
                        if (args && !is_nil(args)) {
×
UNCOV
8665
                            cwd = process.cwd();
×
UNCOV
8666
                            file = path.join(path.dirname(args.car.valueOf()), file);
×
8667
                        }
×
8668
                    }
×
8669
                    global_env.set(PATH, path.dirname(file));
4✔
8670
                    fs.readFile(file, function(err, data) {
4✔
8671
                        if (err) {
4!
UNCOV
8672
                            reject(err);
×
8673
                            global_env.set(PATH, module_path);
×
8674
                        } else {
4✔
8675
                            try {
4✔
8676
                                run(data).then(() => {
4✔
8677
                                    resolve();
4✔
8678
                                    global_env.set(PATH, module_path);
4✔
8679
                                }).catch(reject);
4✔
8680
                            } catch (e) {
4!
8681
                                reject(e);
×
UNCOV
8682
                            }
×
8683
                        }
4✔
8684
                    });
4✔
8685
                } catch(e) {
4!
UNCOV
8686
                    console.error(e);
×
UNCOV
8687
                }
×
8688
            });
4✔
8689
        }
4✔
UNCOV
8690
        if (has_package) {
×
UNCOV
8691
            let path = global_env.get('__dirname', { throwError: false }) ?? current_script;
×
UNCOV
8692
            path ??= current_script;
×
UNCOV
8693
            const root = path.replace(/dist\/?[^\/]*$/, '');
×
UNCOV
8694
            file = file.replace(package_name, root);
×
UNCOV
8695
        }
×
UNCOV
8696
        if (module_path) {
×
UNCOV
8697
            module_path = module_path.valueOf();
×
8698
            if (!file.startsWith('/')) {
×
8699
                file = module_path + '/' + file.replace(/^\.?\/?/, '');
×
8700
            }
×
8701
        }
×
UNCOV
8702
        return fetch(file).then(code => {
×
8703
            global_env.set(PATH, file.replace(/\/[^/]*$/, ''));
×
8704
            return run(code);
×
8705
        }).then(() => {}).finally(() => {
×
8706
            global_env.set(PATH, module_path);
×
8707
        });
×
8708
    }, `(load filename)
1✔
8709
        (load filename environment)
1✔
8710

1✔
8711
        Fetches the file (from disk or network) and evaluates its content as LIPS code.
1✔
8712
        If the second argument is provided and it's an environment the evaluation
1✔
8713
        will happen in that environment.`),
1✔
8714
    // ------------------------------------------------------------------
1✔
8715
    'while': doc(new Macro('while', function(code, args) {
1✔
8716
        const test = code.car;
17,567✔
8717
        const eval_args = { ...args, env: this };
17,567✔
8718
        const body = new Pair(new LSymbol('begin'), code.cdr);
17,567✔
8719
        return (function loop() {
17,567✔
8720
            return unpromise(evaluate(test, eval_args), test => {
44,216✔
8721
                if (test) {
44,216✔
8722
                    return unpromise(evaluate(body, eval_args), loop);
26,649✔
8723
                }
26,649✔
8724
            });
44,216✔
8725
        })();
17,567✔
8726
    }), `(while cond body)
1✔
8727

1✔
8728
         Creates a loop, it executes cond and body until cond expression is false.`),
1✔
8729
    // ------------------------------------------------------------------
1✔
8730
    'do': doc(new Macro('do', async function(code, { use_dynamic, error }) {
1✔
8731
        const self = this;
9✔
8732
        const dynamic_env = self;
9✔
8733
        let scope = self.inherit('do');
9✔
8734
        const vars = code.car;
9✔
8735
        const test = code.cdr.car;
9✔
8736
        let body = code.cdr.cdr;
9✔
8737
        if (!is_nil(body)) {
9✔
8738
            body = new Pair(LSymbol('begin'), body);
4✔
8739
        }
4✔
8740
        let eval_args = { env: self, dynamic_env, use_dynamic, error };
9✔
8741
        let node = vars;
9✔
8742
        // init variables
9✔
8743
        while (!is_nil(node)) {
9✔
8744
            const item = node.car;
14✔
8745
            scope.set(item.car, await evaluate(item.cdr.car, eval_args));
14✔
8746
            node = node.cdr;
14✔
8747
        }
14✔
8748
        eval_args = { env: scope, dynamic_env, error };
9✔
8749
        while ((await evaluate(test.car, eval_args)) === false) {
9✔
8750
            if (!is_nil(body)) {
29✔
8751
                await lips.evaluate(body, eval_args);
17✔
8752
            }
16✔
8753
            let node = vars;
28✔
8754
            const next = {};
28✔
8755
            // next value of variables
28✔
8756
            while (!is_nil(node)) {
29✔
8757
                const item = node.car;
40✔
8758
                if (!is_nil(item.cdr.cdr)) {
40✔
8759
                    const value = await evaluate(item.cdr.cdr.car, eval_args);
30✔
8760
                    next[item.car.valueOf()] = value;
30✔
8761
                }
30✔
8762
                node = node.cdr;
40✔
8763
            }
40✔
8764
            const symbols = Object.getOwnPropertySymbols(next);
28✔
8765
            // new scope for new iteration
28✔
8766
            eval_args.env = scope = self.inherit('do');
28✔
8767
            Object.keys(next).concat(symbols).forEach(key => {
28✔
8768
                scope.set(key, next[key]);
30✔
8769
            });
28✔
8770
        }
28✔
8771
        if (!is_nil(test.cdr)) {
8✔
8772
            return await evaluate(test.cdr.car, eval_args);
8✔
8773
        }
8✔
8774
    }), `(do ((<var> <init> <next>)) (test return) . body)
1✔
8775

1✔
8776
         Iteration macro that evaluates the expression body in scope of the variables.
1✔
8777
         On each loop it changes the variables according to the <next> expression and runs
1✔
8778
         test to check if the loop should continue. If test is a single value, the macro
1✔
8779
         will return undefined. If the test is a pair of expressions the macro will
1✔
8780
         evaluate and return the second expression after the loop exits.`),
1✔
8781
    // ------------------------------------------------------------------
1✔
8782
    'if': doc(new Macro('if', function(code, { error, use_dynamic }) {
1✔
8783
        const dynamic_env = this;
839,750✔
8784
        const env = this;
839,750✔
8785
        const eval_args = { env, dynamic_env, use_dynamic, error };
839,750✔
8786
        const resolve = (cond) => {
839,750✔
8787
            if (is_false(cond)) {
839,750✔
8788
                return evaluate(code.cdr.cdr.car, eval_args);
459,341✔
8789
            } else {
839,750✔
8790
                return evaluate(code.cdr.car, eval_args);
380,409✔
8791
            }
380,409✔
8792
        };
839,750✔
8793
        if (is_nil(code)) {
839,750!
UNCOV
8794
            throw new Error('too few expressions for `if`');
×
UNCOV
8795
        }
×
8796
        const cond = evaluate(code.car, eval_args);
839,750✔
8797
        return unpromise(cond, resolve);
839,750✔
8798
    }), `(if cond true-expr false-expr)
1✔
8799

1✔
8800
         Macro that evaluates cond expression and if the value is true, it
1✔
8801
         evaluates and returns true-expression, if not it evaluates and returns
1✔
8802
         false-expression.`),
1✔
8803
    // ------------------------------------------------------------------
1✔
8804
    'let-env': new Macro('let-env', function(code, options = {}) {
1✔
8805
        const { dynamic_env, use_dynamic, error } = options;
45✔
8806
        typecheck('let-env', code, 'pair');
45✔
8807
        const ret = evaluate(code.car, { env: this, dynamic_env, error, use_dynamic });
45✔
8808
        return unpromise(ret, function(value) {
45✔
8809
            typecheck('let-env', value, 'environment');
45✔
8810
            return evaluate(Pair(LSymbol('begin'), code.cdr), {
45✔
8811
                env: value, dynamic_env, error
45✔
8812
            });
45✔
8813
        });
45✔
8814
    }, `(let-env env . body)
1✔
8815

1✔
8816
        Special macro that evaluates body in context of given environment
1✔
8817
        object.`),
1✔
8818
    // ------------------------------------------------------------------
1✔
8819
    'letrec': doc(
1✔
8820
        let_macro(Symbol.for('letrec')),
1✔
8821
        `(letrec ((a value-a) (b value-b) ...) . body)
1✔
8822

1✔
8823
         Macro that creates a new environment, then evaluates and assigns values to
1✔
8824
         names and then evaluates the body in context of that environment.
1✔
8825
         Values are evaluated sequentially and the next value can access the
1✔
8826
         previous values/names.`),
1✔
8827
    // ---------------------------------------------------------------------
1✔
8828
    'letrec*': doc(
1✔
8829
        let_macro(Symbol.for('letrec')),
1✔
8830
        `(letrec* ((a value-a) (b value-b) ...) . body)
1✔
8831

1✔
8832
         Same as letrec but the order of execution of the binding is guaranteed,
1✔
8833
         so you can use recursive code as well as referencing the previous binding.
1✔
8834

1✔
8835
         In LIPS both letrec and letrec* behave the same.`),
1✔
8836
    // ---------------------------------------------------------------------
1✔
8837
    'let*': doc(
1✔
8838
        let_macro(Symbol.for('let*')),
1✔
8839
        `(let* ((a value-a) (b value-b) ...) . body)
1✔
8840

1✔
8841
         Macro similar to \`let\`, but the subsequent bindings after the first
1✔
8842
         are evaluated in the environment including the previous let variables,
1✔
8843
         so you can define one variable, and use it in the next's definition.`),
1✔
8844
    // ---------------------------------------------------------------------
1✔
8845
    'let': doc(
1✔
8846
        let_macro(Symbol.for('let')),
1✔
8847
        `(let ((a value-a) (b value-b) ...) . body)
1✔
8848

1✔
8849
         Macro that creates a new environment, then evaluates and assigns values to names,
1✔
8850
         and then evaluates the body in context of that environment.  Values are evaluated
1✔
8851
         sequentially but you can't access previous values/names when the next are
1✔
8852
         evaluated. You can only get them in the body of the let expression.  (If you want
1✔
8853
         to define multiple variables and use them in each other's definitions, use
1✔
8854
         \`let*\`.)`),
1✔
8855
    // ------------------------------------------------------------------
1✔
8856
    'begin*': doc(parallel('begin*', function(values) {
1✔
UNCOV
8857
        return values.pop();
×
8858
    }), `(begin* . body)
1✔
8859

1✔
8860
         This macro is a parallel version of begin. It evaluates each expression
1✔
8861
         in the body and if it's a promise it will await it in parallel and return
1✔
8862
         the value of the last expression (i.e. it uses Promise.all()).`),
1✔
8863
    // ------------------------------------------------------------------
1✔
8864
    shuffle: doc('shuffle', function(arg) {
1✔
8865
        typecheck('shuffle', arg, ['pair', 'nil', 'array']);
4✔
8866
        const random = global_env.get('random')
4✔
8867
        if (is_nil(arg)) {
4✔
8868
            return nil;
1✔
8869
        }
1✔
8870
        if (Array.isArray(arg)) {
4✔
8871
            return shuffle(arg.slice(), random);
1✔
8872
        }
1✔
8873
        let arr = global_env.get('list->array')(arg);
2✔
8874
        arr = shuffle(arr, random);
2✔
8875

2✔
8876
        return global_env.get('array->list')(arr);
2✔
8877
    }, `(shuffle obj)
1✔
8878

1✔
8879
        Order items in vector or list in random order.`),
1✔
8880
    // ------------------------------------------------------------------
1✔
8881
    begin: doc(new Macro('begin', function(code, options) {
1✔
8882
        const eval_args = {...options, env: this };
1,194,430✔
8883
        const arr = global_env.get('list->array')(code);
1,194,430✔
8884
        let result;
1,194,430✔
8885
        return (function loop() {
1,194,430✔
8886
            if (arr.length) {
2,717,127✔
8887
                const code = arr.shift();
1,522,928✔
8888
                const ret = evaluate(code, eval_args);
1,522,928✔
8889
                return unpromise(ret, value => {
1,522,928✔
8890
                    result = value;
1,522,697✔
8891
                    return loop();
1,522,697✔
8892
                });
1,522,928✔
8893
            } else {
2,717,127✔
8894
                return result;
1,194,199✔
8895
            }
1,194,199✔
8896
        })();
1,194,430✔
8897
    }), `(begin . args)
1✔
8898

1✔
8899
         Macro that runs a list of expressions in order and returns the value
1✔
8900
         of the last one. It can be used in places where you can only have a
1✔
8901
         single expression, like (if).`),
1✔
8902
    // ------------------------------------------------------------------
1✔
8903
    'ignore': new Macro('ignore', function(code, options) {
1✔
8904
        const eval_args = { ...options, env: this, dynamic_env: this };
18✔
8905
        evaluate(new Pair(new LSymbol('begin'), code), eval_args);
18✔
8906
    }, `(ignore . body)
1✔
8907

1✔
8908
        Macro that will evaluate the expression and swallow any promises that may
1✔
8909
        be created. It will discard any value that may be returned by the last body
1✔
8910
        expression. The code should have side effects and/or when it's promise
1✔
8911
        it should resolve to undefined.`),
1✔
8912
    // ------------------------------------------------------------------
1✔
8913
    'call/cc': doc(Macro.defmacro('call/cc', function(code, eval_args = {}) {
1✔
8914
        const args = {
13✔
8915
            env: this,
13✔
8916
            ...eval_args
13✔
8917
        };
13✔
8918
        return unpromise(evaluate(code.car, args), (result) => {
13✔
8919
            if (is_function(result)) {
13✔
8920
                return result(new Continuation(null));
13✔
8921
            }
13✔
8922
        });
13✔
8923
    }), `(call/cc proc)
1✔
8924

1✔
8925
         Call-with-current-continuation.
1✔
8926

1✔
8927
         NOT SUPPORTED BY LIPS RIGHT NOW`),
1✔
8928
    // ------------------------------------------------------------------
1✔
8929
    parameterize: doc(new Macro('parameterize', function(code, options) {
1✔
8930
        const { dynamic_env } = options;
7✔
8931
        const env = dynamic_env.inherit('parameterize').new_frame(null, {});
7✔
8932
        const eval_args = { ...options, env: this };
7✔
8933
        let params = code.car;
7✔
8934
        if (!is_pair(params)) {
7!
UNCOV
8935
            const t = type(params);
×
UNCOV
8936
            throw new Error(`Invalid syntax for parameterize expecting pair got ${t}`);
×
UNCOV
8937
        }
×
8938
        function next() {
7✔
8939
            const body = new Pair(new LSymbol('begin',), code.cdr);
7✔
8940
            return evaluate(body, { ...eval_args, dynamic_env: env });
7✔
8941
        }
7✔
8942
        return (function loop() {
7✔
8943
            const pair = params.car;
7✔
8944
            const name = pair.car.valueOf();
7✔
8945
            return unpromise(evaluate(pair.cdr.car, eval_args), function(value) {
7✔
8946
                const param = dynamic_env.get(name, { throwError: false });
7✔
8947
                if (!is_parameter(param)) {
7!
UNCOV
8948
                    throw new Error(`Unknown parameter ${name}`);
×
UNCOV
8949
                }
×
8950
                env.set(name, param.inherit(value));
7✔
8951
                if (!is_null(params.cdr)) {
7!
UNCOV
8952
                    params = params.cdr;
×
UNCOV
8953
                    return loop();
×
8954
                } else {
7✔
8955
                    return next();
7✔
8956
                }
7✔
8957
            });
7✔
8958
        })();
7✔
8959
    }), `(parameterize ((name value) ...)
1✔
8960

1✔
8961
         Macro that change the dynamic variable created by make-parameter.`),
1✔
8962
    // ------------------------------------------------------------------
1✔
8963
    'make-parameter': doc(new Macro('make-parameter', function(code, eval_args) {
1✔
8964
        const dynamic_env = eval_args.dynamic_env;
6✔
8965
        const init = evaluate(code.car, eval_args);
6✔
8966
        let fn;
6✔
8967
        if (is_pair(code.cdr.car)) {
6✔
8968
            fn = evaluate(code.cdr.car, eval_args);
1✔
8969
        }
1✔
8970
        return new Parameter(init, fn);
6✔
8971
    }), `(make-parameter init converter)
1✔
8972

1✔
8973
    Function creates new dynamic variable that can be custimized with parameterize
1✔
8974
    macro. The value should be assigned to a variable e.g.:
1✔
8975

1✔
8976
    (define radix (make-parameter 10))
1✔
8977

1✔
8978
    The result value is a procedure that return the value of dynamic variable.`),
1✔
8979
    // ------------------------------------------------------------------
1✔
8980
    'define-syntax-parameter': doc(new Macro('define-syntax-parameter', function(code, eval_args) {
1✔
8981
        const name = code.car;
10✔
8982
        const env = this;
10✔
8983
        if (!(name instanceof LSymbol)) {
10!
UNCOV
8984
            throw new Error(`define-syntax-parameter: invalid syntax expecting symbol got ${type(name)}`);
×
UNCOV
8985
        }
×
8986
        const syntax = evaluate(code.cdr.car, { env, ...eval_args });
10✔
8987
        typecheck('define-syntax-parameter', syntax, 'syntax', 2);
10✔
8988
        syntax.__name__ = name.valueOf();
10✔
8989
        if (syntax.__name__ instanceof LString) {
10!
UNCOV
8990
            syntax.__name__ = syntax.__name__.valueOf();
×
UNCOV
8991
        }
×
8992
        let __doc__;
10✔
8993
        if (is_pair(code.cdr.cdr) &&
10!
8994
            LString.isString(code.cdr.cdr.car)) {
10!
UNCOV
8995
            __doc__ = code.cdr.cdr.car.valueOf();
×
8996
        }
×
8997
        env.set(code.car, new SyntaxParameter(syntax), __doc__, true);
10✔
8998
    }), `(define-syntax-parameter name syntax [__doc__])
1✔
8999

1✔
9000
         Binds <keyword> to the transformer obtained by evaluating <transformer spec>.
1✔
9001
         The transformer provides the default expansion for the syntax parameter,
1✔
9002
         and in the absence of syntax-parameterize, is functionally equivalent to
1✔
9003
         define-syntax.`),
1✔
9004
    // ------------------------------------------------------------------
1✔
9005
    'syntax-parameterize': doc(new Macro('syntax-parameterize', function(code, eval_args) {
1✔
9006
        const args = global_env.get('list->array')(code.car);
10✔
9007
        const env = this.inherit('syntax-parameterize');
10✔
9008
        while (args.length) {
10✔
9009
            const pair = args.shift();
11✔
9010
            if (!(is_pair(pair) || pair.car instanceof LSymbol)) {
11!
UNCOV
9011
                const msg = `invalid syntax for syntax-parameterize: ${repr(code, true)}`;
×
UNCOV
9012
                throw new Error(`syntax-parameterize: ${msg}`);
×
UNCOV
9013
            }
×
9014
            let syntax = evaluate(pair.cdr.car, { ...eval_args, env: this });
11✔
9015
            const name = pair.car;
11✔
9016
            typecheck('syntax-parameterize', syntax, ['syntax']);
11✔
9017
            typecheck('syntax-parameterize', name, 'symbol');
11✔
9018
            syntax.__name__ = name.valueOf();
11✔
9019
            if (syntax.__name__ instanceof LString) {
11!
UNCOV
9020
                syntax.__name__ = syntax.__name__.valueOf();
×
UNCOV
9021
            }
×
9022
            const parameter = new SyntaxParameter(syntax);
11✔
9023
            // used inside syntax-rules
11✔
9024
            if (name.is_gensym()) {
11✔
9025
                const symbol = name.literal();
9✔
9026
                const parent = this.get(symbol, { throwError: false });
9✔
9027
                if (parent instanceof SyntaxParameter) {
9✔
9028
                    // create anaphoric binding for literal symbol
6✔
9029
                    env.set(symbol, parameter);
6✔
9030
                }
6✔
9031
            }
9✔
9032
            env.set(name, parameter);
11✔
9033
        }
11✔
9034
        const expr = hygienic_begin([
10✔
9035
            env, eval_args.dynamic_env
10✔
9036
        ], code.cdr);
10✔
9037
        return evaluate(expr, { ...eval_args, env });
10✔
9038
    }), `(syntax-parameterize (bindings) body)
1✔
9039

1✔
9040
         Macro work similar to let-syntax but the the bindnds will be exposed to the user.
1✔
9041
         With syntax-parameterize you can define anaphoric macros.`),
1✔
9042
    // ------------------------------------------------------------------
1✔
9043
    define: doc(Macro.defmacro('define', function(code, eval_args) {
1✔
9044
        var env = this;
1,273✔
9045
        if (is_pair(code.car) &&
1,273✔
9046
            code.car.car instanceof LSymbol) {
1,273✔
9047
            var new_code = new Pair(
469✔
9048
                new LSymbol("define"),
469✔
9049
                new Pair(
469✔
9050
                    code.car.car,
469✔
9051
                    new Pair(
469✔
9052
                        new Pair(
469✔
9053
                            new LSymbol("lambda"),
469✔
9054
                            new Pair(
469✔
9055
                                code.car.cdr,
469✔
9056
                                code.cdr
469✔
9057
                            )
469✔
9058
                        )
469✔
9059
                    )
469✔
9060
                )
469✔
9061
            );
469✔
9062
            return new_code;
469✔
9063
        } else if (eval_args.macro_expand) {
1,273✔
9064
            // prevent evaluation in macroexpand
1✔
9065
            return;
1✔
9066
        }
1✔
9067
        eval_args.dynamic_env = this;
803✔
9068
        eval_args.env = env;
803✔
9069
        var value = code.cdr.car;
803✔
9070
        let new_expr;
803✔
9071
        if (is_pair(value)) {
1,273✔
9072
            value = evaluate(value, eval_args);
749✔
9073
            new_expr = true;
749✔
9074
        } else if (value instanceof LSymbol) {
1,273✔
9075
            value = env.get(value);
21✔
9076
        }
21✔
9077
        typecheck('define', code.car, 'symbol');
802✔
9078
        return unpromise(value, value => {
802✔
9079
            if (env.__name__ === Syntax.__merge_env__) {
802✔
9080
                env = env.__parent__;
28✔
9081
            }
28✔
9082
            if (new_expr &&
802✔
9083
                ((is_function(value) && is_lambda(value)) ||
748✔
9084
                 (value instanceof Syntax) || is_parameter(value))) {
802✔
9085
                value.__name__ = code.car.valueOf();
664✔
9086
                if (value.__name__ instanceof LString) {
664!
UNCOV
9087
                    value.__name__ = value.__name__.valueOf();
×
UNCOV
9088
                }
×
9089
            }
664✔
9090
            let __doc__;
802✔
9091
            if (is_pair(code.cdr.cdr) &&
802✔
9092
                LString.isString(code.cdr.cdr.car)) {
802✔
9093
                __doc__ = code.cdr.cdr.car.valueOf();
14✔
9094
            }
14✔
9095
            env.set(code.car, value, __doc__, true);
802✔
9096
        });
802✔
9097
    }), `(define name expression)
1✔
9098
         (define name expression "doc string")
1✔
9099
         (define (function-name . args) . body)
1✔
9100

1✔
9101
         Macro for defining values. It can be used to define variables,
1✔
9102
         or functions. If the first argument is list it will create a function
1✔
9103
         with name being first element of the list. This form expands to
1✔
9104
         \`(define function-name (lambda args body))\``),
1✔
9105
    // ------------------------------------------------------------------
1✔
9106
    'set-obj!': doc('set-obj!', function(obj, key, value, options = null) {
1✔
9107
        var obj_type = typeof obj;
1,370✔
9108
        if (is_null(obj) || (obj_type !== 'object' && obj_type !== 'function')) {
1,370!
UNCOV
9109
            var msg = typeErrorMessage('set-obj!', type(obj), ['object', 'function']);
×
UNCOV
9110
            throw new Error(msg);
×
UNCOV
9111
        }
×
9112
        typecheck('set-obj!', key, ['string', 'symbol', 'number']);
1,370✔
9113
        obj = unbind(obj);
1,370✔
9114
        key = key.valueOf();
1,370✔
9115
        if (arguments.length === 2) {
1,370!
UNCOV
9116
            delete obj[key];
×
9117
        } else if (is_prototype(obj) && is_function(value)) {
1,370✔
9118
            obj[key] = unbind(value);
24✔
9119
            obj[key][__prototype__] = true;
24✔
9120
        } else if (is_function(value) || is_native(value) || is_nil(value)) {
1,370✔
9121
            obj[key] = value;
1,158✔
9122
        } else {
1,346✔
9123
            obj[key] = value && !is_prototype(value) ? value.valueOf() : value;
188✔
9124
        }
188✔
9125
        if (props) {
1,365✔
9126
            const value = obj[key];
1,365✔
9127
            Object.defineProperty(obj, key, { ...options, value });
1,365✔
9128
        }
1,365✔
9129
    }, `(set-obj! obj key value)
1✔
9130
        (set-obj! obj key value props)
1✔
9131

1✔
9132
        Function set a property of a JavaScript object. props should be a vector of pairs,
1✔
9133
        passed to Object.defineProperty.`),
1✔
9134
    // ------------------------------------------------------------------
1✔
9135
    'null-environment': doc('null-environment', function() {
1✔
UNCOV
9136
        return global_env.inherit('null');
×
9137
    }, `(null-environment)
1✔
9138

1✔
9139
        Returns a clean environment with only the standard library.`),
1✔
9140
    // ------------------------------------------------------------------
1✔
9141
    'values': doc('values', function values(...args) {
1✔
9142
        return Values(args);
38✔
9143
    }, `(values a1 a2 ...)
1✔
9144

1✔
9145
        If called with more then one element it will create a special
1✔
9146
        Values object that can be used in the call-with-values function.`),
1✔
9147
    // ------------------------------------------------------------------
1✔
9148
    'call-with-values': doc('call-with-values', function(producer, consumer) {
1✔
9149
        typecheck('call-with-values', producer, 'function', 1);
16✔
9150
        typecheck('call-with-values', consumer, 'function', 2);
16✔
9151
        var maybe = producer.apply(this);
16✔
9152
        if (maybe instanceof Values) {
16✔
9153
            return consumer.apply(this, maybe.valueOf());
14✔
9154
        }
14✔
9155
        return consumer.call(this, maybe);
2✔
9156
    }, `(call-with-values producer consumer)
1✔
9157

1✔
9158
        Calls the producer procedure with no arguments, then calls the
1✔
9159
        consumer procedure with the returned value as an argument -- unless
1✔
9160
        the returned value is a special Values object created by (values), if it is
1✔
9161
        the values are unpacked and the consumer is called with multiple arguments.`),
1✔
9162
    // ------------------------------------------------------------------
1✔
9163
    'current-environment': doc('current-environment', function() {
1✔
9164
        if (this.__name__ === '__frame__') {
29✔
9165
            return this.__parent__;
29✔
9166
        }
29✔
UNCOV
9167
        return this;
×
9168
    }, `(current-environment)
1✔
9169

1✔
9170
        Function that returns the current environment (they're first-class objects!)`),
1✔
9171
    // ------------------------------------------------------------------
1✔
9172
    'parent.frame': doc('parent.frame', function() {
1✔
9173
        return user_env;
7✔
9174
    }, `(parent.frame)
1✔
9175

1✔
9176
        Returns the parent environment if called from inside a function.
1✔
9177
        If no parent frame can be found it returns nil.`),
1✔
9178
    // ------------------------------------------------------------------
1✔
9179
    'eval': doc('eval', function(code, env) {
1✔
9180
        env = env || this.get('interaction-environment').call(this);
7✔
9181
        return new Promise((resolve, reject) => {
7✔
9182
            const result = evaluate(code, {
7✔
9183
                env,
7✔
9184
                dynamic_env: env,
7✔
9185
                error: reject
7✔
9186
            });
7✔
9187
            resolve(result);
7✔
9188
        });
7✔
9189
    }, `(eval expr)
1✔
9190
        (eval expr environment)
1✔
9191

1✔
9192
        Function that evaluates LIPS Scheme code. If the second argument is provided
1✔
9193
        it will be the environment that the code is evaluated in.`),
1✔
9194
    // ------------------------------------------------------------------
1✔
9195
    lambda: new Macro('lambda', function(code, { use_dynamic, error } = {}) {
1✔
9196
        var self = this;
74,776✔
9197
        var __doc__;
74,776✔
9198
        if (is_pair(code.cdr) &&
74,776✔
9199
            LString.isString(code.cdr.car) &&
74,776✔
9200
            !is_nil(code.cdr.cdr)) {
74,776✔
9201
            __doc__ = code.cdr.car.valueOf();
364✔
9202
        }
364✔
9203
        function lambda(...args) {
74,776✔
9204
            // lambda got scopes as context in apply
700,088✔
9205
            let { dynamic_env } = is_context(this) ? this : { dynamic_env: self };
700,088✔
9206
            const env = self.inherit('lambda');
700,088✔
9207
            dynamic_env = dynamic_env.inherit('lambda');
700,088✔
9208
            if (this && !is_context(this)) {
700,088✔
9209
                if (this && !this.__instance__) {
3,060✔
9210
                    Object.defineProperty(this, '__instance__', {
1,033✔
9211
                        enumerable: false,
1,033✔
9212
                        get: () => true,
1,033✔
9213
                        set: () => {},
1,033✔
9214
                        configurable: false
1,033✔
9215
                    });
1,033✔
9216
                }
1,033✔
9217
                env.set('this', this);
3,060✔
9218
            }
3,060✔
9219
            // arguments and arguments.callee inside lambda function
700,088✔
9220
            if (this instanceof LambdaContext) {
700,088✔
9221
                const options = { throwError: false };
676,727✔
9222
                env.set('arguments', this.env.get('arguments', options));
676,727✔
9223
                env.set('parent.frame', this.env.get('parent.frame', options));
676,727✔
9224
            } else {
700,088✔
9225
                // this case is for lambda as callback function in JS; e.g. setTimeout
23,361✔
9226
                var _args = args.slice();
23,361✔
9227
                _args.callee = lambda;
23,361✔
9228
                _args.env = env;
23,361✔
9229
                env.set('arguments', _args);
23,361✔
9230
            }
23,361✔
9231
            function set(name, value) {
700,088✔
9232
                env.__env__[name.__name__] = value;
705,987✔
9233
                dynamic_env.__env__[name.__name__] = value;
705,987✔
9234
            }
705,987✔
9235
            let name = code.car;
700,088✔
9236
            let i = 0;
700,088✔
9237
            if (name instanceof LSymbol || !is_nil(name)) {
700,088✔
9238
                while (true) {
635,234✔
9239
                    if (!is_nil(name.car)) {
705,987✔
9240
                        if (name instanceof LSymbol) {
705,987✔
9241
                            // rest argument,  can also be first argument
42,886✔
9242
                            const value = quote(Pair.fromArray(args.slice(i), false));
42,886✔
9243
                            set(name, value);
42,886✔
9244
                            break;
42,886✔
9245
                        } else if (is_pair(name)) {
705,987✔
9246
                            const value = args[i];
663,101✔
9247
                            set(name.car, value);
663,101✔
9248
                        }
663,101✔
9249
                    }
705,987✔
9250
                    if (is_nil(name.cdr)) {
705,987✔
9251
                        break;
592,348✔
9252
                    }
592,348✔
9253
                    i++;
70,753✔
9254
                    name = name.cdr;
70,753✔
9255
                }
70,753✔
9256
            }
635,234✔
9257
            var rest = __doc__ ? code.cdr.cdr : code.cdr;
700,088✔
9258
            var output = hygienic_begin([env, dynamic_env], rest);
700,088✔
9259
            const eval_args = {
700,088✔
9260
                env,
700,088✔
9261
                dynamic_env,
700,088✔
9262
                use_dynamic,
700,088✔
9263
                error
700,088✔
9264
            }
700,088✔
9265
            return evaluate(output, eval_args);
700,088✔
9266
        }
700,088✔
9267
        var length = is_pair(code.car) ? code.car.length() : null;
74,776✔
9268
        lambda.__code__ = new Pair(new LSymbol('lambda'), code);
74,776✔
9269
        lambda[__lambda__] = true;
74,776✔
9270
        if (!is_pair(code.car)) {
74,776✔
9271
            return doc(lambda, __doc__, true); // variable arguments
2,205✔
9272
        }
2,205✔
9273
        // wrap and decorate with __doc__
72,571✔
9274
        return doc(set_fn_length(lambda, length), __doc__, true);
72,571✔
9275
    }, `(lambda (a b) body)
1✔
9276
        (lambda args body)
1✔
9277
        (lambda (a b . rest) body)
1✔
9278

1✔
9279
        The lambda macro creates a new anonymous function. If the first element of
1✔
9280
        the body is a string and there is more elements the string is used as the
1✔
9281
        documentation string, that can be read using (help fn).`),
1✔
9282
    // ------------------------------------------------------------------
1✔
9283
    'macroexpand': doc(
1✔
9284
        new Macro('macroexpand', macro_expand()),
1✔
9285
        `(macroexpand expr)
1✔
9286

1✔
9287
         Macro that expand all macros inside and return single expression as output.`),
1✔
9288
    // ------------------------------------------------------------------
1✔
9289
    'macroexpand-1': doc(
1✔
9290
        new Macro('macroexpand-1', macro_expand(true)),
1✔
9291
        `(macroexpand-1 expr)
1✔
9292

1✔
9293
         Macro similar to macroexpand but it expand macros only one level
1✔
9294
         and return single expression as output.`),
1✔
9295
    // ------------------------------------------------------------------
1✔
9296
    'define-macro': doc(new Macro(macro, function(macro, { use_dynamic, error }) {
1✔
9297
        let name, __doc__, body, args;
64✔
9298
        if (is_named_macro(macro)) {
64✔
9299
            name = macro.car.car.__name__;
62✔
9300
            body = macro.cdr;
62✔
9301
            args = macro.car.cdr;
62✔
9302
        } if (is_lambda_macro(macro)) {
64✔
9303
            name = macro.car.__name__;
2✔
9304
            const lambda = macro.cdr.car;
2✔
9305
            args = lambda.cdr.car;
2✔
9306
            body = lambda.cdr.cdr;
2✔
9307
        }
2✔
9308
        if (name && body && args) {
64✔
9309
            if (LString.isString(body.car) && is_pair(body.cdr)) {
64✔
9310
                __doc__ = body.car.valueOf();
41✔
9311
                body = body.cdr;
41✔
9312
            }
41✔
9313
            const macro_instance = define_macro(name, args, body, __doc__, {
64✔
9314
                use_dynamic, error
64✔
9315
            });
64✔
9316
            this.set(name, macro_instance);
64✔
9317
        } else {
64!
UNCOV
9318
            throw new Error('Syntax Error: Invalid `define-macro` expression');
×
UNCOV
9319
        }
×
9320
    }), `(define-macro (name . args) body)
1✔
9321

1✔
9322
         The meta-macro, that creates new macros. If the return value is a list structure
1✔
9323
         it will be evaluated where the macro is invoked from. You can use quasiquote \`
1✔
9324
         and unquote , and unquote-splicing ,@ inside to create an expression that will be
1✔
9325
         evaluated at runtime. Macros works like this: if you pass any expression to a
1✔
9326
         macro the arguments will not be evaluated unless the macro's body explicitly
1✔
9327
         calls (eval) on it. Because of this a macro can manipulate the expression
1✔
9328
         (arguments) as lists.`),
1✔
9329
    // ------------------------------------------------------------------
1✔
9330
    'syntax-rules': new Macro('syntax-rules', function(macro, options) {
1✔
9331
        var { use_dynamic, error } = options;
193✔
9332
        // TODO: find identifiers and freeze the scope when defined #172
193✔
9333
        var env = this;
193✔
9334
        function get_identifiers(node) {
193✔
9335
            let symbols = [];
525✔
9336
            while (!is_nil(node)) {
525✔
9337
                const x = node.car;
276✔
9338
                symbols.push(x.valueOf());
276✔
9339
                node = node.cdr;
276✔
9340
            }
276✔
9341
            return symbols;
525✔
9342
        }
525✔
9343
        function validate_identifiers(node) {
193✔
9344
            while (!is_nil(node)) {
193✔
9345
                const x = node.car;
88✔
9346
                if (!(x instanceof LSymbol)) {
88!
UNCOV
9347
                    throw new Error('syntax-rules: wrong identifier');
×
UNCOV
9348
                }
×
9349
                node = node.cdr;
88✔
9350
            }
88✔
9351
        }
193✔
9352
        if (macro.car instanceof LSymbol) {
193✔
9353
            validate_identifiers(macro.cdr.car);
12✔
9354
        } else {
193✔
9355
            validate_identifiers(macro.car);
181✔
9356
        }
181✔
9357
        const syntax = new Syntax(function(code, { macro_expand }) {
193✔
9358
            log('>> SYNTAX');
525✔
9359
            log(code);
525✔
9360
            log(macro);
525✔
9361
            const scope = env.inherit('syntax');
525✔
9362
            const dynamic_env = scope;
525✔
9363
            let var_scope = this;
525✔
9364
            // for macros that define variables used in macro (2 levels nestting)
525✔
9365
            if (var_scope.__name__ === Syntax.__merge_env__) {
525✔
9366
                // copy refs for defined gynsyms
138✔
9367
                const props = Object.getOwnPropertySymbols(var_scope.__env__);
138✔
9368
                props.forEach(symbol => {
138✔
9369
                    var_scope.__parent__.set(symbol, var_scope.__env__[symbol]);
183✔
9370
                });
138✔
9371
                var_scope = var_scope.__parent__;
138✔
9372
            }
138✔
9373
            var eval_args = { env: scope, dynamic_env, use_dynamic, error };
525✔
9374
            let ellipsis, rules, symbols;
525✔
9375
            if (macro.car instanceof LSymbol) {
525✔
9376
                ellipsis = macro.car;
22✔
9377
                symbols = get_identifiers(macro.cdr.car);
22✔
9378
                rules = macro.cdr.cdr;
22✔
9379
            } else {
525✔
9380
                ellipsis = '...';
503✔
9381
                symbols = get_identifiers(macro.car);
503✔
9382
                rules = macro.cdr;
503✔
9383
            }
503✔
9384
            try {
525✔
9385
                while (!is_nil(rules)) {
525✔
9386
                    var rule = rules.car.car;
1,027✔
9387
                    var expr = rules.car.cdr.car;
1,027✔
9388
                    log('[[[ RULE');
1,027✔
9389
                    log(rule);
1,027✔
9390
                    var bindings = extract_patterns(rule, code, symbols, ellipsis, {
1,027✔
9391
                        expansion: this, define: env
1,027✔
9392
                    });
1,027✔
9393
                    if (bindings) {
1,027✔
9394
                        /* c8 ignore next 5 */
1✔
9395
                        if (is_debug()) {
1✔
9396
                            console.log(JSON.stringify(symbolize(bindings), true, 2));
1✔
9397
                            console.log('PATTERN: ' + rule.toString(true));
1✔
9398
                            console.log('MACRO: ' + code.toString(true));
1✔
9399
                        }
1✔
9400
                        // name is modified in transform_syntax
498✔
9401
                        var names = [];
498✔
9402
                        const new_expr = transform_syntax({
498✔
9403
                            bindings,
498✔
9404
                            expr,
498✔
9405
                            symbols,
498✔
9406
                            scope,
498✔
9407
                            lex_scope: var_scope,
498✔
9408
                            names,
498✔
9409
                            ellipsis
498✔
9410
                        });
498✔
9411
                        log('OUPUT>>> ', new_expr);
498✔
9412
                        // TODO: if expression is undefined throw an error
498✔
9413
                        if (new_expr) {
498✔
9414
                            expr = new_expr;
492✔
9415
                        }
492✔
9416
                        var new_env = var_scope.merge(scope, Syntax.__merge_env__);
495✔
9417
                        if (macro_expand) {
498✔
9418
                            return { expr, scope: new_env };
1✔
9419
                        }
1✔
9420
                        var result = evaluate(expr, { ...eval_args, env: new_env });
494✔
9421
                        // Hack: update the result if there are generated
494✔
9422
                        //       gensyms that should be literal symbols
494✔
9423
                        // TODO: maybe not the part move when literal elisps may
494✔
9424
                        //       be generated, maybe they will need to be mark somehow
494✔
9425
                        return clear_gensyms(result, names);
494✔
9426
                    }
494✔
9427
                    rules = rules.cdr;
529✔
9428
                }
529✔
9429
            } catch (e) {
525✔
9430
                const location = `\nin macro:\n  ${macro.toString(true)}`;
9✔
9431
                if (!e.message.includes(location)) {
9✔
9432
                    e.message += location;
9✔
9433
                }
9✔
9434
                throw e;
9✔
9435
            }
9✔
9436
            throw new Error(`syntax-rules: no matching syntax in macro ${code.toString(true)}`);
27✔
9437
        }, env);
193✔
9438
        syntax.__code__ = macro;
193✔
9439
        return syntax;
193✔
9440
    }, `(syntax-rules () (pattern expression) ...)
1✔
9441

1✔
9442
        Base of hygienic macros, it will return a new syntax expander
1✔
9443
        that works like Lisp macros.`),
1✔
9444
    // ------------------------------------------------------------------
1✔
9445
    quote: doc(new Macro('quote', function(arg) {
1✔
9446
        return quote(arg.car);
135,255✔
9447
    }), `(quote expression) or 'expression
1✔
9448

1✔
9449
         Macro that returns a single LIPS expression as data (it won't evaluate the
1✔
9450
         argument). It will return a list if put in front of LIPS code.
1✔
9451
         And if put in front of a symbol it will return the symbol itself, not the value
1✔
9452
         bound to that name.`),
1✔
9453
    'unquote-splicing': doc('unquote-splicing', function() {
1✔
UNCOV
9454
        throw new Error(`You can't call \`unquote-splicing\` outside of quasiquote`);
×
9455
    }, `(unquote-splicing code) or ,@code
1✔
9456

1✔
9457
        Special form used in the quasiquote macro. It evaluates the expression inside and
1✔
9458
        splices the list into quasiquote's result. If it is not the last element of the
1✔
9459
        expression, the computed value must be a pair.`),
1✔
9460
    'unquote': doc('unquote', function() {
1✔
UNCOV
9461
        throw new Error(`You can't call \`unquote\` outside of quasiquote`);
×
9462
    }, `(unquote code) or ,code
1✔
9463

1✔
9464
        Special form used in the quasiquote macro. It evaluates the expression inside and
1✔
9465
        substitutes the value into quasiquote's result.`),
1✔
9466
    // ------------------------------------------------------------------
1✔
9467
    quasiquote: Macro.defmacro('quasiquote', function(arg, env) {
1✔
9468
        const { use_dynamic, error } = env;
333,154✔
9469
        const self = this;
333,154✔
9470
        //var max_unquote = 1;
333,154✔
9471
        const dynamic_env = self;
333,154✔
9472
        // -----------------------------------------------------------------
333,154✔
9473
        function is_struct(value) {
333,154✔
9474
            return is_pair(value) ||
4,653,824✔
9475
                is_plain_object(value) ||
4,653,824✔
9476
                Array.isArray(value);
4,653,824✔
9477
        }
4,653,824✔
9478
        // -----------------------------------------------------------------
333,154✔
9479
        function resolve_pair(pair, fn, test = is_struct) {
333,154✔
9480
            if (is_pair(pair)) {
2,326,912✔
9481
                var car = pair.car;
2,326,912✔
9482
                var cdr = pair.cdr;
2,326,912✔
9483
                if (test(car)) {
2,326,912✔
9484
                    car = fn(car);
1,440,093✔
9485
                }
1,440,093✔
9486
                if (test(cdr)) {
2,326,912✔
9487
                    cdr = fn(cdr);
1,542,159✔
9488
                }
1,542,159✔
9489
                if (is_promise(car) || is_promise(cdr)) {
2,326,912✔
9490
                    return promise_all([car, cdr]).then(([car, cdr]) => {
8✔
9491
                        return new Pair(car, cdr);
8✔
9492
                    });
8✔
9493
                } else {
2,326,912✔
9494
                    return new Pair(car, cdr);
2,326,904✔
9495
                }
2,326,904✔
9496
            }
2,326,912✔
UNCOV
9497
            return pair;
×
9498
        }
2,326,912✔
9499
        // -----------------------------------------------------------------
333,154✔
9500
        function join(eval_pair, value) {
333,154✔
9501
            if (is_nil(eval_pair) && is_nil(value)) {
202,946!
UNCOV
9502
                //return nil;
×
UNCOV
9503
            }
×
9504
            if (is_pair(eval_pair)) {
202,946✔
9505
                if (!is_nil(value)) {
202,946✔
9506
                    eval_pair.append(value);
45,601✔
9507
                }
45,601✔
9508
            } else {
202,946!
UNCOV
9509
                eval_pair = new Pair(
×
UNCOV
9510
                    eval_pair,
×
UNCOV
9511
                    value
×
UNCOV
9512
                );
×
UNCOV
9513
            }
×
9514
            return eval_pair;
202,946✔
9515
        }
202,946✔
9516
        // -----------------------------------------------------------------
333,154✔
9517
        function unquoted_arr(arr) {
333,154✔
9518
            return !!arr.filter(value => {
18✔
9519
                return is_pair(value) &&
39✔
9520
                    LSymbol.is(value.car, /^(unquote|unquote-splicing)$/);
39✔
9521
            }).length;
18✔
9522
        }
18✔
9523
        // -----------------------------------------------------------------
333,154✔
9524
        function quote_vector(arr, unquote_cnt, max_unq) {
333,154✔
9525
            return arr.reduce((acc, x) => {
2✔
9526
                if (!is_pair(x)) {
5✔
9527
                    acc.push(x);
1✔
9528
                    return acc;
1✔
9529
                }
1✔
9530
                if (LSymbol.is(x.car, 'unquote-splicing')) {
5✔
9531
                    let result;
1✔
9532
                    if (unquote_cnt + 1 < max_unq) {
1!
UNCOV
9533
                        result = recur(x.cdr, unquote_cnt + 1, max_unq);
×
9534
                    } else {
1✔
9535
                        result = evaluate(x.cdr.car, {
1✔
9536
                            env: self,
1✔
9537
                            use_dynamic,
1✔
9538
                            dynamic_env,
1✔
9539
                            error
1✔
9540
                        });
1✔
9541
                    }
1✔
9542
                    if (!is_pair(result)) {
1!
UNCOV
9543
                        throw new Error(`Expecting list ${type(x)} found`);
×
UNCOV
9544
                    }
×
9545
                    return acc.concat(result.to_array());
1✔
9546
                }
1✔
9547
                acc.push(recur(x, unquote_cnt, max_unq));
3✔
9548
                return acc;
3✔
9549
            }, []);
2✔
9550
        }
2✔
9551
        // -----------------------------------------------------------------
333,154✔
9552
        function quote_object(object, unquote_cnt, max_unq) {
333,154✔
9553
            const result = {};
12✔
9554
            unquote_cnt++;
12✔
9555
            Object.keys(object).forEach(key => {
12✔
9556
                const value = object[key];
24✔
9557
                if (is_pair(value)) {
24✔
9558
                    if (LSymbol.is(value.car, 'unquote-splicing')) {
13!
UNCOV
9559
                        throw new Error("You can't call `unquote-splicing` " +
×
UNCOV
9560
                                        "inside object");
×
UNCOV
9561
                    }
×
9562
                    let output;
13✔
9563
                    if (unquote_cnt < max_unq) {
13!
UNCOV
9564
                        output = recur(value.cdr.car, unquote_cnt, max_unq);
×
9565
                    } else {
13✔
9566
                        output = evaluate(value.cdr.car, {
13✔
9567
                            env: self,
13✔
9568
                            dynamic_env,
13✔
9569
                            use_dynamic,
13✔
9570
                            error
13✔
9571
                        });
13✔
9572
                    }
13✔
9573
                    result[key] = output;
13✔
9574
                } else {
24✔
9575
                    result[key] = value;
11✔
9576
                }
11✔
9577
            });
12✔
9578
            if (Object.isFrozen(object)) {
12!
UNCOV
9579
                Object.freeze(result);
×
UNCOV
9580
            }
×
9581
            return result;
12✔
9582
        }
12✔
9583
        // -----------------------------------------------------------------
333,154✔
9584
        function unquote_splice(pair, unquote_cnt, max_unq) {
333,154✔
9585
            if (unquote_cnt < max_unq) {
206,939✔
9586
                let cdr = nil;
9✔
9587
                if (!is_nil(pair.cdr)) {
9✔
9588
                    cdr = recur(pair.cdr, unquote_cnt - 1, max_unq);
3✔
9589
                }
3✔
9590
                return new Pair(
9✔
9591
                    new Pair(
9✔
9592
                        pair.car.car,
9✔
9593
                        recur(pair.car.cdr, unquote_cnt, max_unq)
9✔
9594
                    ),
9✔
9595
                    cdr
9✔
9596
                );
9✔
9597
            }
9✔
9598
            var lists = [];
206,930✔
9599
            return (function next(node) {
206,930✔
9600
                var value = evaluate(node.car, {
206,934✔
9601
                    env: self,
206,934✔
9602
                    dynamic_env,
206,934✔
9603
                    use_dynamic,
206,934✔
9604
                    error
206,934✔
9605
                });
206,934✔
9606
                lists.push(value);
206,934✔
9607
                if (is_pair(node.cdr)) {
206,934✔
9608
                    return next(node.cdr);
4✔
9609
                }
4✔
9610
                return unpromise(lists, function(arr) {
206,930✔
9611
                    if (arr.some(x => !is_pair(x))) {
206,930✔
9612
                        if (is_pair(pair.cdr) &&
3,988✔
9613
                            LSymbol.is(pair.cdr.car, '.') &&
3,988!
9614
                            is_pair(pair.cdr.cdr) &&
3,988!
9615
                            is_nil(pair.cdr.cdr.cdr)) {
3,988!
UNCOV
9616
                            return pair.cdr.cdr.car;
×
UNCOV
9617
                        }
×
9618
                        if (!(is_nil(pair.cdr) || is_pair(pair.cdr))) {
3,988!
UNCOV
9619
                            const msg = "You can't splice atom inside list";
×
UNCOV
9620
                            throw new Error(msg);
×
UNCOV
9621
                        }
×
9622
                        if (arr.length > 1) {
3,988!
9623
                            const msg = "You can't splice multiple atoms inside list";
×
9624
                            throw new Error(msg);
×
UNCOV
9625
                        }
×
9626
                        if (!(is_pair(pair.cdr) && is_nil(arr[0]))) {
3,988✔
9627
                            return arr[0];
3,985✔
9628
                        }
3,985✔
9629
                    }
3,988✔
9630
                    // don't create Cycles
202,945✔
9631
                    arr = arr.map(eval_pair => {
202,945✔
9632
                        if (splices.has(eval_pair)) {
202,949✔
9633
                            return eval_pair.clone();
8✔
9634
                        } else {
202,949✔
9635
                            splices.add(eval_pair);
202,941✔
9636
                            return eval_pair;
202,941✔
9637
                        }
202,941✔
9638
                    });
202,945✔
9639
                    const value = recur(pair.cdr, 0, 1);
202,945✔
9640
                    if (is_nil(value) && is_nil(arr[0])) {
206,930!
UNCOV
9641
                        return undefined;
×
UNCOV
9642
                    }
×
9643
                    return unpromise(value, value => {
202,945✔
9644
                        if (is_nil(arr[0])) {
202,945✔
9645
                            return value;
3✔
9646
                        }
3✔
9647
                        if (arr.length === 1) {
202,945✔
9648
                            return join(arr[0], value);
202,939✔
9649
                        }
202,939✔
9650
                        var result = arr.reduce((result, eval_pair) => {
3✔
9651
                            return join(result, eval_pair);
4✔
9652
                        });
3✔
9653
                        return join(result, value);
3✔
9654
                    });
202,945✔
9655
                });
206,930✔
9656
            })(pair.car.cdr);
206,930✔
9657
        }
206,939✔
9658
        // -----------------------------------------------------------------
333,154✔
9659
        var splices = new Set();
333,154✔
9660
        function recur(pair, unquote_cnt, max_unq) {
333,154✔
9661
            if (is_pair(pair)) {
3,525,064✔
9662
                if (is_pair(pair.car)) {
3,367,705✔
9663
                    if (LSymbol.is(pair.car.car, 'unquote-splicing')) {
1,647,034✔
9664
                        return unquote_splice(pair, unquote_cnt + 1, max_unq);
206,934✔
9665
                    }
206,934✔
9666
                    if (LSymbol.is(pair.car.car, 'unquote')) {
1,647,034✔
9667
                        // + 2 - one for unquote and one for unquote splicing
781,595✔
9668
                        if (unquote_cnt + 2 === max_unq &&
781,595✔
9669
                            is_pair(pair.car.cdr) &&
781,595✔
9670
                            is_pair(pair.car.cdr.car) &&
781,595✔
9671
                            LSymbol.is(pair.car.cdr.car.car, 'unquote-splicing')) {
781,595✔
9672
                            const rest = pair.car.cdr;
5✔
9673
                            return new Pair(
5✔
9674
                                new Pair(
5✔
9675
                                    new LSymbol('unquote'),
5✔
9676
                                    unquote_splice(rest, unquote_cnt + 2, max_unq)
5✔
9677
                                ),
5✔
9678
                                nil
5✔
9679
                            );
5✔
9680
                        } else if (is_pair(pair.car.cdr) &&
781,595✔
9681
                                   !is_nil(pair.car.cdr.cdr)) {
781,590✔
9682
                            if (is_pair(pair.car.cdr.car)) {
4✔
9683
                                // values inside unquote are lists
1✔
9684
                                const result = [];
1✔
9685
                                return (function recur(node) {
1✔
9686
                                    if (is_nil(node)) {
4✔
9687
                                        return Pair.fromArray(result);
1✔
9688
                                    }
1✔
9689
                                    return unpromise(evaluate(node.car, {
3✔
9690
                                        env: self,
3✔
9691
                                        dynamic_env,
3✔
9692
                                        use_dynamic,
3✔
9693
                                        error
3✔
9694
                                    }), function(next) {
3✔
9695
                                        result.push(next);
3✔
9696
                                        return recur(node.cdr);
3✔
9697
                                    });
3✔
9698
                                })(pair.car.cdr);
1✔
9699
                            } else {
4✔
9700
                                // same as in guile if (unquote 1 2 3) it should be
3✔
9701
                                // spliced - scheme spec say it's unspecify but it
3✔
9702
                                // work like in CL
3✔
9703
                                return pair.car.cdr;
3✔
9704
                            }
3✔
9705
                        }
4✔
9706
                    }
781,595✔
9707
                }
1,647,034✔
9708
                if (LSymbol.is(pair.car, 'quasiquote')) {
3,367,705✔
9709
                    var cdr = recur(pair.cdr, unquote_cnt, max_unq + 1);
23✔
9710
                    return new Pair(pair.car, cdr);
23✔
9711
                }
23✔
9712
                if (LSymbol.is(pair.car, 'quote')) {
3,367,705✔
9713
                    return new Pair(
6,673✔
9714
                        pair.car,
6,673✔
9715
                        recur(pair.cdr, unquote_cnt, max_unq)
6,673✔
9716
                    );
6,673✔
9717
                }
6,673✔
9718
                if (LSymbol.is(pair.car, 'unquote')) {
3,367,705✔
9719
                    unquote_cnt++;
827,154✔
9720
                    if (unquote_cnt < max_unq) {
827,154✔
9721
                        return new Pair(
14✔
9722
                            new LSymbol('unquote'),
14✔
9723
                            recur(pair.cdr, unquote_cnt, max_unq)
14✔
9724
                        );
14✔
9725
                    }
14✔
9726
                    if (unquote_cnt > max_unq) {
827,154!
UNCOV
9727
                        throw new Error("You can't call `unquote` outside " +
×
UNCOV
9728
                                        "of quasiquote");
×
UNCOV
9729
                    }
×
9730
                    if (is_pair(pair.cdr)) {
827,140✔
9731
                        if (!is_nil(pair.cdr.cdr)) {
827,140!
UNCOV
9732
                            if (is_pair(pair.cdr.car)) {
×
UNCOV
9733
                                // TODO: test if this part is needed
×
UNCOV
9734
                                // this part was duplicated in previous section
×
UNCOV
9735
                                // if (LSymbol.is(pair.car.car, 'unquote')) {
×
UNCOV
9736
                                // so this probably can be removed
×
UNCOV
9737
                                const result = [];
×
UNCOV
9738
                                // evaluate all values in unquote
×
UNCOV
9739
                                return (function recur(node) {
×
UNCOV
9740
                                    if (is_nil(node)) {
×
UNCOV
9741
                                        return Pair.fromArray(result);
×
UNCOV
9742
                                    }
×
UNCOV
9743
                                    return unpromise(evaluate(node.car, {
×
UNCOV
9744
                                        env: self,
×
UNCOV
9745
                                        dynamic_env,
×
UNCOV
9746
                                        use_dynamic,
×
UNCOV
9747
                                        error
×
UNCOV
9748
                                    }), function(next) {
×
UNCOV
9749
                                        result.push(next);
×
UNCOV
9750
                                        return recur(node.cdr);
×
UNCOV
9751
                                    });
×
UNCOV
9752
                                })(pair.cdr);
×
UNCOV
9753
                            } else {
×
UNCOV
9754
                                return pair.cdr;
×
UNCOV
9755
                            }
×
9756
                        } else {
827,140✔
9757
                            return evaluate(pair.cdr.car, {
827,140✔
9758
                                env: self,
827,140✔
9759
                                dynamic_env,
827,140✔
9760
                                error
827,140✔
9761
                            });
827,140✔
9762
                        }
827,140✔
9763
                    } else {
827,154!
UNCOV
9764
                        return pair.cdr;
×
UNCOV
9765
                    }
×
9766
                }
827,154✔
9767
                return resolve_pair(pair, (pair) => {
2,326,912✔
9768
                    return recur(pair, unquote_cnt, max_unq);
2,982,252✔
9769
                });
2,326,912✔
9770
            } else if (is_plain_object(pair)) {
3,525,064✔
9771
                return quote_object(pair, unquote_cnt, max_unq);
12✔
9772
            } else if (pair instanceof Array) {
157,359✔
9773
                return quote_vector(pair, unquote_cnt, max_unq);
2✔
9774
            }
2✔
9775
            return pair;
157,345✔
9776
        }
3,525,064✔
9777
        // -----------------------------------------------------------------
333,154✔
9778
        function clear(node) {
333,154✔
9779
            if (is_pair(node)) {
35,262,395✔
9780
                delete node[__data__];
17,464,627✔
9781
                if (!node.have_cycles('car')) {
17,464,627✔
9782
                    clear(node.car);
17,464,627✔
9783
                }
17,464,627✔
9784
                if (!node.have_cycles('cdr')) {
17,464,627✔
9785
                    clear(node.cdr);
17,464,627✔
9786
                }
17,464,627✔
9787
            }
17,464,627✔
9788
        }
35,262,395✔
9789
        // -----------------------------------------------------------------
333,154✔
9790
        if (is_plain_object(arg.car) && !unquoted_arr(Object.values(arg.car))) {
333,154✔
9791
            return quote(arg.car);
2✔
9792
        }
2✔
9793
        if (Array.isArray(arg.car) && !unquoted_arr(arg.car)) {
333,154✔
9794
            return quote(arg.car);
4✔
9795
        }
4✔
9796
        if (is_pair(arg.car) &&
333,154✔
9797
            !arg.car.find('unquote') &&
333,154✔
9798
            !arg.car.find('unquote-splicing') &&
333,154✔
9799
            !arg.car.find('quasiquote')) {
333,154✔
9800
            return quote(arg.car);
6✔
9801
        }
6✔
9802
        var x = recur(arg.car, 0, 1);
333,142✔
9803
        return unpromise(x, value => {
333,142✔
9804
            // clear nested data for tests
333,141✔
9805
            clear(value);
333,141✔
9806
            return quote(value);
333,141✔
9807
        });
333,142✔
9808
    }, `(quasiquote list)
1✔
9809

1✔
9810
        Similar macro to \`quote\` but inside it you can use special expressions (unquote
1✔
9811
        x) abbreviated to ,x that will evaluate x and insert its value verbatim or
1✔
9812
        (unquote-splicing x) abbreviated to ,@x that will evaluate x and splice the value
1✔
9813
        into the result. Best used with macros but it can be used outside.`),
1✔
9814
    // ------------------------------------------------------------------
1✔
9815
    clone: doc('clone', function clone(list) {
1✔
9816
        typecheck('clone', list, 'pair');
1✔
9817
        return list.clone();
1✔
9818
    }, `(clone list)
1✔
9819

1✔
9820
        Function that returns a clone of the list, that does not share any pairs with the
1✔
9821
        original, so the clone can be safely mutated without affecting the original.`),
1✔
9822
    // ------------------------------------------------------------------
1✔
9823
    append: doc('append', function append(...items) {
1✔
9824
        items = items.map(item => {
69✔
9825
            if (is_pair(item)) {
151✔
9826
                return item.clone();
112✔
9827
            }
112✔
9828
            return item;
39✔
9829
        });
69✔
9830
        return global_env.get('append!').call(this, ...items);
69✔
9831
    }, `(append item ...)
1✔
9832

1✔
9833
        Function that creates a new list with each argument appended end-to-end.
1✔
9834
        It will always return a new list and not modify its arguments.`),
1✔
9835
    // ------------------------------------------------------------------
1✔
9836
    'append!': doc('append!', function(...items) {
1✔
9837
        var is_list = global_env.get('list?');
71✔
9838
        return items.reduce((acc, item) => {
71✔
9839
            typecheck('append!', acc, ['nil', 'pair']);
158✔
9840
            if ((is_pair(item) || is_nil(item)) && !is_list(item)) {
158✔
9841
                throw new Error('append!: Invalid argument, value is not a list');
1✔
9842
            }
1✔
9843
            if (is_nil(acc)) {
158✔
9844
                if (is_nil(item)) {
82✔
9845
                    return nil;
16✔
9846
                }
16✔
9847
                return item;
66✔
9848
            }
66✔
9849
            if (is_null(item)) {
158✔
9850
                return acc;
18✔
9851
            }
18✔
9852
            return acc.append(item);
57✔
9853
        }, nil);
71✔
9854
    }, `(append! arg1 ...)
1✔
9855

1✔
9856
        Destructive version of append, it can modify the lists in place. It returns
1✔
9857
        a new list where each argument is appended to the end. It may modify
1✔
9858
        lists added as arguments.`),
1✔
9859
    // ------------------------------------------------------------------
1✔
9860
    reverse: doc('reverse', function reverse(arg) {
1✔
9861
        typecheck('reverse', arg, ['array', 'pair', 'nil']);
87✔
9862
        if (is_nil(arg)) {
87!
UNCOV
9863
            return nil;
×
UNCOV
9864
        }
×
9865
        if (is_pair(arg)) {
87✔
9866
            var arr = global_env.get('list->array')(arg).reverse();
87✔
9867
            return global_env.get('array->list')(arr);
87✔
9868
        } else if (Array.isArray(arg)) {
87!
UNCOV
9869
            return arg.reverse();
×
UNCOV
9870
        } else {
×
UNCOV
9871
            throw new Error(typeErrorMessage('reverse', type(arg), 'array or pair'));
×
UNCOV
9872
        }
×
9873
    }, `(reverse list)
1✔
9874

1✔
9875
        Function that reverses the list or array. If value is not a list
1✔
9876
        or array it will error.`),
1✔
9877
    // ------------------------------------------------------------------
1✔
9878
    nth: doc('nth', function nth(index, obj) {
1✔
9879
        typecheck('nth', index, 'number');
4✔
9880
        typecheck('nth', obj, ['array', 'pair']);
4✔
9881
        if (is_pair(obj)) {
4✔
9882
            var node = obj;
4✔
9883
            var count = 0;
4✔
9884
            while (count < index) {
4✔
9885
                if (!node.cdr || is_nil(node.cdr) || node.have_cycles('cdr')) {
6!
UNCOV
9886
                    return nil;
×
UNCOV
9887
                }
×
9888
                node = node.cdr;
6✔
9889
                count++;
6✔
9890
            }
6✔
9891
            return node.car;
4✔
9892
        } else if (obj instanceof Array) {
4!
UNCOV
9893
            return obj[index];
×
UNCOV
9894
        } else {
×
UNCOV
9895
            throw new Error(typeErrorMessage('nth', type(obj), 'array or pair', 2));
×
UNCOV
9896
        }
×
9897
    }, `(nth index obj)
1✔
9898

1✔
9899
        Function that returns the nth element of the list or array.
1✔
9900
        If used with a non-indexable value it will error.`),
1✔
9901
    // ------------------------------------------------------------------
1✔
9902
    list: doc('list', function list(...args) {
1✔
9903
        return args.reverse().reduce((list, item) => new Pair(item, list), nil);
298✔
9904
    }, `(list . args)
1✔
9905

1✔
9906
        Function that creates a new list out of its arguments.`),
1✔
9907
    // ------------------------------------------------------------------
1✔
9908
    substring: doc('substring', function substring(string, start, end) {
1✔
9909
        typecheck('substring', string, 'string');
395✔
9910
        typecheck('substring', start, 'number');
395✔
9911
        typecheck('substring', end, ['number', 'void']);
395✔
9912
        return string.substring(start.valueOf(), end && end.valueOf());
395✔
9913
    }, `(substring string start end)
1✔
9914

1✔
9915
        Function that returns the slice of the string starting at start and ending
1✔
9916
        with end.`),
1✔
9917
    // ------------------------------------------------------------------
1✔
9918
    concat: doc('concat', function concat(...args) {
1✔
9919
        args.forEach((arg, i) => typecheck('concat', arg, 'string', i + 1));
227✔
9920
        return args.join('');
227✔
9921
    }, `(concat . strings)
1✔
9922

1✔
9923
        Function that creates a new string by joining its arguments.`),
1✔
9924
    // ------------------------------------------------------------------
1✔
9925
    join: doc('join', function join(separator, list) {
1✔
9926
        typecheck('join', separator, 'string');
2✔
9927
        typecheck('join', list, ['pair', 'nil']);
2✔
9928
        return global_env.get('list->array')(list).join(separator);
2✔
9929
    }, `(join separator list)
1✔
9930

1✔
9931
        Function that returns a string by joining elements of the list using separator.`),
1✔
9932
    // ------------------------------------------------------------------
1✔
9933
    split: doc('split', function split(separator, string) {
1✔
9934
        typecheck('split', separator, ['regex', 'string']);
45,535✔
9935
        typecheck('split', string, 'string');
45,535✔
9936
        return global_env.get('array->list')(string.split(separator));
45,535✔
9937
    }, `(split separator string)
1✔
9938

1✔
9939
        Function that creates a list by splitting string by separator which can
1✔
9940
        be a string or regular expression.`),
1✔
9941
    // ------------------------------------------------------------------
1✔
9942
    replace: doc('replace', function replace(pattern, replacement, string) {
1✔
9943
        typecheck('replace', pattern, ['regex', 'string']);
3✔
9944
        typecheck('replace', replacement, ['string', 'function']);
3✔
9945
        typecheck('replace', string, 'string');
3✔
9946
        if (is_function(replacement)) {
3✔
9947
            // ref: https://stackoverflow.com/a/48032528/387194
1✔
9948
            const replacements = [];
1✔
9949
            string.replace(pattern, function(...args) {
1✔
9950
                replacements.push(replacement(...args));
1✔
9951
            });
1✔
9952
            return unpromise(replacements, replacements => {
1✔
9953
                return string.replace(pattern, () => replacements.shift());
1✔
9954
            });
1✔
9955
        }
1✔
9956
        return string.replace(pattern, replacement);
2✔
9957
    }, `(replace pattern replacement string)
1✔
9958

1✔
9959
        Function that changes pattern to replacement inside string. Pattern can be a
1✔
9960
        string or regex and replacement can be function or string. See Javascript
1✔
9961
        String.replace().`),
1✔
9962
    // ------------------------------------------------------------------
1✔
9963
    match: doc('match', function match(pattern, string) {
1✔
9964
        typecheck('match', pattern, ['regex', 'string']);
1✔
9965
        typecheck('match', string, 'string');
1✔
9966
        var m = string.match(pattern);
1✔
9967
        return m ? global_env.get('array->list')(m) : false;
1!
9968
    }, `(match pattern string)
1✔
9969

1✔
9970
        Function that returns a match object from JavaScript as a list or #f if
1✔
9971
        no match.`),
1✔
9972
    // ------------------------------------------------------------------
1✔
9973
    search: doc('search', function search(pattern, string) {
1✔
9974
        typecheck('search', pattern, ['regex', 'string']);
4✔
9975
        typecheck('search', string, 'string');
4✔
9976
        return string.search(pattern);
4✔
9977
    }, `(search pattern string)
1✔
9978

1✔
9979
        Function that returns the first found index of the pattern inside a string.`),
1✔
9980
    // ------------------------------------------------------------------
1✔
9981
    repr: doc('repr', function repr(obj, quote) {
1✔
9982
        return to_string(obj, quote);
91,573✔
9983
    }, `(repr obj)
1✔
9984

1✔
9985
        Function that returns a LIPS code representation of the object as a string.`),
1✔
9986
    // ------------------------------------------------------------------
1✔
9987
    'escape-regex': doc('escape-regex', function(string) {
1✔
9988
        typecheck('escape-regex', string, 'string');
1✔
9989
        return escape_regex(string.valueOf());
1✔
9990
    }, `(escape-regex string)
1✔
9991

1✔
9992
        Function that returns a new string where all special operators used in regex,
1✔
9993
        are escaped with backslashes so they can be used in the RegExp constructor
1✔
9994
        to match a literal string.`),
1✔
9995
    // ------------------------------------------------------------------
1✔
9996
    env: doc('env', function env(env) {
1✔
9997
        env = env || this.env;
24✔
9998
        const names = Object.keys(env.__env__).map(LSymbol);
24✔
9999
        let result;
24✔
10000
        if (names.length) {
24✔
10001
            result = Pair.fromArray(names);
19✔
10002
        } else {
24✔
10003
            result = nil;
5✔
10004
        }
5✔
10005
        if (env.__parent__ instanceof Environment) {
24✔
10006
            return global_env.get('env').call(this, env.__parent__).append(result);
21✔
10007
        }
21✔
10008
        return result;
3✔
10009
    }, `(env)
1✔
10010
        (env obj)
1✔
10011

1✔
10012
        Function that returns a list of names (functions, macros and variables)
1✔
10013
        that are bound in the current environment or one of its parents.`),
1✔
10014
    // ------------------------------------------------------------------
1✔
10015
    'new': doc('new', function(obj, ...args) {
1✔
10016
        const cls = unbind(obj);
1,494✔
10017
        let instance;
1,494✔
10018
        if (cls[Symbol.for("__class__")]) {
1,494✔
10019
            instance = new cls(...args);
1,007✔
10020
        } else {
1,494✔
10021
            instance = new cls(...args.map(x => unbox(x)));
487✔
10022
        }
487✔
10023
        return instance;
1,494✔
10024
    }, `(new obj . args)
1✔
10025

1✔
10026
        Function that creates new JavaScript instance of an object.`),
1✔
10027
    // ------------------------------------------------------------------
1✔
10028
    'typecheck': doc(
1✔
10029
        typecheck,
1✔
10030
        `(typecheck label value type [position])
1✔
10031

1✔
10032
         Checks the type of value and errors if the type is not one allowed.  Type can be
1✔
10033
         string or list of strings. The position optional argument is used to create a
1✔
10034
         proper error message for the nth argument of function calls.`),
1✔
10035
    // ------------------------------------------------------------------
1✔
10036
    'typecheck-number': doc(
1✔
10037
        typecheck_number,
1✔
10038
        `(typecheck-number label value type [position])
1✔
10039

1✔
10040
         Function similar to typecheck but checks if the argument is a number
1✔
10041
         and specific type of number e.g. complex.`),
1✔
10042
    // ------------------------------------------------------------------
1✔
10043
    'unset-special!': doc('unset-special!', function(symbol) {
1✔
10044
        typecheck('remove-special!', symbol, 'string');
7✔
10045
        delete specials.remove(symbol.valueOf());
7✔
10046
    }, `(unset-special! name)
1✔
10047

1✔
10048
        Function that removes a special symbol from parser added by \`set-special!\`,
1✔
10049
        name must be a string.`),
1✔
10050
    // ------------------------------------------------------------------
1✔
10051
    'set-special!': doc('set-special!', function(seq, name, type = specials.LITERAL) {
1✔
10052
        typecheck('set-special!', seq, 'string', 1);
23✔
10053
        typecheck('set-special!', name, 'symbol', 2);
23✔
10054
        specials.append(seq.valueOf(), name, type);
23✔
10055
    }, `(set-special! symbol name [type])
1✔
10056

1✔
10057
        Add a special symbol to the list of transforming operators by the parser.
1✔
10058
        e.g.: \`(add-special! "#" 'x)\` will allow to use \`#(1 2 3)\` and it will be
1✔
10059
        transformed into (x (1 2 3)) so you can write x macro that will process
1✔
10060
        the list. 3rd argument is optional, and it can be one of two values:
1✔
10061
        lips.specials.LITERAL, which is the default behavior, or
1✔
10062
        lips.specials.SPLICE which causes the value to be unpacked into the expression.
1✔
10063
        This can be used for e.g. to make \`#(1 2 3)\` into (x 1 2 3) that is needed
1✔
10064
        by # that defines vectors.`),
1✔
10065
    // ------------------------------------------------------------------
1✔
10066
    'get': get,
1✔
10067
    '.': get,
1✔
10068
    // ------------------------------------------------------------------
1✔
10069
    'unbind': doc(
1✔
10070
        unbind,
1✔
10071
        `(unbind fn)
1✔
10072

1✔
10073
         Function that removes the weak 'this' binding from a function so you
1✔
10074
         can get properties from the actual function object.`),
1✔
10075
    // ------------------------------------------------------------------
1✔
10076
    type: doc(
1✔
10077
        type,
1✔
10078
        `(type object)
1✔
10079

1✔
10080
         Function that returns the type of an object as string.`),
1✔
10081
    // ------------------------------------------------------------------
1✔
10082
    'debugger': doc('debugger', function() {
1✔
UNCOV
10083
        /* eslint-disable */
×
UNCOV
10084
        debugger;
×
UNCOV
10085
        /* eslint-enable */
×
10086
    }, `(debugger)
1✔
10087

1✔
10088
        Function that triggers the JavaScript debugger (e.g. the browser devtools)
1✔
10089
        using the "debugger;" statement. If a debugger is not running this
1✔
10090
        function does nothing.`),
1✔
10091
    // ------------------------------------------------------------------
1✔
10092
    'in': doc('in', function(a, b) {
1✔
10093
        if (a instanceof LSymbol ||
8✔
10094
            a instanceof LString ||
8✔
10095
            a instanceof LNumber) {
8✔
10096
            a = a.valueOf();
8✔
10097
        }
8✔
10098
        return a in unbox(b);
8✔
10099
    }, `(in key value)
1✔
10100

1✔
10101
        Function that uses the Javascript "in" operator to check if key is
1✔
10102
        a valid property in the value.`),
1✔
10103
    // ------------------------------------------------------------------
1✔
10104
    'instance?': doc('instance?', function(obj) {
1✔
10105
        return is_instance(obj);
2,243✔
10106
    }, `(instance? obj)
1✔
10107

1✔
10108
        Checks if object is an instance, created with a new operator`),
1✔
10109
    // ------------------------------------------------------------------
1✔
10110
    'instanceof': doc('instanceof', function(type, obj) {
1✔
10111
        return obj instanceof unbind(type);
4,067✔
10112
    }, `(instanceof type obj)
1✔
10113

1✔
10114
        Predicate that tests if the obj is an instance of type.`),
1✔
10115
    // ------------------------------------------------------------------
1✔
10116
    'prototype?': doc(
1✔
10117
        'prototype?',
1✔
10118
        is_prototype,
1✔
10119
        `(prototype? obj)
1✔
10120

1✔
10121
         Predicate that tests if value is a valid JavaScript prototype,
1✔
10122
         i.e. calling (new) with it will not throw '<x> is not a constructor'.`),
1✔
10123
    // ------------------------------------------------------------------
1✔
10124
    'macro?': doc('macro?', function(obj) {
1✔
UNCOV
10125
        return obj instanceof Macro;
×
10126
    }, `(macro? expression)
1✔
10127

1✔
10128
        Predicate that tests if value is a macro.`),
1✔
10129
    // ------------------------------------------------------------------
1✔
10130
    'continuation?': doc(
1✔
10131
        'continuation?',
1✔
10132
        is_continuation,
1✔
10133
        `(continuation? expression)
1✔
10134

1✔
10135
         Predicate that tests if value is a callable continuation.`),
1✔
10136
    // ------------------------------------------------------------------
1✔
10137
    'function?': doc(
1✔
10138
        'function?',
1✔
10139
        is_function,
1✔
10140
        `(function? expression)
1✔
10141

1✔
10142
         Predicate that tests if value is a callable function.`),
1✔
10143
    // ------------------------------------------------------------------
1✔
10144
    'real?': doc('real?', function(value) {
1✔
UNCOV
10145
        if (type(value) !== 'number') {
×
UNCOV
10146
            return false;
×
UNCOV
10147
        }
×
UNCOV
10148
        if (value instanceof LNumber) {
×
UNCOV
10149
            return value.isFloat();
×
UNCOV
10150
        }
×
UNCOV
10151
        return LNumber.isFloat(value);
×
10152
    }, `(real? number)
1✔
10153

1✔
10154
        Predicate that tests if value is a real number (not complex).`),
1✔
10155
    // ------------------------------------------------------------------
1✔
10156
    'number?': doc('number?', function(x) {
1✔
10157
        return Number.isNaN(x) || LNumber.isNumber(x);
5,724✔
10158
    }, `(number? expression)
1✔
10159

1✔
10160
        Predicate that tests if value is a number or NaN value.`),
1✔
10161
    // ------------------------------------------------------------------
1✔
10162
    'string?': doc('string?', function(obj) {
1✔
10163
        return LString.isString(obj);
94,820✔
10164
    }, `(string? expression)
1✔
10165

1✔
10166
        Predicate that tests if value is a string.`),
1✔
10167
    // ------------------------------------------------------------------
1✔
10168
    'pair?': doc(
1✔
10169
        'pair?',
1✔
10170
        is_pair,
1✔
10171
        `(pair? expression)
1✔
10172

1✔
10173
         Predicate that tests if value is a pair or list structure.`),
1✔
10174
    // ------------------------------------------------------------------
1✔
10175
    'regex?': doc('regex?', function(obj) {
1✔
UNCOV
10176
        return obj instanceof RegExp;
×
10177
    }, `(regex? expression)
1✔
10178

1✔
10179
        Predicate that tests if value is a regular expression.`),
1✔
10180
    // ------------------------------------------------------------------
1✔
10181
    'null?': doc('null?', function(obj) {
1✔
10182
        return is_null(obj);
75,167✔
10183
    }, `(null? expression)
1✔
10184

1✔
10185
        Predicate that tests if value is null-ish (i.e. undefined, nil, or
1✔
10186
        Javascript null).`),
1✔
10187
    // ------------------------------------------------------------------
1✔
10188
    'boolean?': doc('boolean?', function(obj) {
1✔
10189
        return typeof obj === 'boolean';
×
10190
    }, `(boolean? expression)
1✔
10191

1✔
10192
        Predicate that tests if value is a boolean (#t or #f).`),
1✔
10193
    // ------------------------------------------------------------------
1✔
10194
    'symbol?': doc('symbol?', function(obj) {
1✔
10195
        return obj instanceof LSymbol;
86,044✔
10196
    }, `(symbol? expression)
1✔
10197

1✔
10198
        Predicate that tests if value is a LIPS symbol.`),
1✔
10199
    // ------------------------------------------------------------------
1✔
10200
    'array?': doc('array?', function(obj) {
1✔
10201
        return obj instanceof Array;
2,251✔
10202
    }, `(array? expression)
1✔
10203

1✔
10204
        Predicate that tests if value is an array.`),
1✔
10205
    // ------------------------------------------------------------------
1✔
10206
    'object?': doc('object?', function(obj) {
1✔
10207
        return !is_nil(obj) && obj !== null &&
113✔
10208
            !(obj instanceof LCharacter) &&
113✔
10209
            !(obj instanceof RegExp) &&
113✔
10210
            !(obj instanceof LString) &&
113✔
10211
            !is_pair(obj) &&
113✔
10212
            !(obj instanceof LNumber) &&
113✔
10213
            typeof obj === 'object' &&
113✔
10214
            !(obj instanceof Array);
113✔
10215
    }, `(object? expression)
1✔
10216

1✔
10217
        Predicate that tests if value is an plain object (not another LIPS type).`),
1✔
10218
    // ------------------------------------------------------------------
1✔
10219
    flatten: doc('flatten', function flatten(list) {
1✔
10220
        typecheck('flatten', list, 'pair');
×
UNCOV
10221
        return list.flatten();
×
10222
    }, `(flatten list)
1✔
10223

1✔
10224
        Returns a shallow list from tree structure (pairs).`),
1✔
10225
    // ------------------------------------------------------------------
1✔
10226
    'vector-append': doc('vector-append', function(...args) {
1✔
10227
        if (!args.length) {
24!
UNCOV
10228
            return [];
×
UNCOV
10229
        }
×
10230
        typecheck_args('vector-append', args, 'array');
24✔
10231
        const [first, ...rest] = args;
24✔
10232
        return first.concat(...rest);
24✔
10233
    }, `(vector-append v1 v2 ...)
1✔
10234

1✔
10235
         Returns new vector by combining it's arguments that should be vectors.`),
1✔
10236
    // ------------------------------------------------------------------
1✔
10237
    'array->list': doc('array->list', function(array) {
1✔
10238
        typecheck('array->list', array, 'array');
45,653✔
10239
        return Pair.fromArray(array);
45,653✔
10240
    }, `(array->list array)
1✔
10241

1✔
10242
        Function that converts a JavaScript array to a LIPS cons list.`),
1✔
10243
    // ------------------------------------------------------------------
1✔
10244
    'tree->array': doc(
1✔
10245
        'tree->array',
1✔
10246
        to_array('tree->array', true),
1✔
10247
        `(tree->array list)
1✔
10248

1✔
10249
         Function that converts a LIPS cons tree structure into a JavaScript array.`),
1✔
10250
    // ------------------------------------------------------------------
1✔
10251
    'list->array': doc(
1✔
10252
        'list->array',
1✔
10253
        to_array('list->array'),
1✔
10254
        `(list->array list)
1✔
10255

1✔
10256
         Function that converts a LIPS list into a JavaScript array.`),
1✔
10257
    // ------------------------------------------------------------------
1✔
10258
    apply: doc('apply', function apply(fn, ...args) {
1✔
10259
        typecheck('apply', fn, 'function', 1);
4,104✔
10260
        var last = args.pop();
4,104✔
10261
        typecheck('apply', last, ['pair', 'nil'], args.length + 2);
4,104✔
10262
        args = args.concat(global_env.get('list->array').call(this, last));
4,104✔
10263
        return fn.apply(this, prepare_fn_args(fn, args));
4,104✔
10264
    }, `(apply fn list)
1✔
10265

1✔
10266
        Function that calls fn with the list of arguments.`),
1✔
10267
    // ------------------------------------------------------------------
1✔
10268
    length: doc('length', function length(obj) {
1✔
10269
        if (!obj || is_nil(obj)) {
17,768!
UNCOV
10270
            return 0;
×
UNCOV
10271
        }
×
10272
        if (is_pair(obj)) {
17,768✔
10273
            return obj.length();
17✔
10274
        }
17✔
10275
        if ("length" in obj) {
17,751✔
10276
            return obj.length;
17,751✔
10277
        }
17,751✔
10278
    }, `(length expression)
1✔
10279

1✔
10280
        Function that returns the length of the object. The object can be a LIPS
1✔
10281
        list or any object that has a "length" property. Returns undefined if the
1✔
10282
        length could not be found.`),
1✔
10283
    // ------------------------------------------------------------------
1✔
10284
    'string->number': doc('string->number', function(arg, radix = 10) {
1✔
10285
        typecheck('string->number', arg, 'string', 1);
23✔
10286
        typecheck('string->number', radix, 'number', 2);
23✔
10287
        arg = arg.valueOf();
23✔
10288
        radix = radix.valueOf();
23✔
10289
        if (arg.match(rational_bare_re) || arg.match(rational_re)) {
23✔
10290
            return parse_rational(arg, radix);
7✔
10291
        } else if (arg.match(complex_bare_re) || arg.match(complex_re)) {
23✔
10292
            return parse_complex(arg, radix);
7✔
10293
        } else {
16✔
10294
            const valid_bare = (radix === 10 && !arg.match(/e/i)) || radix === 16;
9✔
10295
            if (arg.match(int_bare_re) && valid_bare || arg.match(int_re)) {
9✔
10296
                return parse_integer(arg, radix);
8✔
10297
            }
8✔
10298
            if (arg.match(float_re)) {
1✔
10299
                return parse_float(arg);
1✔
10300
            }
1✔
10301
        }
9✔
UNCOV
10302
        return false;
×
10303
    }, `(string->number number [radix])
1✔
10304

1✔
10305
        Function that parses a string into a number.`),
1✔
10306
    // ------------------------------------------------------------------
1✔
10307
    'try': doc(new Macro('try', function(code, { use_dynamic, error }) {
1✔
10308
        return new Promise((resolve, reject) => {
197✔
10309
            let catch_clause, finally_clause, body_error;
197✔
10310
            if (LSymbol.is(code.cdr.car.car, 'catch')) {
197✔
10311
                catch_clause = code.cdr.car;
187✔
10312
                if (is_pair(code.cdr.cdr) &&
187✔
10313
                    LSymbol.is(code.cdr.cdr.car.car, 'finally')) {
187✔
10314
                    finally_clause = code.cdr.cdr.car;
7✔
10315
                }
7✔
10316
            } else if (LSymbol.is(code.cdr.car.car, 'finally')) {
197✔
10317
                finally_clause = code.cdr.car;
10✔
10318
            }
10✔
10319
            if (!(finally_clause || catch_clause)) {
197!
UNCOV
10320
                throw new Error('try: invalid syntax');
×
UNCOV
10321
            }
×
10322
            function finalize(result) {
197✔
10323
                resolve(result);
108✔
10324
                throw new IgnoreException('[CATCH]');
108✔
10325
            }
108✔
10326
            let next = (result, next) => {
197✔
10327
                next(result);
176✔
10328
            }
176✔
10329
            if (finally_clause) {
197✔
10330
                next = function(result, cont) {
17✔
10331
                    // prevent infinite loop when finally throw exception
17✔
10332
                    next = reject;
17✔
10333
                    args.error = (e) => {
17✔
10334
                        throw e;
2✔
10335
                    };
17✔
10336
                    unpromise(evaluate(new Pair(
17✔
10337
                        new LSymbol('begin'),
17✔
10338
                        finally_clause.cdr
17✔
10339
                    ), args), function() {
17✔
10340
                        cont(result);
16✔
10341
                    });
17✔
10342
                };
17✔
10343
            }
17✔
10344
            const args = {
197✔
10345
                env: this,
197✔
10346
                use_dynamic,
197✔
10347
                dynamic_env: this,
197✔
10348
                error: (e) => {
197✔
10349
                    if (e instanceof IgnoreException) {
237✔
10350
                        throw e;
123✔
10351
                    }
123✔
10352
                    body_error = true;
114✔
10353
                    if (catch_clause) {
237✔
10354
                        var env = this.inherit('try');
112✔
10355
                        const name = catch_clause.cdr.car.car;
112✔
10356
                        if (!(name instanceof LSymbol)) {
112!
UNCOV
10357
                            throw new Error('try: invalid syntax: catch require variable name');
×
UNCOV
10358
                        }
×
10359
                        env.set(name, e);
112✔
10360
                        let catch_error;
112✔
10361
                        var catch_args = {
112✔
10362
                            env,
112✔
10363
                            use_dynamic,
112✔
10364
                            dynamic_env: this,
112✔
10365
                            error: (e) => {
112✔
10366
                                catch_error = true;
8✔
10367
                                reject(e);
8✔
10368
                                throw new IgnoreException('[CATCH]');
8✔
10369
                            }
8✔
10370
                        };
112✔
10371
                        const value = evaluate(new Pair(
112✔
10372
                            new LSymbol('begin'),
112✔
10373
                            catch_clause.cdr.cdr
112✔
10374
                        ), catch_args);
112✔
10375
                        unpromise(value, function handler(result) {
112✔
10376
                            if (!catch_error) {
108✔
10377
                                next(result, finalize);
108✔
10378
                            }
108✔
10379
                        });
112✔
10380
                    } else {
237✔
10381
                        next(undefined, () => {
2✔
10382
                            reject(e);
2✔
10383
                        });
2✔
10384
                    }
2✔
10385
                }
237✔
10386
            };
197✔
10387
            const value = evaluate(code.car, args);
197✔
10388
            unpromise(value, function(result) {
197✔
10389
                next(result, resolve);
85✔
10390
            }, args.error);
197✔
10391
        });
197✔
10392
    }), `(try expr (catch (e) code))
1✔
10393
         (try expr (catch (e) code) (finally code))
1✔
10394
         (try expr (finally code))
1✔
10395

1✔
10396
         Macro that executes expr and catches any exceptions thrown. If catch is provided
1✔
10397
         it's executed when an error is thrown. If finally is provided it's always
1✔
10398
         executed at the end.`),
1✔
10399
    // ------------------------------------------------------------------
1✔
10400
    'raise': doc('raise', function(obj) {
1✔
10401
        throw obj;
6✔
10402
    }, `(raise obj)
1✔
10403

1✔
10404
        Throws the object verbatim (no wrapping an a new Error).`),
1✔
10405
    'throw': doc('throw', function(message) {
1✔
10406
        throw new Error(message);
8✔
10407
    }, `(throw string)
1✔
10408

1✔
10409
        Throws a new exception.`),
1✔
10410
    // ------------------------------------------------------------------
1✔
10411
    find: doc('find', function find(arg, list) {
1✔
10412
        typecheck('find', list, ['pair', 'nil']);
30✔
10413
        if (is_null(list)) {
30✔
10414
            return false;
1✔
10415
        }
1✔
10416
        var fn = matcher('find', arg);
29✔
10417
        return unpromise(fn(list.car), function(value) {
29✔
10418
            if (value && !is_nil(value)) {
29✔
10419
                return list.car;
9✔
10420
            }
9✔
10421
            return find(arg, list.cdr);
20✔
10422
        });
29✔
10423
    }, `(find fn list)
1✔
10424
        (find regex list)
1✔
10425
        (find atom list)
1✔
10426

1✔
10427
        Higher-order function that finds the first value for which fn return true.
1✔
10428
        If called with a regex or any atom it will create a matcher function.`),
1✔
10429
    // ------------------------------------------------------------------
1✔
10430
    'for-each': doc('for-each', function(fn, ...lists) {
1✔
10431
        typecheck('for-each', fn, 'function');
91✔
10432
        lists.forEach((arg, i) => {
91✔
10433
            typecheck('for-each', arg, ['pair', 'nil'], i + 1);
90✔
10434
        });
91✔
10435
        // we need to use call(this because babel transpile this code into:
91✔
10436
        // var ret = map.apply(void 0, [fn].concat(lists));
91✔
10437
        // it don't work with weakBind
91✔
10438
        var ret = global_env.get('map').call(this, fn, ...lists);
91✔
10439
        if (is_promise(ret)) {
91✔
10440
            return ret.then(() => {});
15✔
10441
        }
15✔
10442
    }, `(for-each fn . lists)
1✔
10443

1✔
10444
        Higher-order function that calls function \`fn\` on each
1✔
10445
        value of the argument. If you provide more than one list
1✔
10446
        it will take each value from each list and call \`fn\` function
1✔
10447
        with that many arguments as number of list arguments.`),
1✔
10448
    // ------------------------------------------------------------------
1✔
10449
    map: doc('map', function map(fn, ...lists) {
1✔
10450
        typecheck('map', fn, 'function');
94,077✔
10451
        var is_list = global_env.get('list?');
94,077✔
10452
        lists.forEach((arg, i) => {
94,077✔
10453
            typecheck('map', arg, ['pair', 'nil'], i + 1);
94,072✔
10454
            // detect cycles
94,072✔
10455
            if (is_pair(arg) && !is_list.call(this, arg)) {
94,072!
UNCOV
10456
                throw new Error(`map: argument ${i + 1} is not a list`);
×
UNCOV
10457
            }
×
10458
        });
94,077✔
10459
        if (lists.length === 0) {
94,077✔
10460
            return nil;
5✔
10461
        }
5✔
10462
        if (lists.some(is_nil)) {
94,077✔
10463
            return nil;
46,822✔
10464
        }
46,822✔
10465
        var args = lists.map(l => l.car);
47,250✔
10466
        const { env, dynamic_env, use_dynamic } = this;
47,250✔
10467
        const result = call_function(fn, args, { env, dynamic_env, use_dynamic });
47,250✔
10468
        return unpromise(result, (head) => {
47,250✔
10469
            return unpromise(map.call(this, fn, ...lists.map(l => l.cdr)), (rest) => {
47,247✔
10470
                return new Pair(head, rest);
47,241✔
10471
            });
47,247✔
10472
        });
47,250✔
10473
    }, `(map fn . lists)
1✔
10474

1✔
10475
        Higher-order function that calls function \`fn\` with each
1✔
10476
        value of the list. If you provide more then one list as argument
1✔
10477
        it will take each value from each list and call \`fn\` function
1✔
10478
        with that many argument as number of list arguments. The return
1✔
10479
        values of the fn calls are accumulated in a result list and
1✔
10480
        returned by map.`),
1✔
10481
    // ------------------------------------------------------------------
1✔
10482
    'list?': doc('list?', function(obj) {
1✔
10483
        var node = obj;
47,404✔
10484
        while (true) {
47,404✔
10485
            if (is_nil(node)) {
96,149✔
10486
                return true;
47,402✔
10487
            }
47,402✔
10488
            if (!is_pair(node)) {
96,149✔
10489
                return false;
1✔
10490
            }
1✔
10491
            if (node.have_cycles('cdr')) {
96,149✔
10492
                return false;
1✔
10493
            }
1✔
10494
            node = node.cdr;
48,745✔
10495
        }
48,745✔
10496
    }, `(list? obj)
1✔
10497

1✔
10498
        Predicate that tests if value is a proper linked list structure.
1✔
10499
        The car of each pair can be any value. It returns false on cyclic lists."`),
1✔
10500
    // ------------------------------------------------------------------
1✔
10501
    fold: doc('fold', fold('fold', function(fold, fn, init, ...lists) {
1✔
10502
        typecheck('fold', fn, 'function');
12✔
10503
        lists.forEach((arg, i) => {
12✔
10504
            typecheck('fold', arg, ['pair', 'nil'], i + 1);
12✔
10505
        });
12✔
10506
        if (lists.some(is_nil)) {
12!
UNCOV
10507
            return init;
×
UNCOV
10508
        }
×
10509
        const value = fold.call(this, fn, init, ...lists.map(l => l.cdr));
12✔
10510
        return unpromise(value, value => {
12✔
10511
            return fn(...lists.map(l => l.car), value);
12✔
10512
        });
12✔
10513
    }), `(fold fn init . lists)
1✔
10514

1✔
10515
         Function fold is left-to-right reversal of reduce. It call \`fn\`
1✔
10516
         on each pair of elements of the list and returns a single value.
1✔
10517
         e.g. it computes (fn 'a 'x (fn 'b 'y (fn 'c 'z 'foo)))
1✔
10518
         for: (fold fn 'foo '(a b c) '(x y z))`),
1✔
10519
    // ------------------------------------------------------------------
1✔
10520
    pluck: doc('pluck', function pluck(...keys) {
1✔
10521
        return function(obj) {
4✔
10522
            keys = keys.map(x => x instanceof LSymbol ? x.__name__ : x);
4✔
10523
            if (keys.length === 0) {
4✔
10524
                return nil;
1✔
10525
            } else if (keys.length === 1) {
4✔
10526
                const [key] = keys;
2✔
10527
                return obj[key];
2✔
10528
            }
2✔
10529
            var result = {};
1✔
10530
            keys.forEach((key) => {
1✔
10531
                result[key] = obj[key];
2✔
10532
            });
1✔
10533
            return result;
1✔
10534
        };
4✔
10535
    }, `(pluck . strings)
1✔
10536

1✔
10537
        If called with a single string it will return a function that when
1✔
10538
        called with an object will return that key from the object.
1✔
10539
        If called with more then one string the returned function will
1✔
10540
        create a new object by copying all properties from the given object.`),
1✔
10541
    // ------------------------------------------------------------------
1✔
10542
    reduce: doc('reduce', fold('reduce', function(reduce, fn, init, ...lists) {
1✔
UNCOV
10543
        typecheck('reduce', fn, 'function');
×
UNCOV
10544
        lists.forEach((arg, i) => {
×
UNCOV
10545
            typecheck('reduce', arg, ['pair', 'nil'], i + 1);
×
UNCOV
10546
        });
×
UNCOV
10547
        if (lists.some(is_nil)) {
×
UNCOV
10548
            return init;
×
UNCOV
10549
        }
×
UNCOV
10550
        return unpromise(fn(...lists.map(l => l.car), init), (value) => {
×
10551
            return reduce.call(this, fn, value, ...lists.map(l => l.cdr));
×
10552
        });
×
10553
    }), `(reduce fn init list . lists)
1✔
10554

1✔
10555
         Higher-order function that takes each element of the list and calls
1✔
10556
         the fn with result of previous call or init and the next element
1✔
10557
         of the list until each element is processed, and returns a single value
1✔
10558
         as result of last call to \`fn\` function.
1✔
10559
         e.g. it computes (fn 'c 'z (fn 'b 'y (fn 'a 'x 'foo)))
1✔
10560
         for: (reduce fn 'foo '(a b c) '(x y z))`),
1✔
10561
    // ------------------------------------------------------------------
1✔
10562
    filter: doc('filter', function filter(arg, list) {
1✔
UNCOV
10563
        typecheck('filter', arg, ['regex', 'function']);
×
UNCOV
10564
        typecheck('filter', list, ['pair', 'nil']);
×
UNCOV
10565
        var array = global_env.get('list->array')(list);
×
UNCOV
10566
        var result = [];
×
UNCOV
10567
        var fn = matcher('filter', arg);
×
UNCOV
10568
        return (function loop(i) {
×
UNCOV
10569
            function next(value) {
×
UNCOV
10570
                if (value && !is_nil(value)) {
×
UNCOV
10571
                    result.push(item);
×
UNCOV
10572
                }
×
UNCOV
10573
                return loop(++i);
×
UNCOV
10574
            }
×
UNCOV
10575
            if (i === array.length) {
×
UNCOV
10576
                return Pair.fromArray(result);
×
UNCOV
10577
            }
×
UNCOV
10578
            var item = array[i];
×
UNCOV
10579
            return unpromise(fn(item), next);
×
UNCOV
10580
        })(0);
×
10581
    }, `(filter fn list)
1✔
10582
        (filter regex list)
1✔
10583

1✔
10584
        Higher-order function that calls \`fn\` for each element of the list
1✔
10585
        and return a new list for only those elements for which fn returns
1✔
10586
        a truthy value. If called with a regex it will create a matcher function.`),
1✔
10587
    // ------------------------------------------------------------------
1✔
10588
    compose: doc(
1✔
10589
        compose,
1✔
10590
        `(compose . fns)
1✔
10591

1✔
10592
         Higher-order function that creates a new function that applies all functions
1✔
10593
         from right to left and returns the last value. Reverse of pipe.
1✔
10594
         e.g.:
1✔
10595
         ((compose (curry + 2) (curry * 3)) 10) ==> (+ 2 (* 3 10)) ==> 32`),
1✔
10596
    pipe: doc(
1✔
10597
        pipe,
1✔
10598
        `(pipe . fns)
1✔
10599

1✔
10600
         Higher-order function that creates a new function that applies all functions
1✔
10601
         from left to right and returns the last value. Reverse of compose.
1✔
10602
         e.g.:
1✔
10603
         ((pipe (curry + 2) (curry * 3)) 10) ==> (* 3 (+ 2 10)) ==> 36`),
1✔
10604
    curry: doc(
1✔
10605
        curry,
1✔
10606
        `(curry fn . args)
1✔
10607

1✔
10608
         Higher-order function that creates a curried version of the function.
1✔
10609
         The result function will have partially applied arguments and it
1✔
10610
         will keep returning one-argument functions until all arguments are provided,
1✔
10611
         then it calls the original function with the accumulated arguments.
1✔
10612

1✔
10613
         e.g.:
1✔
10614
         (define (add a b c d) (+ a b c d))
1✔
10615
         (define add1 (curry add 1))
1✔
10616
         (define add12 (add 2))
1✔
10617
         (display (add12 3 4))`),
1✔
10618
    // ------------------------------------------------------------------
1✔
10619
    // Numbers
1✔
10620
    // ------------------------------------------------------------------
1✔
10621
    gcd: doc('gcd', function gcd(...args) {
1✔
10622
        typecheck_args('lcm', args, 'number');
×
10623
        return args.reduce(function(result, item) {
×
10624
            return result.gcd(item);
×
UNCOV
10625
        });
×
10626
    }, `(gcd n1 n2 ...)
1✔
10627

1✔
10628
        Function that returns the greatest common divisor of the arguments.`),
1✔
10629
    // ------------------------------------------------------------------
1✔
10630
    lcm: doc('lcm', function lcm(...args) {
1✔
UNCOV
10631
        typecheck_args('lcm', args, 'number');
×
UNCOV
10632
        // ref: https://rosettacode.org/wiki/Least_common_multiple#JavaScript
×
UNCOV
10633
        var n = args.length, a = abs(args[0]);
×
UNCOV
10634
        for (var i = 1; i < n; i++) {
×
UNCOV
10635
            var b = abs(args[i]), c = a;
×
UNCOV
10636
            while (a && b) {
×
UNCOV
10637
                a > b ? a %= b : b %= a;
×
UNCOV
10638
            }
×
UNCOV
10639
            a = abs(c * args[i]) / (a + b);
×
UNCOV
10640
        }
×
UNCOV
10641
        return LNumber(a);
×
10642
    }, `(lcm n1 n2 ...)
1✔
10643

1✔
10644
        Function that returns the least common multiple of the arguments.`),
1✔
10645
    // ------------------------------------------------------------------
1✔
10646
    'odd?': doc('odd?', single_math_op(function(num) {
1✔
10647
        return LNumber(num).isOdd();
16✔
10648
    }), `(odd? number)
1✔
10649

1✔
10650
         Checks if number is odd.`),
1✔
10651
    // ------------------------------------------------------------------
1✔
10652
    'even?': doc('even?', single_math_op(function(num) {
1✔
10653
        return LNumber(num).isEven();
5✔
10654
    }), `(even? number)
1✔
10655

1✔
10656
         Checks if number is even.`),
1✔
10657
    // ------------------------------------------------------------------
1✔
10658
    // math functions
1✔
10659
    '*': doc('*', reduce_math_op(function(a, b) {
1✔
10660
        return LNumber(a).mul(b);
10,410✔
10661
    }, LNumber(1)), `(* . numbers)
1✔
10662

1✔
10663
        Multiplies all numbers passed as arguments. If single value is passed
1✔
10664
        it will return that value.`),
1✔
10665
    // ------------------------------------------------------------------
1✔
10666
    '+': doc('+', reduce_math_op(function(a, b) {
1✔
10667
        return LNumber(a).add(b);
88,531✔
10668
    }, LNumber(0)), `(+ . numbers)
1✔
10669

1✔
10670
        Sums all numbers passed as arguments. If single value is passed it will
1✔
10671
        return that value.`),
1✔
10672
    // ------------------------------------------------------------------
1✔
10673
    '-': doc('-', function(...args) {
1✔
10674
        if (args.length === 0) {
18,771✔
10675
            throw new Error('-: procedure require at least one argument');
1✔
10676
        }
1✔
10677
        typecheck_args('-', args, 'number');
18,770✔
10678
        if (args.length === 1) {
18,771✔
10679
            return LNumber(args[0]).sub();
8✔
10680
        }
8✔
10681
        if (args.length) {
18,762✔
10682
            return args.reduce(binary_math_op(function(a, b) {
18,762✔
10683
                return LNumber(a).sub(b);
18,764✔
10684
            }));
18,762✔
10685
        }
18,762✔
10686
    }, `(- n1 n2 ...)
1✔
10687
        (- n)
1✔
10688

1✔
10689
        Subtracts n2 and subsequent numbers from n1. If only one argument is passed
1✔
10690
        it will negate the value.`),
1✔
10691
    // ------------------------------------------------------------------
1✔
10692
    '/': doc('/', function(...args) {
1✔
10693
        if (args.length === 0) {
233!
UNCOV
10694
            throw new Error('/: procedure require at least one argument');
×
UNCOV
10695
        }
×
10696
        typecheck_args('/', args, 'number');
233✔
10697
        if (args.length === 1) {
233✔
10698
            return LNumber(1).div(args[0]);
24✔
10699
        }
24✔
10700
        return args.reduce(binary_math_op(function(a, b) {
209✔
10701
            return LNumber(a).div(b);
209✔
10702
        }));
209✔
10703
    }, `(/ n1 n2 ...)
1✔
10704
        (/ n)
1✔
10705

1✔
10706
        Divides n1 by n2 and subsequent arguments one by one. If single argument
1✔
10707
        is passed it will calculate (/ 1 n).`),
1✔
10708
    // ------------------------------------------------------------------
1✔
10709
    abs: doc('abs', single_math_op(function(n) {
1✔
10710
        return LNumber(n).abs();
21✔
10711
    }), `(abs number)
1✔
10712

1✔
10713
         Function that returns the absolute value (magnitude) of number.`),
1✔
10714
    // ------------------------------------------------------------------
1✔
10715
    truncate: doc('truncate', function(n) {
1✔
10716
        typecheck('truncate', n, 'number');
1✔
10717
        if (LNumber.isFloat(n)) {
1✔
10718
            if (n instanceof LNumber) {
1✔
10719
                n = n.valueOf();
1✔
10720
            }
1✔
10721
            return LFloat(truncate(n));
1✔
10722
        }
1✔
UNCOV
10723
        return n;
×
10724
    }, `(truncate n)
1✔
10725

1✔
10726
        Function that returns the integer part (floor) of a real number.`),
1✔
10727
    // ------------------------------------------------------------------
1✔
10728
    sqrt: doc('sqrt', single_math_op(function(n) {
1✔
10729
        return LNumber(n).sqrt();
19✔
10730
    }), `(sqrt number)
1✔
10731

1✔
10732
         Function that returns the square root of the number.`),
1✔
10733
    // ------------------------------------------------------------------
1✔
10734
    '**': doc('**', binary_math_op(function(a, b) {
1✔
10735
        a = LNumber(a);
63✔
10736
        b = LNumber(b);
63✔
10737
        if (LNumber.isInteger(b)) {
63✔
10738
            const neg = b.cmp(0) === -1;
13✔
10739
            if (neg) {
13✔
10740
                b = b.sub();
3✔
10741
            }
3✔
10742
            if (LNumber.isRational(a)) {
13✔
10743
                if (neg) {
4✔
10744
                    const denom = a.__denom__.pow(b);
2✔
10745
                    if (a.__num__.cmp(1) === 0) {
2✔
10746
                        return denom;
1✔
10747
                    }
1✔
10748
                    const num = a.__num__.pow(b);
1✔
10749
                    return LRational({
1✔
10750
                        num: denom,
1✔
10751
                        denom: num
1✔
10752
                    });
1✔
10753
                }
1✔
10754
                return LRational({
2✔
10755
                    num: a.__num__.pow(b),
2✔
10756
                    denom: a.__denom__.pow(b)
2✔
10757
                });
2✔
10758
            }
2✔
10759
            if (neg) {
13✔
10760
                return LRational({ num: 1, denom: a.pow(b) });
1✔
10761
            }
1✔
10762
        }
13✔
10763
        if (LNumber.isRational(b) && a.cmp(0) === -1) {
63✔
10764
            const denom = b.__denom__;
4✔
10765
            const num = b.__num__;
4✔
10766

4✔
10767
            // square root of negative value can create exact complex without
4✔
10768
            // real value
4✔
10769
            if (denom.cmp(2) === 0) {
4✔
10770
                let base = a.abs().pow(LRational({ num: 1, denom: 2 }));
3✔
10771
                [, base] = a.coerce(base); // get 3.0 when a == -9.0
3✔
10772
                if (b.cmp(0) === 1) {
3✔
10773
                    return LComplex({
2✔
10774
                        re: LNumber(0),
2✔
10775
                        im: base
2✔
10776
                    });
2✔
10777
                }
2✔
10778
                if (num.cmp(0) === -1) {
1✔
10779
                    return LComplex({
1✔
10780
                        re: LNumber(0),
1✔
10781
                        im: LRational({ num: -1, denom: base })
1✔
10782
                    });
1✔
10783
                }
1✔
10784
            }
3✔
10785

1✔
10786
            // this will create complex float, that can have rounding errors
1✔
10787
            const exponent = b.valueOf();
1✔
10788
            const alpha = exponent * Math.PI;
1✔
10789
            const base = a.abs().pow(exponent);
1✔
10790

1✔
10791
            const re = base.mul(Math.cos(alpha));
1✔
10792
            const im = base.mul(Math.sin(alpha));
1✔
10793

1✔
10794
            return LComplex({ re, im });
1✔
10795
        }
1✔
10796
        [a, b] = a.coerce(b);
54✔
10797
        return a.pow(b);
54✔
10798
    }), `(** a b)
1✔
10799

1✔
10800
         Function that calculates number a to to the power of b.`),
1✔
10801
    // ------------------------------------------------------------------
1✔
10802
    '1+': doc('1+', single_math_op(function(number) {
1✔
UNCOV
10803
        return LNumber(number).add(1);
×
10804
    }), `(1+ number)
1✔
10805

1✔
10806
         Function that adds 1 to the number and return result.`),
1✔
10807
    // ------------------------------------------------------------------
1✔
10808
    '1-': doc(single_math_op(function(number) {
1✔
10809
        return LNumber(number).sub(1);
47,706✔
10810
    }), `(1- number)
1✔
10811

1✔
10812
         Function that subtracts 1 from the number and return result.`),
1✔
10813
    // ------------------------------------------------------------------
1✔
10814
    '%': doc('%', function(a, b) {
1✔
10815
        typecheck_args('%', [a, b], 'number');
5✔
10816
        return LNumber(a).rem(b);
5✔
10817
    }, `(% n1 n2)
1✔
10818

1✔
10819
        Function returns the remainder of n1/n2 (modulo).`),
1✔
10820
    // ------------------------------------------------------------------
1✔
10821
    // Booleans
1✔
10822
    '==': doc('==', function(...args) {
1✔
10823
        typecheck_args('==', args, 'number');
27,556✔
10824
        return seq_compare((a, b) => LNumber(a).cmp(b) === 0, args);
27,556✔
10825
    }, `(== x1 x2 ...)}
1✔
10826

1✔
10827
        Function that compares its numerical arguments and checks if they are
1✔
10828
        all equal.`),
1✔
10829
    // ------------------------------------------------------------------
1✔
10830
    '>': doc('>', function(...args) {
1✔
10831
        typecheck_numbers('>', args, ['bigint', 'float', 'rational']);
8,081✔
10832
        return seq_compare((a, b) => LNumber(a).cmp(b) === 1, args);
8,081✔
10833
    }, `(> x1 x2 x3 ...)
1✔
10834

1✔
10835
        Function that compares its numerical arguments and checks if they are
1✔
10836
        monotonically decreasing, i.e. x1 > x2 and x2 > x3 and so on.`),
1✔
10837
    // ------------------------------------------------------------------
1✔
10838
    '<': doc('<', function(...args) {
1✔
10839
        typecheck_numbers('<', args, ['bigint', 'float', 'rational']);
107,268✔
10840
        return seq_compare((a, b) => LNumber(a).cmp(b) === -1, args);
107,268✔
10841
    }, `(< x1 x2 ...)
1✔
10842

1✔
10843
        Function that compares its numerical arguments and checks if they are
1✔
10844
        monotonically increasing, i.e. x1 < x2 and x2 < x3 and so on.`),
1✔
10845
    // ------------------------------------------------------------------
1✔
10846
    '<=': doc('<=', function(...args) {
1✔
10847
        typecheck_numbers('<=', args, ['bigint', 'float', 'rational']);
86✔
10848
        return seq_compare((a, b) => [0, -1].includes(LNumber(a).cmp(b)), args);
86✔
10849
    }, `(<= x1 x2 ...)
1✔
10850

1✔
10851
        Function that compares its numerical arguments and checks if they are
1✔
10852
        monotonically nondecreasing, i.e. x1 <= x2 and x2 <= x3 and so on.`),
1✔
10853
    // ------------------------------------------------------------------
1✔
10854
    '>=': doc('>=', function(...args) {
1✔
10855
        typecheck_numbers('>=', args, ['bigint', 'float', 'rational']);
14✔
10856
        return seq_compare((a, b) => [0, 1].includes(LNumber(a).cmp(b)), args);
14✔
10857
    }, `(>= x1 x2 ...)
1✔
10858

1✔
10859
        Function that compares its numerical arguments and checks if they are
1✔
10860
        monotonically nonincreasing, i.e. x1 >= x2 and x2 >= x3 and so on.`),
1✔
10861
    // ------------------------------------------------------------------
1✔
10862
    'eq?': doc(
1✔
10863
        'eq?',
1✔
10864
        equal,
1✔
10865
        `(eq? a b)
1✔
10866

1✔
10867
         Function that compares two values if they are identical.`),
1✔
10868
    // ------------------------------------------------------------------
1✔
10869
    or: doc(new Macro('or', function(code, { use_dynamic, error }) {
1✔
10870
        var args = global_env.get('list->array')(code);
14,121✔
10871
        var self = this;
14,121✔
10872
        const dynamic_env = self;
14,121✔
10873
        if (!args.length) {
14,121✔
10874
            return false;
1✔
10875
        }
1✔
10876
        var result;
14,120✔
10877
        return (function loop() {
14,120✔
10878
            function next(value) {
27,965✔
10879
                result = value;
23,869✔
10880
                if (!is_false(result)) {
23,869✔
10881
                    return result;
10,024✔
10882
                } else {
23,869✔
10883
                    return loop();
13,845✔
10884
                }
13,845✔
10885
            }
23,869✔
10886
            if (!args.length) {
27,965✔
10887
                if (!is_false(result)) {
4,096!
UNCOV
10888
                    return result;
×
10889
                } else {
4,096✔
10890
                    return false;
4,096✔
10891
                }
4,096✔
10892
            } else {
27,965✔
10893
                var arg = args.shift();
23,869✔
10894
                var value = evaluate(arg, { env: self, dynamic_env, use_dynamic, error });
23,869✔
10895
                return unpromise(value, next);
23,869✔
10896
            }
23,869✔
10897
        })();
14,120✔
10898
    }), `(or . expressions)
1✔
10899

1✔
10900
         Macro that executes the values one by one and returns the first that is
1✔
10901
         a truthy value. If there are no expressions that evaluate to true it
1✔
10902
         returns false.`),
1✔
10903
    // ------------------------------------------------------------------
1✔
10904
    and: doc(new Macro('and', function(code, { use_dynamic, error } = {}) {
1✔
10905
        const args = global_env.get('list->array')(code);
237,403✔
10906
        const self = this;
237,403✔
10907
        const dynamic_env = self;
237,403✔
10908
        if (!args.length) {
237,403✔
10909
            return true;
3✔
10910
        }
3✔
10911
        let result;
237,400✔
10912
        const eval_args = { env: self, dynamic_env, use_dynamic, error };
237,400✔
10913
        return (function loop() {
237,400✔
10914
            function next(value) {
500,929✔
10915
                result = value;
435,184✔
10916
                if (is_false(result)) {
435,184✔
10917
                    return result;
171,655✔
10918
                } else {
435,184✔
10919
                    return loop();
263,529✔
10920
                }
263,529✔
10921
            }
435,184✔
10922
            if (!args.length) {
500,929✔
10923
                if (!is_false(result)) {
65,745✔
10924
                    return result;
65,745✔
10925
                } else {
65,745!
UNCOV
10926
                    return false;
×
UNCOV
10927
                }
×
10928
            } else {
500,929✔
10929
                const arg = args.shift();
435,184✔
10930
                return unpromise(evaluate(arg, eval_args), next);
435,184✔
10931
            }
435,184✔
10932
        })();
237,400✔
10933
    }), `(and . expressions)
1✔
10934

1✔
10935
         Macro that evaluates each expression in sequence and if any value returns false
1✔
10936
         it will stop and return false. If each value returns true it will return the
1✔
10937
         last value. If it's called without arguments it will return true.`),
1✔
10938
    // bit operations
1✔
10939
    '|': doc('|', function(a, b) {
1✔
UNCOV
10940
        return LNumber(a).or(b);
×
10941
    }, `(| a b)
1✔
10942

1✔
10943
        Function that calculates the bitwise or operation.`),
1✔
10944
    '&': doc('&', function(a, b) {
1✔
UNCOV
10945
        return LNumber(a).and(b);
×
10946
    }, `(& a b)
1✔
10947

1✔
10948
        Function that calculates the bitwise and operation.`),
1✔
10949
    '~': doc('~', function(a) {
1✔
UNCOV
10950
        return LNumber(a).neg();
×
10951
    }, `(~ number)
1✔
10952

1✔
10953
        Function that calculates the bitwise inverse (flip all the bits).`),
1✔
10954
    '>>': doc('>>', function(a, b) {
1✔
UNCOV
10955
        return LNumber(a).shr(b);
×
10956
    }, `(>> a b)
1✔
10957

1✔
10958
        Function that right shifts the value a by value b bits.`),
1✔
10959
    '<<': doc('<<', function(a, b) {
1✔
UNCOV
10960
        return LNumber(a).shl(b);
×
10961
    }, `(<< a b)
1✔
10962

1✔
10963
        Function that left shifts the value a by value b bits.`),
1✔
10964
    not: doc('not', function not(value) {
1✔
10965
        return !value;
22✔
10966
    }, `(not object)
1✔
10967

1✔
10968
        Function that returns the Boolean negation of its argument.`)
1✔
10969
}, undefined, 'global');
1✔
10970
var user_env = global_env.inherit('user-env');
1✔
10971
// -------------------------------------------------------------------------
1✔
10972
function set_interaction_env(interaction, internal) {
1✔
10973
    interaction.constant('**internal-env**', internal);
1✔
10974
    interaction.doc(
1✔
10975
        '**internal-env**',
1✔
10976
        `**internal-env**
1✔
10977

1✔
10978
         Constant used to hide stdin, stdout and stderr so they don't interfere
1✔
10979
         with variables with the same name. Constants are an internal type
1✔
10980
         of variable that can't be redefined, defining a variable with the same name
1✔
10981
         will throw an error.`
1✔
10982
    );
1✔
10983
    global_env.set('**interaction-environment**', interaction);
1✔
10984
}
1✔
10985
// -------------------------------------------------------------------------
1✔
10986
set_interaction_env(user_env, internal_env);
1✔
10987
global_env.doc(
1✔
10988
    '**interaction-environment**',
1✔
10989
    `**interaction-environment**
1✔
10990

1✔
10991
    Internal dynamic, global variable used to find interpreter environment.
1✔
10992
    It's used so the read and write functions can locate **internal-env**
1✔
10993
    that contains the references to stdin, stdout and stderr.`
1✔
10994
);
1✔
10995
function set_fs(fs) {
1✔
10996
    user_env.get('**internal-env**').set('fs', fs);
1✔
10997
}
1✔
10998

1✔
10999
// -------------------------------------------------------------------------
1✔
11000
(function() {
1✔
11001
    var map = { ceil: 'ceiling' };
1✔
11002
    ['floor', 'round', 'ceil'].forEach(fn => {
1✔
11003
        var name = map[fn] ? map[fn] : fn;
3✔
11004
        global_env.set(name, doc(name, function(value) {
3✔
11005
            typecheck(name, value, 'number');
29✔
11006
            if (value instanceof LNumber) {
29✔
11007
                return value[fn]();
29✔
11008
            }
29✔
11009
        }, `(${name} number)
3✔
11010

3✔
11011
            Function that calculates the ${name} of a number.`));
3✔
11012
    });
1✔
11013
})();
1✔
11014
// -------------------------------------------------------------------------
1✔
11015
// ref: https://stackoverflow.com/a/4331218/387194
1✔
11016
function allPossibleCases(arr) {
14✔
11017
    if (arr.length === 1) {
14✔
11018
        return arr[0];
4✔
11019
    } else {
14✔
11020
        var result = [];
10✔
11021
        // recur with the rest of array
10✔
11022
        var allCasesOfRest = allPossibleCases(arr.slice(1));
10✔
11023
        for (var i = 0; i < allCasesOfRest.length; i++) {
10✔
11024
            for (var j = 0; j < arr[0].length; j++) {
52✔
11025
                result.push(arr[0][j] + allCasesOfRest[i]);
104✔
11026
            }
104✔
11027
        }
52✔
11028
        return result;
10✔
11029
    }
10✔
11030
}
14✔
11031

1✔
11032
// -------------------------------------------------------------------------
1✔
11033
function combinations(input, start, end) {
1✔
11034
    var result = [];
1✔
11035
    for (var i = start; i <= end; ++i) {
1✔
11036
        var input_arr = [];
4✔
11037
        for (var j = 0; j < i; ++j) {
4✔
11038
            input_arr.push(input);
14✔
11039
        }
14✔
11040
        result = result.concat(allPossibleCases(input_arr));
4✔
11041
    }
4✔
11042
    return result;
1✔
11043
}
1✔
11044

1✔
11045
// -------------------------------------------------------------------------
1✔
11046
// cadr caddr cadadr etc.
1✔
11047
combinations(['d', 'a'], 2, 5).forEach(spec => {
1✔
11048
    const s = spec.split('');
60✔
11049
    const chars = s.slice().reverse();
60✔
11050
    const code = s.map(c => `(c${c}r`).join(' ') + ' arg' + ')'.repeat(s.length);
60✔
11051
    const name = 'c' + spec + 'r';
60✔
11052
    global_env.set(name, doc(name, function(arg) {
60✔
11053
        return chars.reduce(function(list, type) {
37,336✔
11054
            typecheck(name, list, 'pair');
74,807✔
11055
            if (type === 'a') {
74,807✔
11056
                return list.car;
37,158✔
11057
            } else {
74,807✔
11058
                return list.cdr;
37,649✔
11059
            }
37,649✔
11060
        }, arg);
37,336✔
11061
    }, `(${name} arg)
60✔
11062

60✔
11063
        Function that calculates ${code}`));
60✔
11064
});
1✔
11065
// -----------------------------------------------------------------------------
1✔
11066
function reversseFind(dir, fn) {
9✔
11067
    var parts = dir.split(path.sep).filter(Boolean);
9✔
11068
    for (var i = parts.length; i--;) {
9✔
11069
        var p = path.join('/', ...parts.slice(0, i + 1));
9✔
11070
        if (fn(p)) {
9✔
11071
            return p;
9✔
11072
        }
9✔
11073
    }
9✔
11074
}
9✔
11075

1✔
11076
// -----------------------------------------------------------------------------
1✔
11077
function nodeModuleFind(dir) {
9✔
11078
    return reversseFind(dir, function(dir) {
9✔
11079
        return fs.existsSync(path.join(dir, 'node_modules'));
9✔
11080
    });
9✔
11081
}
9✔
11082

1✔
11083
// -------------------------------------------------------------------------
1✔
11084
function is_node() {
6✔
11085
    return typeof global !== 'undefined' && global.global === global;
6✔
11086
}
6✔
11087
// -------------------------------------------------------------------------
1✔
11088
const noop = () => {};
1✔
11089
// -------------------------------------------------------------------------
1✔
11090
async function node_specific() {
1✔
11091
    const { createRequire } = await import('mod' + 'ule');
1✔
11092
    nodeRequire = createRequire(import.meta.url);
1✔
11093
    fs = await import('fs');
1✔
11094
    path = await import('path');
1✔
11095
    global_env.set('global', global);
1✔
11096
    global_env.set('self', global);
1✔
11097
    global_env.set('window', undefined);
1✔
11098
    const moduleURL = new URL(import.meta.url);
1✔
11099
    // using name __direname and __filename breaks after transpilation
1✔
11100
    const __dirname__ = path.dirname(moduleURL.pathname);
1✔
11101
    const __filename__ = path.basename(moduleURL.pathname);
1✔
11102
    global_env.set('__dirname', __dirname__);
1✔
11103
    global_env.set('__filename', __filename__);
1✔
11104
    // ---------------------------------------------------------------------
1✔
11105
    global_env.set('require.resolve', doc('require.resolve', function(path) {
1✔
UNCOV
11106
        typecheck('require.resolve', path, 'string');
×
UNCOV
11107
        var name = path.valueOf();
×
UNCOV
11108
        return nodeRequire.resolve(name);
×
11109
    }, `(require.resolve path)
1✔
11110

1✔
11111
        Returns the path relative to the current module.
1✔
11112

1✔
11113
        Only available when LIPS is running under Node.js.`));
1✔
11114
    // ---------------------------------------------------------------------
1✔
11115
    global_env.set('require', doc('require', function(module) {
1✔
11116
        typecheck('require', module, 'string');
9✔
11117
        module = module.valueOf();
9✔
11118
        var root = process.cwd();
9✔
11119
        var value;
9✔
11120
        try {
9✔
11121
            if (module.match(/^\s*\./)) {
9!
UNCOV
11122
                value = nodeRequire(path.join(root, module));
×
11123
            } else {
9✔
11124
                var dir = nodeModuleFind(root);
9✔
11125
                if (dir) {
9✔
11126
                    value = nodeRequire(path.join(dir, 'node_modules', module));
9✔
11127
                } else {
9!
UNCOV
11128
                    value = nodeRequire(module);
×
UNCOV
11129
                }
×
11130
            }
9✔
11131
        } catch (e) {
9✔
11132
            value = nodeRequire(module);
7✔
11133
        }
7✔
11134
        return patch_value(value, global);
9✔
11135
    }, `(require module)
1✔
11136

1✔
11137
        Function used inside Node.js to import a module.`));
1✔
11138

1✔
11139
    // ignore exceptions that are caught elsewhere. This is needed to fix AVA
1✔
11140
    // reporting unhandled rejections for try..catch
1✔
11141
    // see: https://github.com/avajs/ava/discussions/3289
1✔
11142
    process.on('unhandledRejection', (reason, promise) => {
1✔
11143
        if (reason instanceof IgnoreException) {
26✔
11144
            promise.catch(noop);
26✔
11145
        }
26✔
11146
    });
1✔
11147
}
1✔
11148
// -------------------------------------------------------------------------
1✔
11149
/* c8 ignore next 15 */
1✔
11150
let node_ready; // Scheme load function need to wait for node_specific
1✔
11151
if (is_node()) {
1✔
11152
    node_ready = node_specific();
1✔
11153
} else {
1✔
11154
    node_ready = Promise.resolve();
1✔
11155
    if (typeof window !== 'undefined' && window === root) {
1✔
11156
        global_env.set('window', window);
1✔
11157
        global_env.set('global', undefined);
1✔
11158
        global_env.set('self', window);
1✔
11159
    } else if (typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined') {
1✔
11160
        global_env.set('self', self);
1✔
11161
        global_env.set('window', undefined);
1✔
11162
        global_env.set('global', undefined);
1✔
11163
    }
1✔
11164
}
1✔
11165
// -------------------------------------------------------------------------
1✔
11166
function typeErrorMessage(fn, got, expected, position = null) {
5✔
11167
    let postfix = fn ? ` in expression \`${fn}\`` : '';
5✔
11168
    if (position !== null) {
5✔
11169
        postfix += ` (argument ${position})`;
2✔
11170
    }
2✔
11171
    if (is_function(expected)) {
5!
11172
        return `Invalid type: got ${got}${postfix}`;
×
11173
    }
×
11174
    if (expected instanceof Array) {
5✔
11175
        if (expected.length === 1) {
3✔
11176
            const first = expected[0].toLowerCase();
2✔
11177
            expected = 'a' + ('aeiou'.includes(first) ? 'n ' : ' ') + expected[0];
2!
11178
        } else {
3✔
11179
            expected = new Intl.ListFormat('en', {
1✔
11180
                style: 'long', type: 'disjunction'
1✔
11181
            }).format(expected);
1✔
11182
        }
1✔
11183
    }
3✔
11184
    return `Expecting ${expected} got ${got}${postfix}`;
5✔
11185
}
5✔
11186

1✔
11187
// -------------------------------------------------------------------------
1✔
11188
function typecheck_number(fn, arg, expected, position = null) {
230,900✔
11189
    typecheck(fn, arg, 'number', position);
230,900✔
11190
    const arg_type = arg.__type__;
230,900✔
11191
    let match;
230,900✔
11192
    if (is_pair(expected)) {
230,900!
UNCOV
11193
        expected = expected.to_array();
×
UNCOV
11194
    }
×
11195
    if (expected instanceof Array) {
230,900✔
11196
        expected = expected.map(x => x.valueOf());
230,900✔
11197
    }
230,900✔
11198
    if (expected instanceof Array) {
230,900✔
11199
        expected = expected.map(x => x.valueOf().toLowerCase());
230,900✔
11200
        if (expected.includes(arg_type)) {
230,900✔
11201
            match = true;
230,900✔
11202
        }
230,900✔
11203
    } else {
230,900!
UNCOV
11204
        expected = expected.valueOf().toLowerCase();
×
UNCOV
11205
    }
×
11206
    if (!match && arg_type !== expected) {
230,900!
UNCOV
11207
        throw new Error(typeErrorMessage(fn, arg_type, expected, position));
×
UNCOV
11208
    }
×
11209
}
230,900✔
11210

1✔
11211
// -------------------------------------------------------------------------
1✔
11212
function typecheck_numbers(fn, args, expected) {
115,449✔
11213
    args.forEach((arg, i) => {
115,449✔
11214
        typecheck_number(fn, arg, expected, i + 1);
230,900✔
11215
    });
115,449✔
11216
}
115,449✔
11217

1✔
11218
// -------------------------------------------------------------------------
1✔
11219
function typecheck_args(fn, args, expected) {
46,588✔
11220
    args.forEach((arg, i) => {
46,588✔
11221
        typecheck(fn, arg, expected, i + 1);
93,146✔
11222
    });
46,588✔
11223
}
46,588✔
11224
// -------------------------------------------------------------------------
1✔
11225
function typecheck_text_port(fn, arg, type) {
272✔
11226
    typecheck(fn, arg, type);
272✔
11227
    if (arg.__type__ === binary_port) {
272!
UNCOV
11228
        throw new Error(typeErrorMessage(
×
UNCOV
11229
            fn,
×
UNCOV
11230
            'binary-port',
×
UNCOV
11231
            'textual-port'
×
UNCOV
11232
        ));
×
UNCOV
11233
    }
×
11234
}
272✔
11235
// -------------------------------------------------------------------------
1✔
11236
function typecheck(fn, arg, expected, position = null) {
40,396,488✔
11237
    fn = fn.valueOf();
40,396,488✔
11238
    const arg_type = type(arg).toLowerCase();
40,396,488✔
11239
    if (is_function(expected)) {
40,396,488!
UNCOV
11240
        if (!expected(arg)) {
×
UNCOV
11241
            throw new Error(typeErrorMessage(fn, arg_type, expected, position));
×
UNCOV
11242
        }
×
UNCOV
11243
        return;
×
UNCOV
11244
    }
×
11245
    var match = false;
40,396,488✔
11246
    if (is_pair(expected)) {
40,396,488✔
11247
        expected = expected.to_array();
19✔
11248
    }
19✔
11249
    if (expected instanceof Array) {
40,396,488✔
11250
        expected = expected.map(x => x.valueOf());
38,691,063✔
11251
    }
38,691,063✔
11252
    if (expected instanceof Array) {
40,396,488✔
11253
        expected = expected.map(x => x.valueOf().toLowerCase());
38,691,063✔
11254
        if (expected.includes(arg_type)) {
38,691,063✔
11255
            match = true;
38,691,060✔
11256
        }
38,691,060✔
11257
    } else {
40,396,488✔
11258
        expected = expected.valueOf().toLowerCase();
1,705,425✔
11259
    }
1,705,425✔
11260
    if (!match && arg_type !== expected) {
40,396,488✔
11261
        throw new Error(typeErrorMessage(fn, arg_type, expected, position));
5✔
11262
    }
5✔
11263
}
40,396,488✔
11264

1✔
11265
// -------------------------------------------------------------------------
1✔
11266
function memoize(fn) {
1✔
11267
    var memo = new WeakMap();
1✔
11268
    return function(arg) {
1✔
11269
        var result = memo.get(arg);
40,398,666✔
11270
        if (!result) {
40,398,666✔
11271
            result = fn(arg);
40,398,666✔
11272
        }
40,398,666✔
11273
        return result;
40,398,666✔
11274
    };
1✔
11275
}
1✔
11276
// -------------------------------------------------------------------------
1✔
11277
/* eslint-disable no-func-assign */
1✔
11278
type = memoize(type);
1✔
11279
/* eslint-enable no-func-assign */
1✔
11280
// -------------------------------------------------------------------------
1✔
11281
function type(obj) {
40,411,289✔
11282
    let t = type_constants.get(obj);
40,411,289✔
11283
    if (t) {
40,411,289✔
11284
        return t;
6✔
11285
    }
6✔
11286
    if (typeof obj === 'object') {
40,411,289✔
11287
        for (let [key, value] of Object.entries(type_mapping)) {
16,452,230✔
11288
            if (obj instanceof value) {
33,386,661✔
11289
                return key;
16,451,567✔
11290
            }
16,451,567✔
11291
        }
33,386,661✔
11292
        if (is_instance(obj)) {
16,452,230✔
11293
            if (is_function(obj.typeOf)) {
6✔
11294
                return obj.typeOf();
6✔
11295
            }
6✔
UNCOV
11296
            return 'instance';
×
UNCOV
11297
        }
×
11298
        if (obj.constructor) {
657✔
11299
            if (obj.constructor.__class__) {
657✔
11300
                return obj.constructor.__class__;
555✔
11301
            }
555✔
11302
            if (obj.constructor === Object) {
657✔
11303
                if (is_iterator(obj, Symbol.iterator)) {
40!
UNCOV
11304
                    return 'iterator';
×
UNCOV
11305
                }
×
11306
                if (is_iterator(obj, Symbol.asyncIterator)) {
40!
UNCOV
11307
                    return 'async-iterator';
×
UNCOV
11308
                }
×
11309
            }
40✔
11310
            if (obj.constructor.name === '') {
657!
UNCOV
11311
                return 'object';
×
UNCOV
11312
            }
×
11313
            return obj.constructor.name.toLowerCase();
102✔
11314
        }
102✔
11315
    }
16,452,230✔
11316
    if (obj === undefined) {
40,411,289✔
11317
        return 'void';
172✔
11318
    }
172✔
11319
    return typeof obj;
23,958,881✔
11320
}
40,411,289✔
11321
// -------------------------------------------------------------------------
1✔
11322
// :; wrap tree of Promises with single Promise or return argument as is
1✔
11323
// :: if tree have no Promises
1✔
11324
// -------------------------------------------------------------------------
1✔
11325
function resolve_promises(arg) {
10,046,993✔
11326
    var promises = [];
10,046,993✔
11327
    traverse(arg);
10,046,993✔
11328
    if (promises.length) {
10,046,993✔
11329
        return resolve(arg);
9,480✔
11330
    }
9,480✔
11331
    return arg;
10,037,513✔
11332
    function traverse(node) {
10,037,513✔
11333
        if (is_promise(node)) {
235,966,655✔
11334
            promises.push(node);
9,480✔
11335
        } else if (is_pair(node)) {
235,966,655✔
11336
            if (!node.have_cycles('car')) {
112,694,546✔
11337
                traverse(node.car);
112,694,545✔
11338
            }
112,694,545✔
11339
            if (!node.have_cycles('cdr')) {
112,694,546✔
11340
                traverse(node.cdr);
112,694,534✔
11341
            }
112,694,534✔
11342
        } else if (node instanceof Array) {
235,957,175✔
11343
            node.forEach(traverse);
353,178✔
11344
        }
353,178✔
11345
    }
235,966,655✔
11346
    async function promise(node) {
10,037,513✔
UNCOV
11347
        var pair = new Pair(
×
11348
            node.have_cycles('car') ? node.car : await resolve(node.car),
×
11349
            node.have_cycles('cdr') ? node.cdr : await resolve(node.cdr)
×
UNCOV
11350
        );
×
11351
        if (node[__data__]) {
×
11352
            pair[__data__] = true;
×
UNCOV
11353
        }
×
UNCOV
11354
        return pair;
×
11355
    }
×
11356
    function resolve(node) {
10,037,513✔
11357
        if (node instanceof Array) {
9,480!
UNCOV
11358
            return promise_all(node.map(resolve));
×
UNCOV
11359
        }
×
11360
        if (is_pair(node) && promises.length) {
9,480!
UNCOV
11361
            return promise(node);
×
UNCOV
11362
        }
×
11363
        return node;
9,480✔
11364
    }
9,480✔
11365
}
10,046,993✔
11366
// -------------------------------------------------------------------------
1✔
11367
function evaluate_args(rest, { use_dynamic, ...options }) {
2,765,124✔
11368
    var args = [];
2,765,124✔
11369
    var node = rest;
2,765,124✔
11370
    function next() {
2,765,124✔
11371
        return args;
2,765,117✔
11372
    }
2,765,117✔
11373
    return (function loop() {
2,765,124✔
11374
        if (is_pair(node)) {
6,459,669✔
11375
            let arg = evaluate(node.car, { use_dynamic, ...options });
3,694,551✔
11376
            if (use_dynamic) {
3,694,551✔
11377
                // NOTE: why native function need bind to env?
238,557✔
11378
                arg = unpromise(arg, arg => {
238,557✔
11379
                    if (is_native_function(arg)) {
238,557!
UNCOV
11380
                        return arg.bind(dynamic_env);
×
UNCOV
11381
                    }
×
11382
                    return arg;
238,557✔
11383
                });
238,557✔
11384
            }
238,557✔
11385
            return unpromise(resolve_promises(arg), function(arg) {
3,694,547✔
11386
                args.push(arg);
3,694,545✔
11387
                if (node.have_cycles('cdr')) {
3,694,545!
UNCOV
11388
                    throw new Error(`Invalid expression: Can't evaluate cycle`);
×
UNCOV
11389
                }
×
11390
                node = node.cdr;
3,694,545✔
11391
                return loop();
3,694,545✔
11392
            });
3,694,547✔
11393
        } else if (is_nil(node)) {
6,459,669✔
11394
            return next();
2,765,117✔
11395
        } else {
2,765,118✔
11396
            throw new Error('Syntax Error: improper list found in apply');
1✔
11397
        }
1✔
11398
    })();
2,765,124✔
11399
}
2,765,124✔
11400
// -------------------------------------------------------------------------
1✔
11401
function evaluate_syntax(macro, code, eval_args) {
524✔
11402
    var value = macro.invoke(code, eval_args);
524✔
11403
    return unpromise(resolve_promises(value), function(value) {
524✔
11404
        if (is_pair(value)) {
488✔
11405
            value.mark_cycles();
246✔
11406
        }
246✔
11407
        return quote(value);
488✔
11408
    });
524✔
11409
}
524✔
11410
// -------------------------------------------------------------------------
1✔
11411
function evaluate_macro(macro, code, eval_args) {
3,465,542✔
11412
    function finalize(result) {
3,465,542✔
11413
        if (is_pair(result)) {
761,580✔
11414
            result.mark_cycles();
895✔
11415
            return result;
895✔
11416
        }
895✔
11417
        return quote(result);
760,685✔
11418
    }
761,580✔
11419
    const value = macro.invoke(code, eval_args);
3,465,542✔
11420
    return unpromise(resolve_promises(value), function ret(value) {
3,465,542✔
11421
        if (!value || value && value[__data__] || self_evaluated(value)) {
3,465,156✔
11422
            return value;
2,703,548✔
11423
        } else {
3,465,156✔
11424
            return unpromise(evaluate(value, eval_args), finalize);
761,608✔
11425
        }
761,608✔
11426
    }, error => {
3,465,542✔
11427
        throw error;
76✔
11428
    });
3,465,542✔
11429
}
3,465,542✔
11430

1✔
11431
// -------------------------------------------------------------------------
1✔
11432
function prepare_fn_args(fn, args) {
2,769,220✔
11433
    if (is_bound(fn) && !is_object_bound(fn) &&
2,769,220✔
11434
        (!lips_context(fn) || is_port_method(fn))) {
2,769,220✔
11435
        args = args.map(unbox);
51,731✔
11436
    }
51,731✔
11437
    if (!is_raw_lambda(fn) &&
2,769,220✔
11438
        args.some(is_lips_function) &&
2,769,220✔
11439
        !is_lips_function(fn) &&
2,769,220✔
11440
        !is_array_method(fn)) {
2,769,220✔
11441
        // we unbox values from callback functions #76
2,163✔
11442
        // calling map on array should not unbox the value
2,163✔
11443
        var result = [], i = args.length;
2,163✔
11444
        while (i--) {
2,163✔
11445
            let arg = args[i];
2,698✔
11446
            if (is_lips_function(arg)) {
2,698✔
11447
                var wrapper = function(...args) {
2,169✔
11448
                    return unpromise(arg.apply(this, args), unbox);
2,154✔
11449
                };
2,169✔
11450
                // make wrapper work like output of bind
2,169✔
11451
                hidden_prop(wrapper, '__bound__', true);
2,169✔
11452
                hidden_prop(wrapper, '__fn__', arg);
2,169✔
11453
                // copy prototype from function to wrapper
2,169✔
11454
                // so this work when calling new from JavaScript
2,169✔
11455
                // case of Preact that pass LIPS class as argument
2,169✔
11456
                // to h function
2,169✔
11457
                wrapper.prototype = arg.prototype;
2,169✔
11458
                result[i] = wrapper;
2,169✔
11459
            } else {
2,698✔
11460
                result[i] = arg;
529✔
11461
            }
529✔
11462
        }
2,698✔
11463
        args = result;
2,163✔
11464
    }
2,163✔
11465
    return args;
2,769,220✔
11466
}
2,769,220✔
11467

1✔
11468
// -------------------------------------------------------------------------
1✔
11469
function call_function(fn, args, { env, dynamic_env, use_dynamic } = {}) {
2,812,410✔
11470
    const scope = env?.new_frame(fn, args);
2,812,410✔
11471
    const dynamic_scope = dynamic_env?.new_frame(fn, args);
2,812,410✔
11472
    const context = new LambdaContext({
2,812,410✔
11473
        env: scope,
2,812,410✔
11474
        use_dynamic,
2,812,410✔
11475
        dynamic_env: dynamic_scope
2,812,410✔
11476
    });
2,812,410✔
11477
    return resolve_promises(fn.apply(context, args));
2,812,410✔
11478
}
2,812,410✔
11479

1✔
11480
// -------------------------------------------------------------------------
1✔
11481
function apply(fn, args, { env, dynamic_env, use_dynamic, error = () => {} } = {}) {
2,765,124✔
11482
    args = evaluate_args(args, { env, dynamic_env, error, use_dynamic });
2,765,124✔
11483
    return unpromise(args, function(args) {
2,765,124✔
11484
        if (is_raw_lambda(fn)) {
2,765,117✔
11485
            // lambda need environment as context
626,514✔
11486
            // normal functions are bound to their contexts
626,514✔
11487
            fn = unbind(fn);
626,514✔
11488
        }
626,514✔
11489
        args = prepare_fn_args(fn, args);
2,765,117✔
11490
        const _args = args.slice();
2,765,117✔
11491
        const result = call_function(fn, _args, { env, dynamic_env, use_dynamic });
2,765,117✔
11492
        return unpromise(result, (result) => {
2,765,117✔
11493
            if (is_pair(result)) {
2,765,021✔
11494
                result.mark_cycles();
351,662✔
11495
                return quote(result);
351,662✔
11496
            }
351,662✔
11497
            return box(result);
2,413,359✔
11498
        }, error);
2,765,117✔
11499
    });
2,765,124✔
11500
}
2,765,124✔
11501
// -------------------------------------------------------------------------
1✔
11502
// :: Parameters for make-parameter and parametrize
1✔
11503
// -------------------------------------------------------------------------
1✔
11504
class Parameter {
13✔
11505
    __value__;
13✔
11506
    __fn__;
13✔
11507
    #__p_name__;
13✔
11508
    constructor(init, fn = null, name = null) {
13✔
11509
        this.__value__ = init;
13✔
11510
        if (fn) {
13✔
11511
            if (!is_function(fn)) {
2!
UNCOV
11512
                throw new Error('Section argument to Parameter need to be function ' +
×
UNCOV
11513
                                `${type(fn)} given`);
×
UNCOV
11514
            }
×
11515
            this.__fn__ = fn;
2✔
11516
        }
2✔
11517
        if (name) {
13✔
11518
            this.#__p_name__ = name;
7✔
11519
        }
7✔
11520
    }
13✔
11521
    get __name__() {
13✔
11522
        return this.#__p_name__;
33✔
11523
    }
33✔
11524
    set __name__(name) {
13✔
11525
        this.#__p_name__ = name;
6✔
11526
        if (this.__fn__) {
6✔
11527
            this.__fn__.__name__ = `fn-${name}`;
1✔
11528
        }
1✔
11529
    }
6✔
11530
    invoke() {
13✔
11531
        if (is_function(this.__fn__)) {
13✔
11532
            return this.__fn__(this.__value__);
2✔
11533
        }
2✔
11534
        return this.__value__;
11✔
11535
    }
13✔
11536
    inherit(value) {
13✔
11537
        return new Parameter(value, this.__fn__, this.__name__);
7✔
11538
    }
7✔
11539
}
13✔
11540
// -------------------------------------------------------------------------
1✔
11541
class LambdaContext {
2,812,410✔
11542
    env;
2,812,410✔
11543
    dynamic_env;
2,812,410✔
11544
    use_dynamic;
2,812,410✔
11545
    constructor(payload) {
2,812,410✔
11546
        Object.assign(this, payload);
2,812,410✔
11547
    }
2,812,410✔
11548
    get __name__() {
2,812,410✔
11549
        return this.env.__name__;
33✔
11550
    }
33✔
11551
    get __parent__() {
2,812,410✔
11552
        return this.env.__parent__;
33✔
11553
    }
33✔
11554
    get(...args) {
2,812,410✔
11555
        return this.env.get(...args);
210✔
11556
    }
210✔
11557
}
2,812,410✔
11558
// -------------------------------------------------------------------------
1✔
11559
function search_param(env, param) {
15✔
11560
    let candidate = env.get(param.__name__, { throwError: false });
15✔
11561
    if (is_parameter(candidate) && candidate !== param) {
15✔
11562
        return candidate;
8✔
11563
    }
8✔
11564
    let is_first_env = true;
7✔
11565
    const top_env = user_env.get('**interaction-environment**');
7✔
11566
    while (true) {
15✔
11567
        const parent = env.get('parent.frame', { throwError: false });
12✔
11568
        env = parent(0);
12✔
11569
        if (env === top_env) {
12✔
11570
            break;
7✔
11571
        }
7✔
11572
        is_first_env = false;
5✔
11573
        candidate = env.get(param.__name__, { throwError: false });
5✔
11574
        if (is_parameter(candidate) && candidate !== param) {
12!
UNCOV
11575
            return candidate;
×
UNCOV
11576
        }
×
11577
    }
12✔
11578
    return param;
7✔
11579
}
15✔
11580

1✔
11581
// -------------------------------------------------------------------------
1✔
11582
// :: Continuations object from call/cc
1✔
11583
// -------------------------------------------------------------------------
1✔
11584
class Continuation {
13✔
11585
    __value__;
13✔
11586
    constructor(k) {
13✔
11587
        this.__value__ = k;
13✔
11588
    }
13✔
11589
    invoke() {
13✔
11590
        if (this.__value__ === null) {
8✔
11591
            throw new Error('Continuations are not implemented yet');
8✔
11592
        }
8✔
11593
    }
8✔
11594
}
13✔
11595

1✔
11596
// -------------------------------------------------------------------------
1✔
11597
function evaluate(code, { env, dynamic_env, use_dynamic, error = noop, ...rest } = {}) {
11,547,098✔
11598
    try {
11,547,098✔
11599
        if (!is_env(dynamic_env)) {
11,547,098✔
11600
            dynamic_env = env === true ? user_env : (env || user_env);
130!
11601
        }
130✔
11602
        if (use_dynamic) {
11,547,098✔
11603
            env = dynamic_env;
811,112✔
11604
        } else if (env === true) {
11,547,098!
UNCOV
11605
            env = user_env;
×
11606
        } else {
10,735,986✔
11607
            env = env || global_env;
10,735,986✔
11608
        }
10,735,986✔
11609
        var eval_args = { env, dynamic_env, use_dynamic, error };
11,547,098✔
11610
        var value;
11,547,098✔
11611
        if (is_null(code)) {
11,547,098✔
11612
            return code;
2,971✔
11613
        }
2,971✔
11614
        if (code instanceof LSymbol) {
11,547,098✔
11615
            return env.get(code);
3,888,671✔
11616
        }
3,888,671✔
11617
        if (!is_pair(code)) {
11,547,098✔
11618
            return code;
1,424,225✔
11619
        }
1,424,225✔
11620
        var first = code.car;
6,231,231✔
11621
        var rest = code.cdr;
6,231,231✔
11622
        if (is_pair(first)) {
11,547,098✔
11623
            value = resolve_promises(evaluate(first, eval_args));
1,057✔
11624
            if (is_promise(value)) {
1,057✔
11625
                return value.then((value) => {
3✔
11626
                    if (!is_callable(value)) {
3✔
11627
                        throw new Error(
1✔
11628
                            type(value) + ' ' + env.get('repr')(value) +
1✔
11629
                                ' is not callable while evaluating ' + code.toString()
1✔
11630
                        );
1✔
11631
                    }
1✔
11632
                    return evaluate(new Pair(value, code.cdr), eval_args);
2✔
11633
                });
3✔
11634
                // else is later in code
3✔
11635
            } else if (!is_callable(value)) {
1,057!
UNCOV
11636
                throw new Error(
×
UNCOV
11637
                    type(value) + ' ' + env.get('repr')(value) +
×
UNCOV
11638
                        ' is not callable while evaluating ' + code.toString()
×
UNCOV
11639
                );
×
UNCOV
11640
            }
×
11641
        }
1,057✔
11642
        if (first instanceof LSymbol) {
11,547,098✔
11643
            value = env.get(first);
6,230,171✔
11644
        } else if (is_function(first)) {
11,547,098✔
11645
            value = first;
2✔
11646
        }
2✔
11647
        let result;
6,231,223✔
11648
        if (value instanceof Syntax) {
11,547,098✔
11649
            result = evaluate_syntax(value, code, eval_args);
513✔
11650
        } else if (value instanceof Macro) {
11,547,098✔
11651
            result = evaluate_macro(value, rest, eval_args);
3,465,542✔
11652
        } else if (is_function(value)) {
6,230,710✔
11653
            result = apply(value, rest, eval_args);
2,765,124✔
11654
        } else if (value instanceof SyntaxParameter) {
2,765,168✔
11655
            result = evaluate_syntax(value._syntax, code, eval_args);
11✔
11656
        } else if (is_parameter(value)) {
44✔
11657
            const param = search_param(dynamic_env, value);
15✔
11658
            if (is_null(code.cdr)) {
15✔
11659
                result = param.invoke();
13✔
11660
            } else {
15✔
11661
                return unpromise(evaluate(code.cdr.car, eval_args), function(value) {
2✔
11662
                    param.__value__ = value;
2✔
11663
                });
2✔
11664
            }
2✔
11665
        } else if (is_continuation(value)) {
33✔
11666
            result = value.invoke();
8✔
11667
        } else if (is_pair(code)) {
18✔
11668
            value = first && first.toString();
10✔
11669
            throw new Error(`${type(first)} ${value} is not a function`);
10✔
11670
        } else {
10!
UNCOV
11671
            return code;
×
UNCOV
11672
        }
×
11673
        // escape promise feature #54
6,230,765✔
11674
        var __promise__ = env.get(Symbol.for('__promise__'), { throwError: false });
6,230,765✔
11675
        if (__promise__ === true && is_promise(result)) {
11,547,098✔
11676
            // fix #139 evaluate the code inside the promise that is not data.
12✔
11677
            // When promise is not quoted it happen automatically, when returning
12✔
11678
            // promise from evaluate.
12✔
11679
            result = result.then(result => {
12✔
11680
                if (is_pair(result) && !value[__data__]) {
10!
11681
                    return evaluate(result, eval_args);
×
11682
                }
×
11683
                return result;
10✔
11684
            });
12✔
11685
            return new QuotedPromise(result);
12✔
11686
        }
12✔
11687
        return result;
6,230,753✔
11688
    } catch (e) {
11,547,098✔
11689
        error && error.call(env, e, code);
473✔
11690
    }
473✔
11691
}
11,547,098✔
11692
// -------------------------------------------------------------------------
1✔
11693
const compile = exec_collect(function(code) {
1✔
UNCOV
11694
    return code;
×
11695
});
1✔
11696
// -------------------------------------------------------------------------
1✔
11697
const exec = exec_collect(function(code, value) {
1✔
11698
    return value;
915✔
11699
});
1✔
11700
// -------------------------------------------------------------------------
1✔
11701
function exec_with_stacktrace(code, { env, dynamic_env, use_dynamic } = {}) {
915✔
11702
    return evaluate(code, {
915✔
11703
        env,
915✔
11704
        dynamic_env,
915✔
11705
        use_dynamic,
915✔
11706
        error: (e, code) => {
915✔
11707
            if (e && e.message) {
273✔
11708
                if (e.message.match(/^Error:/)) {
272!
UNCOV
11709
                    var re = /^(Error:)\s*([^:]+:\s*)/;
×
UNCOV
11710
                    // clean duplicated Error: added by JS
×
UNCOV
11711
                    e.message = e.message.replace(re, '$1 $2');
×
UNCOV
11712
                }
×
11713
                if (code) {
272✔
11714
                    // LIPS stack trace
268✔
11715
                    if (!(e.__code__ instanceof Array)) {
268✔
11716
                        e.__code__ = [];
39✔
11717
                    }
39✔
11718
                    e.__code__.push(code.toString(true));
268✔
11719
                }
268✔
11720
            }
272✔
11721
            if (!(e instanceof IgnoreException)) {
273✔
11722
                throw e;
273✔
11723
            }
273✔
11724
        }
273✔
11725
    });
915✔
11726
}
915✔
11727
// -------------------------------------------------------------------------
1✔
11728
function exec_collect(collect_callback) {
2✔
11729
    return async function exec_lambda(arg, { env, dynamic_env, use_dynamic } = {}) {
2✔
11730
        if (!is_env(dynamic_env)) {
7✔
11731
            dynamic_env = env === true ? user_env : env || user_env;
7!
11732
        }
7✔
11733
        if (env === true) {
7!
UNCOV
11734
            env = user_env;
×
11735
        } else {
7✔
11736
            env = env || user_env;
7✔
11737
        }
7✔
11738
        const results = [];
7✔
11739
        if (is_pair(arg)) {
7!
UNCOV
11740
            return [await exec_with_stacktrace(arg, { env, dynamic_env, use_dynamic })];
×
UNCOV
11741
        }
×
11742
        const input = Array.isArray(arg) ? arg : _parse(arg);
7✔
11743
        for await (let code of input) {
7✔
11744
            const value = await exec_with_stacktrace(code, { env, dynamic_env, use_dynamic });
915✔
11745
            results.push(collect_callback(code, value));
915✔
11746
        }
915✔
11747
        return results;
6✔
11748
    };
2✔
11749
}
2✔
11750
// -------------------------------------------------------------------------
1✔
11751
function balanced(code) {
35✔
11752
    var maching_pairs = {
35✔
11753
        '[': ']',
35✔
11754
        '(': ')'
35✔
11755
    };
35✔
11756
    var tokens;
35✔
11757
    if (typeof code === 'string') {
35!
UNCOV
11758
        try {
×
UNCOV
11759
            tokens = tokenize(code);
×
UNCOV
11760
        } catch(e) {
×
UNCOV
11761
            if (e instanceof Unterminated) {
×
UNCOV
11762
                return false;
×
UNCOV
11763
            }
×
UNCOV
11764
            throw e;
×
UNCOV
11765
        }
×
11766
    } else {
35✔
11767
        tokens = code.map(x => x && x.token ? x.token : x);
35✔
11768
    }
35✔
11769

35✔
11770
    var open_tokens = Object.keys(maching_pairs);
35✔
11771
    var brackets = Object.values(maching_pairs).concat(open_tokens);
35✔
11772
    tokens = tokens.filter(token => brackets.includes(token));
35✔
11773

35✔
11774
    const stack = new Stack();
35✔
11775
    for (const token of tokens) {
35✔
11776
        if (open_tokens.includes(token)) {
235✔
11777
            stack.push(token);
134✔
11778
        } else if (!stack.is_empty()) { // closing token
235✔
11779
            var last = stack.top();
101✔
11780
            // last on stack need to match
101✔
11781
            const closing_token = maching_pairs[last];
101✔
11782
            if (token === closing_token) {
101✔
11783
                stack.pop();
101✔
11784
            } else {
101!
11785
                throw new Error(`Syntax error: missing closing ${closing_token}`);
×
UNCOV
11786
            }
×
11787
        } else {
101!
UNCOV
11788
            // closing bracket without opening
×
UNCOV
11789
            throw new Error(`Syntax error: not matched closing ${token}`);
×
UNCOV
11790
        }
×
11791
    }
235✔
11792
    return stack.is_empty();
35✔
11793
}
35✔
11794

1✔
11795
// -------------------------------------------------------------------------
1✔
UNCOV
11796
function fworker(fn) {
×
UNCOV
11797
    // ref: https://stackoverflow.com/a/10372280/387194
×
UNCOV
11798
    var str = '(' + fn.toString() + ')()';
×
UNCOV
11799
    var URL = window.URL || window.webkitURL;
×
UNCOV
11800
    var blob;
×
UNCOV
11801
    try {
×
11802
        blob = new Blob([str], { type: 'application/javascript' });
×
11803
    } catch (e) { // Backwards-compatibility
×
11804
        const BlobBuilder = window.BlobBuilder ||
×
11805
              window.WebKitBlobBuilder ||
×
11806
              window.MozBlobBuilder;
×
11807
        blob = new BlobBuilder();
×
11808
        blob.append(str);
×
11809
        blob = blob.getBlob();
×
UNCOV
11810
    }
×
UNCOV
11811
    return new root.Worker(URL.createObjectURL(blob));
×
UNCOV
11812
}
×
11813

1✔
11814
// -------------------------------------------------------------------------
1✔
UNCOV
11815
function is_dev() {
×
UNCOV
11816
    return lips.version.match(/^(\{\{VER\}\}|DEV)$/);
×
UNCOV
11817
}
×
11818

1✔
11819
// -------------------------------------------------------------------------
1✔
11820
function get_current_script() {
1✔
11821
    if (is_node()) {
1✔
11822
        return;
1✔
11823
    }
1✔
UNCOV
11824
    let script;
×
UNCOV
11825
    if (document.currentScript) {
×
UNCOV
11826
        script = document.currentScript;
×
UNCOV
11827
    } else {
×
UNCOV
11828
        const scripts = document.querySelectorAll('script');
×
11829
        if (!scripts.length) {
×
11830
            return;
×
UNCOV
11831
        }
×
11832
        script = scripts[scripts.length - 1];
×
11833
    }
×
11834
    const url = script.getAttribute('src');
×
UNCOV
11835
    return url;
×
11836
}
1✔
11837

1✔
11838
// -------------------------------------------------------------------------
1✔
11839
const current_script = get_current_script();
1✔
11840

1✔
11841
// -------------------------------------------------------------------------
1✔
11842
function bootstrap(url = '') {
×
11843
    const std = 'dist/std.xcb';
×
11844
    if (url === '') {
×
11845
        if (current_script) {
×
11846
            url = current_script.replace(/[^/]*$/, 'std.xcb');
×
11847
        } else if (is_dev()) {
×
11848
            url = `https://cdn.jsdelivr.net/gh/jcubic/lips@devel/${std}`;
×
11849
        } else {
×
11850
            url = `https://cdn.jsdelivr.net/npm/@jcubic/lips@${lips.version}/${std}`;
×
11851
        }
×
11852
    }
×
11853
    global_env.set('__dirname', url.replace(/[^/]+$/, ''));
×
11854
    var load = global_env.get('load');
×
11855
    return load.call(user_env, url, global_env);
×
11856
}
×
11857
// -------------------------------------------------------------------------
1✔
UNCOV
11858
function Worker(url) {
×
11859
    this.url = url;
×
11860
    const worker = this.worker = fworker(function() {
×
11861
        var interpreter;
×
UNCOV
11862
        var init;
×
UNCOV
11863
        // string, numbers, booleans
×
UNCOV
11864
        self.addEventListener('message', function(response) {
×
UNCOV
11865
            var data = response.data;
×
UNCOV
11866
            var id = data.id;
×
UNCOV
11867
            if (data.type !== 'RPC' || id === null) {
×
11868
                return;
×
11869
            }
×
11870
            function send_result(result) {
×
11871
                self.postMessage({ id: id, type: 'RPC', result: result });
×
11872
            }
×
11873
            function send_error(message) {
×
11874
                self.postMessage({ id: id, type: 'RPC', error: message });
×
11875
            }
×
11876
            if (data.method === 'eval') {
×
11877
                if (!init) {
×
11878
                    send_error('Worker RPC: LIPS not initialized, call init first');
×
11879
                    return;
×
UNCOV
11880
                }
×
UNCOV
11881
                init.then(function() {
×
UNCOV
11882
                    // we can use ES6 inside function that's converted to blob
×
UNCOV
11883
                    var code = data.params[0];
×
UNCOV
11884
                    var use_dynamic = data.params[1];
×
UNCOV
11885
                    interpreter.exec(code, { use_dynamic }).then(function(result) {
×
11886
                        result = result.map(function(value) {
×
11887
                            return value && value.valueOf();
×
11888
                        });
×
11889
                        send_result(result);
×
11890
                    }).catch(error => {
×
11891
                        send_error(error);
×
11892
                    });
×
11893
                });
×
11894
            } else if (data.method === 'init') {
×
11895
                var url = data.params[0];
×
11896
                if (typeof url !== 'string') {
×
11897
                    send_error('Worker RPC: url is not a string');
×
11898
                } else {
×
11899
                    importScripts(`${url}/dist/lips.min.js`);
×
11900
                    interpreter = new lips.Interpreter('worker');
×
UNCOV
11901
                    init = bootstrap(url);
×
11902
                    init.then(() => {
×
11903
                        send_result(true);
×
11904
                    });
×
11905
                }
×
11906
            }
×
11907
        });
×
11908
    });
×
11909
    this.rpc = (function() {
×
11910
        var id = 0;
×
11911
        return function rpc(method, params) {
×
11912
            var _id = ++id;
×
11913
            return new Promise(function(resolve, reject) {
×
11914
                worker.addEventListener('message', function handler(response) {
×
11915
                    var data = response.data;
×
11916
                    if (data && data.type === 'RPC' && data.id === _id) {
×
11917
                        if (data.error) {
×
11918
                            reject(data.error);
×
11919
                        } else {
×
11920
                            resolve(data.result);
×
11921
                        }
×
11922
                        worker.removeEventListener('message', handler);
×
11923
                    }
×
11924
                });
×
11925
                worker.postMessage({
×
11926
                    type: 'RPC',
×
11927
                    method: method,
×
11928
                    id: _id,
×
11929
                    params: params
×
11930
                });
×
11931
            });
×
11932
        };
×
11933
    })();
×
11934
    this.rpc('init', [url]).catch((error) => {
×
11935
        console.error(error);
×
11936
    });
×
11937
    this.exec = function(code, { use_dynamic = false }) {
×
11938
        return this.rpc('eval', [code, use_dynamic]);
×
11939
    };
×
11940
}
×
11941

1✔
11942
// -------------------------------------------------------------------------
1✔
11943
// :: Serialization
1✔
11944
// -------------------------------------------------------------------------
1✔
11945
var serialization_map = {
1✔
11946
    'pair': ([car, cdr]) => Pair(car, cdr),
1✔
11947
    'number': function(value) {
1✔
11948
        if (LString.isString(value)) {
292✔
11949
            return LNumber([value, 10]);
282✔
11950
        }
282✔
11951
        return LNumber(value);
10✔
11952
    },
1✔
11953
    'regex': function([pattern, flag]) {
1✔
11954
        return new RegExp(pattern, flag);
8✔
11955
    },
1✔
11956
    'nil': function() {
1✔
11957
        return nil;
6,188✔
11958
    },
1✔
11959
    'symbol': function(value) {
1✔
11960
        if (LString.isString(value)) {
10,208✔
11961
            return LSymbol(value);
10,208✔
11962
        } else if (Array.isArray(value)) {
10,208!
11963
            return LSymbol(Symbol.for(value[0]));
×
11964
        }
×
11965
    },
1✔
11966
    'string': LString,
1✔
11967
    'character': LCharacter
1✔
11968
};
1✔
11969
// -------------------------------------------------------------------------
1✔
11970
// class mapping to create smaller JSON
1✔
11971
const available_class = Object.keys(serialization_map);
1✔
11972
const class_map = {};
1✔
11973
for (let [i, cls] of Object.entries(available_class)) {
1✔
11974
    class_map[cls] = +i;
7✔
11975
}
7✔
11976
function mangle_name(name) {
50✔
11977
    return class_map[name];
50✔
11978
}
50✔
11979
function resolve_name(i) {
19✔
11980
    return available_class[i];
19✔
11981
}
19✔
11982
// -------------------------------------------------------------------------
1✔
11983
function serialize(data) {
1✔
11984
    return JSON.stringify(data, function(key, value) {
1✔
11985
        const v0 = this[key];
59✔
11986
        if (v0) {
59✔
11987
            if (v0 instanceof RegExp) {
50✔
11988
                return {
1✔
11989
                    '@': mangle_name('regex'),
1✔
11990
                    '#': [v0.source, v0.flags]
1✔
11991
                };
1✔
11992
            }
1✔
11993
            var cls = mangle_name(v0.constructor.__class__);
49✔
11994
            if (!is_undef(cls)) {
50✔
11995
                return {
18✔
11996
                    '@': cls,
18✔
11997
                    '#': v0.serialize()
18✔
11998
                };
18✔
11999
            }
18✔
12000
        }
50✔
12001
        return value;
40✔
12002
    });
1✔
12003
}
1✔
12004
// -------------------------------------------------------------------------
1✔
12005
function unserialize(string) {
1✔
12006
    return JSON.parse(string, (_, object) => {
1✔
12007
        if (object && typeof object === 'object') {
59✔
12008
            if (!is_undef(object['@'])) {
29✔
12009
                var cls = resolve_name(object['@']);
19✔
12010
                if (serialization_map[cls]) {
19✔
12011
                    return serialization_map[cls](object['#']);
19✔
12012
                }
19✔
12013
            }
19✔
12014
        }
29✔
12015
        return object;
40✔
12016
    });
1✔
12017
}
1✔
12018

1✔
12019
// -------------------------------------------------------------------------
1✔
12020
// binary serialization using CBOR binary data format
1✔
12021
// -------------------------------------------------------------------------
1✔
12022
const cbor = (function() {
1✔
12023

1✔
12024
    var types = {
1✔
12025
        'pair': Pair,
1✔
12026
        'symbol': LSymbol,
1✔
12027
        'number': LNumber,
1✔
12028
        'string': LString,
1✔
12029
        'character': LCharacter,
1✔
12030
        'nil': nil.constructor,
1✔
12031
        'regex': RegExp
1✔
12032
    };
1✔
12033

1✔
12034
    function serializer(Class, fn) {
1✔
12035
        return {
7✔
12036
            deserialize: fn,
7✔
12037
            Class
7✔
12038
        };
7✔
12039
    }
7✔
12040

1✔
12041
    var encoder = new Encoder();
1✔
12042

1✔
12043
    const cbor_serialization_map = {};
1✔
12044
    for (const [ name, fn ] of Object.entries(serialization_map)) {
1✔
12045
        const Class = types[name];
7✔
12046
        cbor_serialization_map[name] = serializer(Class, fn);
7✔
12047
    }
7✔
12048
    // add CBOR data mapping
1✔
12049
    let tag = 43311;
1✔
12050
    Object.keys(cbor_serialization_map).forEach(type => {
1✔
12051
        const data = cbor_serialization_map[type];
7✔
12052
        if (typeof data === 'function') {
7!
UNCOV
12053
            const Class = data;
×
UNCOV
12054
            addExtension({
×
UNCOV
12055
                Class,
×
UNCOV
12056
                tag,
×
UNCOV
12057
                encode(instance, encode) {
×
UNCOV
12058
                    encode(instance.serialize());
×
UNCOV
12059
                },
×
UNCOV
12060
                decode(data) {
×
UNCOV
12061
                    return new Class(data);
×
UNCOV
12062
                }
×
UNCOV
12063
            });
×
12064
        } else {
7✔
12065
            const { deserialize, Class } = data;
7✔
12066
            addExtension({
7✔
12067
                Class,
7✔
12068
                tag,
7✔
12069
                encode(instance, encode) {
7✔
UNCOV
12070
                    if (instance instanceof RegExp) {
×
UNCOV
12071
                        return encode([instance.source, instance.flags]);
×
UNCOV
12072
                    }
×
UNCOV
12073
                    encode(instance.serialize());
×
12074
                },
7✔
12075
                decode(data) {
7✔
12076
                    return deserialize(data);
34,995✔
12077
                }
34,995✔
12078
            });
7✔
12079
        }
7✔
12080
        tag++;
7✔
12081
    });
1✔
12082
    return encoder;
1✔
12083
})();
1✔
12084

1✔
12085
// -------------------------------------------------------------------------
1✔
UNCOV
12086
function merge_uint8_array(...args) {
×
UNCOV
12087
    if (args.length > 1) {
×
UNCOV
12088
        const len = args.reduce((acc, arr) => acc + arr.length, 0);
×
UNCOV
12089
        const result = new Uint8Array(len);
×
UNCOV
12090
        let offset = 0;
×
UNCOV
12091
        args.forEach(item => {
×
UNCOV
12092
            result.set(item, offset);
×
UNCOV
12093
            offset += item.length;
×
UNCOV
12094
        });
×
UNCOV
12095
        return result;
×
UNCOV
12096
    } else if (args.length) {
×
12097
        return args[0];
×
12098
    }
×
12099
}
×
12100

1✔
12101
// -------------------------------------------------------------------------
1✔
12102
function encode_magic() {
×
12103
    const VERSION = 1;
×
12104
    const encoder = new TextEncoder('utf-8');
×
12105
    return encoder.encode(`LIPS${VERSION.toString().padStart(3, ' ')}`);
×
12106
}
×
12107

1✔
12108
// -------------------------------------------------------------------------
1✔
12109
const MAGIC_LENGTH = 7;
1✔
12110

1✔
12111
// -------------------------------------------------------------------------
1✔
12112
function decode_magic(obj) {
1✔
12113
    const decoder = new TextDecoder('utf-8');
1✔
12114
    const prefix = decoder.decode(obj.slice(0, MAGIC_LENGTH));
1✔
12115
    const name = prefix.substring(0, 4);
1✔
12116
    if (name === 'LIPS') {
1✔
12117
        const m = prefix.match(/^(....).*([0-9]+)$/);
1✔
12118
        if (m) {
1✔
12119
            return {
1✔
12120
                type: m[1],
1✔
12121
                version: Number(m[2])
1✔
12122
            };
1✔
12123
        }
1✔
12124
    }
1✔
UNCOV
12125
    return {
×
UNCOV
12126
        type: 'unknown'
×
UNCOV
12127
    };
×
12128
}
1✔
12129

1✔
12130
// -------------------------------------------------------------------------
1✔
12131
function serialize_bin(obj) {
×
12132
    const magic = encode_magic();
×
12133
    const payload = cbor.encode(obj);
×
12134
    return merge_uint8_array(magic, pack(payload, { magic: false }));
×
12135
}
×
12136

1✔
12137
// -------------------------------------------------------------------------
1✔
12138
function unserialize_bin(data) {
1✔
12139
    const { type, version } = decode_magic(data);
1✔
12140
    if (type === 'LIPS' && version === 1) {
1✔
12141
        const arr = unpack(data.slice(MAGIC_LENGTH), { magic: false });
1✔
12142
        return cbor.decode(arr);
1✔
12143
    } else {
1!
UNCOV
12144
        throw new Error(`Invalid file format ${type}`);
×
UNCOV
12145
    }
×
12146
}
1✔
12147

1✔
12148
// -------------------------------------------------------------------------
1✔
12149
function execError(e) {
×
12150
    console.error(e.message || e);
×
UNCOV
12151
    if (Array.isArray(e.code)) {
×
UNCOV
12152
        console.error(e.code.map((line, i) => `[${i + 1}]: ${line}`));
×
UNCOV
12153
    }
×
UNCOV
12154
}
×
12155

1✔
12156
// -------------------------------------------------------------------------
1✔
UNCOV
12157
function init() {
×
UNCOV
12158
    var lips_mimes = ['text/x-lips', 'text/x-scheme'];
×
UNCOV
12159
    var bootstrapped;
×
UNCOV
12160
    function boostrap_attr(script) {
×
UNCOV
12161
        return script.getAttribute('data-bootstrap') ?? script.getAttribute('bootstrap');
×
UNCOV
12162
    }
×
UNCOV
12163
    function load(script) {
×
UNCOV
12164
        return new Promise(function(resolve) {
×
UNCOV
12165
            var src = script.getAttribute('src');
×
UNCOV
12166
            if (src) {
×
UNCOV
12167
                return fetch(src).then(res => res.text())
×
UNCOV
12168
                    .then(exec).then(resolve).catch((e) => {
×
12169
                        execError(e);
×
12170
                        resolve();
×
12171
                    });
×
UNCOV
12172
            } else {
×
UNCOV
12173
                return exec(script.innerHTML).then(resolve).catch((e) => {
×
UNCOV
12174
                    execError(e);
×
12175
                    resolve();
×
12176
                });
×
12177
            }
×
12178
        });
×
12179
    }
×
UNCOV
12180

×
UNCOV
12181
    function loop() {
×
UNCOV
12182
        return new Promise(function(resolve) {
×
UNCOV
12183
            var scripts = Array.from(document.querySelectorAll('script'));
×
UNCOV
12184
            return (function loop() {
×
UNCOV
12185
                var script = scripts.shift();
×
UNCOV
12186
                if (!script) {
×
UNCOV
12187
                    resolve();
×
12188
                } else {
×
12189
                    var type = script.getAttribute('type');
×
UNCOV
12190
                    if (lips_mimes.includes(type)) {
×
UNCOV
12191
                        const attr = boostrap_attr(script);
×
UNCOV
12192
                        if (!bootstrapped && typeof attr === 'string') {
×
12193
                            return bootstrap(attr).then(function() {
×
12194
                                return load(script, function(e) {
×
12195
                                    console.error(e);
×
12196
                                });
×
12197
                            }).then(loop);
×
12198
                        } else {
×
UNCOV
12199
                            return load(script, function(e) {
×
UNCOV
12200
                                console.error(e);
×
12201
                            }).then(loop);
×
12202
                        }
×
12203
                    } else if (type && type.match(/lips|lisp/)) {
×
12204
                        console.warn('Expecting ' + lips_mimes.join(' or ') +
×
12205
                                     ' found ' + type);
×
12206
                    }
×
12207
                    return loop();
×
12208
                }
×
12209
            })();
×
12210
        });
×
12211
    }
×
12212
    if (!window.document) {
×
12213
        return Promise.resolve();
×
12214
    } else if (currentScript) {
×
12215
        var script = currentScript;
×
12216
        const attr = boostrap_attr(script);
×
12217
        if (typeof attr === 'string') {
×
12218
            return bootstrap(attr).then(function() {
×
12219
                bootstrapped = true;
×
12220
                return loop();
×
12221
            });
×
12222
        }
×
12223
    }
×
12224
    return loop();
×
12225
}
×
12226
// this can't be in init function, because it need to be in script context
1✔
12227
const currentScript = typeof window !== 'undefined' &&
1!
12228
      window.document && document.currentScript;
1!
12229
// -------------------------------------------------------------------------
1✔
12230
if (typeof window !== 'undefined') {
1!
12231
    contentLoaded(window, init);
×
12232
}
×
12233
// -------------------------------------------------------------------------
1✔
12234
var banner = (function() {
1✔
12235
    // Rollup tree-shaking is removing the variable if it's normal string because
1✔
12236
    // obviously '{{DATE}}' == '{{' + 'DATE}}'; can be removed
1✔
12237
    // but disabling Tree-shaking is adding lot of not used code so we use this
1✔
12238
    // hack instead
1✔
12239
    var date = LString('{{DATE}}').valueOf();
1✔
12240
    var _date = date === '{{' + 'DATE}}' ? new Date() : new Date(date);
1!
12241
    var _format = x => x.toString().padStart(2, '0');
1✔
12242
    var _year = _date.getFullYear();
1✔
12243
    var _build = [
1✔
12244
        _year,
1✔
12245
        _format(_date.getMonth() + 1),
1✔
12246
        _format(_date.getDate())
1✔
12247
    ].join('-');
1✔
12248
    var banner = `
1✔
12249
  __ __                          __
1✔
12250
 / / \\ \\       _    _  ___  ___  \\ \\
1✔
12251
| |   \\ \\     | |  | || . \\/ __>  | |
1✔
12252
| |    > \\    | |_ | ||  _/\\__ \\  | |
1✔
12253
| |   / ^ \\   |___||_||_|  <___/  | |
1✔
12254
 \\_\\ /_/ \\_\\                     /_/
1✔
12255

1✔
12256
LIPS Interpreter {{VER}} (${_build}) <https://lips.js.org>
1✔
12257
Copyright (c) 2018-${_year} Jakub T. Jankiewicz
1✔
12258

1✔
12259
Type (env) to see environment with functions macros and variables. You can also
1✔
12260
use (help name) to display help for specific function or macro, (apropos name)
1✔
12261
to display list of matched names in environment and (dir object) to list
1✔
12262
properties of an object.
1✔
12263
`.replace(/^.*\n/, '');
1✔
12264
    return banner;
1✔
12265
})();
1✔
12266
// -------------------------------------------------------------------------
1✔
12267
// to be used with string function when code is minified
1✔
12268
// -------------------------------------------------------------------------
1✔
12269
read_only(Ahead, '__class__', 'ahead');
1✔
12270
read_only(Pair, '__class__', 'pair');
1✔
12271
read_only(Nil, '__class__', 'nil');
1✔
12272
read_only(Pattern, '__class__', 'pattern');
1✔
12273
read_only(Formatter, '__class__', 'formatter');
1✔
12274
read_only(Macro, '__class__', 'macro');
1✔
12275
read_only(Syntax, '__class__', 'syntax');
1✔
12276
read_only(Syntax.Parameter, '__class__', 'syntax-parameter');
1✔
12277
read_only(Environment, '__class__', 'environment');
1✔
12278
read_only(InputPort, '__class__', 'input-port');
1✔
12279
read_only(OutputPort, '__class__', 'output-port');
1✔
12280
read_only(BufferedOutputPort, '__class__', 'output-port');
1✔
12281
read_only(OutputStringPort, '__class__', 'output-string-port');
1✔
12282
read_only(InputStringPort, '__class__', 'input-string-port');
1✔
12283
read_only(InputFilePort, '__class__', 'input-file-port');
1✔
12284
read_only(OutputFilePort, '__class__', 'output-file-port');
1✔
12285
read_only(LipsError, '__class__', 'lips-error');
1✔
12286
[LNumber, LComplex, LRational, LFloat, LBigInteger].forEach(cls => {
1✔
12287
    read_only(cls, '__class__', 'number');
5✔
12288
});
1✔
12289
read_only(LCharacter, '__class__', 'character');
1✔
12290
read_only(LSymbol, '__class__', 'symbol');
1✔
12291
read_only(LString, '__class__', 'string');
1✔
12292
read_only(QuotedPromise, '__class__', 'promise');
1✔
12293
read_only(Parameter, '__class__', 'parameter');
1✔
12294
// -------------------------------------------------------------------------
1✔
12295
const version = '{{VER}}';
1✔
12296
const date = '{{DATE}}';
1✔
12297

1✔
12298
// unwrap async generator into Promise<Array>
1✔
12299
const parse = compose(uniterate_async, _parse);
1✔
12300

1✔
12301
export {
1✔
12302
    version,
1✔
12303
    banner,
1✔
12304
    date,
1✔
12305
    exec,
1✔
12306
    parse,
1✔
12307
    tokenize,
1✔
12308
    evaluate,
1✔
12309
    compile,
1✔
12310

1✔
12311
    serialize,
1✔
12312
    unserialize,
1✔
12313

1✔
12314
    box,
1✔
12315
    unbox,
1✔
12316

1✔
12317
    serialize_bin,
1✔
12318
    unserialize_bin,
1✔
12319

1✔
12320
    bootstrap,
1✔
12321

1✔
12322
    Environment,
1✔
12323
    user_env as env,
1✔
12324

1✔
12325
    Worker,
1✔
12326

1✔
12327
    Interpreter,
1✔
12328
    balanced as balanced_parenthesis,
1✔
12329
    balanced as balancedParenthesis,
1✔
12330
    balanced,
1✔
12331

1✔
12332
    Macro,
1✔
12333
    Syntax,
1✔
12334
    Pair,
1✔
12335
    Values,
1✔
12336
    QuotedPromise,
1✔
12337
    LipsError as Error,
1✔
12338
    is_directive as _is_directive,
1✔
12339

1✔
12340
    quote,
1✔
12341

1✔
12342
    InputPort,
1✔
12343
    OutputPort,
1✔
12344
    BufferedOutputPort,
1✔
12345
    InputFilePort,
1✔
12346
    OutputFilePort,
1✔
12347
    InputStringPort,
1✔
12348
    OutputStringPort,
1✔
12349
    InputByteVectorPort,
1✔
12350
    OutputByteVectorPort,
1✔
12351
    InputBinaryFilePort,
1✔
12352
    OutputBinaryFilePort,
1✔
12353
    set_fs,
1✔
12354

1✔
12355
    Formatter,
1✔
12356
    Parser,
1✔
12357
    Lexer,
1✔
12358
    specials,
1✔
12359
    repr,
1✔
12360
    nil,
1✔
12361
    eof,
1✔
12362

1✔
12363
    LSymbol,
1✔
12364
    LNumber,
1✔
12365
    LFloat,
1✔
12366
    LComplex,
1✔
12367
    LRational,
1✔
12368
    LBigInteger,
1✔
12369
    LCharacter,
1✔
12370
    LString,
1✔
12371
    Parameter,
1✔
12372
    rationalize
1✔
12373
};
1✔
12374
const lips = {
1✔
12375
    version,
1✔
12376
    banner,
1✔
12377
    date,
1✔
12378
    exec,
1✔
12379
    parse,
1✔
12380
    tokenize,
1✔
12381
    evaluate,
1✔
12382
    compile,
1✔
12383

1✔
12384
    serialize,
1✔
12385
    unserialize,
1✔
12386

1✔
12387
    box,
1✔
12388
    unbox,
1✔
12389

1✔
12390
    serialize_bin,
1✔
12391
    unserialize_bin,
1✔
12392

1✔
12393
    bootstrap,
1✔
12394

1✔
12395
    Environment,
1✔
12396
    env: user_env,
1✔
12397

1✔
12398
    Worker,
1✔
12399

1✔
12400
    Interpreter,
1✔
12401
    balanced_parenthesis: balanced,
1✔
12402
    balancedParenthesis: balanced,
1✔
12403
    balanced,
1✔
12404

1✔
12405
    Macro,
1✔
12406
    Syntax,
1✔
12407
    Pair,
1✔
12408
    Values,
1✔
12409
    QuotedPromise,
1✔
12410
    Error: LipsError,
1✔
12411

1✔
12412
    quote,
1✔
12413

1✔
12414
    InputPort,
1✔
12415
    OutputPort,
1✔
12416
    BufferedOutputPort,
1✔
12417
    InputFilePort,
1✔
12418
    OutputFilePort,
1✔
12419
    InputStringPort,
1✔
12420
    OutputStringPort,
1✔
12421
    InputByteVectorPort,
1✔
12422
    OutputByteVectorPort,
1✔
12423
    InputBinaryFilePort,
1✔
12424
    OutputBinaryFilePort,
1✔
12425
    set_fs,
1✔
12426

1✔
12427
    Formatter,
1✔
12428
    Parser,
1✔
12429
    Lexer,
1✔
12430
    specials,
1✔
12431
    repr,
1✔
12432
    nil,
1✔
12433
    eof,
1✔
12434

1✔
12435
    LSymbol,
1✔
12436
    LNumber,
1✔
12437
    LFloat,
1✔
12438
    LComplex,
1✔
12439
    LRational,
1✔
12440
    LBigInteger,
1✔
12441
    LCharacter,
1✔
12442
    LString,
1✔
12443
    Parameter,
1✔
12444
    rationalize
1✔
12445
};
1✔
12446
export default lips;
1✔
12447
global_env.set('lips', lips);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc