xbintsc 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/ast/expressions.d.ts +8 -0
- package/dist/src/binder/binder.js +1 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/references.d.ts +1 -1
- package/dist/src/binder/references.js +34 -5
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/binder/types.d.ts +4 -0
- package/dist/src/binder/types.js.map +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +9 -1
- package/dist/src/codegen/generator/calls/access.js +146 -13
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
- package/dist/src/codegen/generator/calls/closures.js +25 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +12 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/calls/literals.js +12 -4
- package/dist/src/codegen/generator/calls/literals.js.map +1 -1
- package/dist/src/codegen/generator/calls/super.js +4 -7
- package/dist/src/codegen/generator/calls/super.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +21 -2
- package/dist/src/codegen/generator/context.js +42 -5
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.js +1 -3
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
- package/dist/src/codegen/generator/expressions/primary.js +99 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.js +25 -15
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +15 -0
- package/dist/src/codegen/generator/state.js +28 -1
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
- package/dist/src/codegen/generator/statements/control-flow.js +85 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +15 -6
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
- package/dist/src/codegen/generator/statements/loops.js +25 -11
- package/dist/src/codegen/generator/statements/loops.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +41 -1
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +1 -1
- package/dist/src/driver/compiler.js +33 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +4 -3
- package/dist/src/driver/modules.js +61 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/toolchain.js +1 -1
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.js +4 -0
- package/dist/src/parser/declarations/classes.js.map +1 -1
- package/dist/src/parser/declarations/type-members.js.map +1 -1
- package/dist/src/parser/expressions.d.ts +2 -1
- package/dist/src/parser/expressions.js +10 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -2
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +79 -6
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/parser.js +0 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/statements.d.ts +1 -0
- package/dist/src/parser/statements.js +13 -1
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +2 -2
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/ast/factory.test.d.ts +1 -0
- package/dist/tests/ast/factory.test.js +35 -0
- package/dist/tests/ast/factory.test.js.map +1 -0
- package/dist/tests/ast/visitor.test.d.ts +1 -0
- package/dist/tests/ast/visitor.test.js +53 -0
- package/dist/tests/ast/visitor.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +109 -9
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.d.ts +1 -0
- package/dist/tests/codegen/closures.test.js +32 -0
- package/dist/tests/codegen/closures.test.js.map +1 -0
- package/dist/tests/codegen/expressions.test.d.ts +1 -0
- package/dist/tests/codegen/expressions.test.js +149 -0
- package/dist/tests/codegen/expressions.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.js +14 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/codegen/values.test.d.ts +1 -0
- package/dist/tests/codegen/values.test.js +48 -0
- package/dist/tests/codegen/values.test.js.map +1 -0
- package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
- package/dist/tests/diagnostics/diagnostic.test.js +117 -0
- package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
- package/dist/tests/diagnostics/source.test.d.ts +1 -0
- package/dist/tests/diagnostics/source.test.js +58 -0
- package/dist/tests/diagnostics/source.test.js.map +1 -0
- package/dist/tests/driver/modules.test.d.ts +1 -0
- package/dist/tests/driver/modules.test.js +140 -0
- package/dist/tests/driver/modules.test.js.map +1 -0
- package/dist/tests/driver/paths.test.js +17 -1
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
- package/dist/tests/driver/runtime-lib.test.js +19 -0
- package/dist/tests/driver/runtime-lib.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +91 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +11 -1
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +14 -1
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +465 -2
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/differential.test.d.ts +7 -0
- package/dist/tests/e2e/differential.test.js +581 -0
- package/dist/tests/e2e/differential.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +21 -0
- package/dist/tests/e2e/harness.js +26 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/extensions/node.test.d.ts +1 -0
- package/dist/tests/extensions/node.test.js +32 -0
- package/dist/tests/extensions/node.test.js.map +1 -0
- package/dist/tests/lexer/identifiers.test.d.ts +1 -0
- package/dist/tests/lexer/identifiers.test.js +13 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -0
- package/dist/tests/lexer/numbers.test.d.ts +1 -0
- package/dist/tests/lexer/numbers.test.js +23 -0
- package/dist/tests/lexer/numbers.test.js.map +1 -0
- package/dist/tests/lexer/strings.test.d.ts +1 -0
- package/dist/tests/lexer/strings.test.js +72 -0
- package/dist/tests/lexer/strings.test.js.map +1 -0
- package/dist/tests/parser/declarations.test.d.ts +1 -0
- package/dist/tests/parser/declarations.test.js +70 -0
- package/dist/tests/parser/declarations.test.js.map +1 -0
- package/dist/tests/parser/expressions.test.d.ts +1 -0
- package/dist/tests/parser/expressions.test.js +47 -0
- package/dist/tests/parser/expressions.test.js.map +1 -0
- package/dist/tests/parser/helpers.test.d.ts +1 -0
- package/dist/tests/parser/helpers.test.js +58 -0
- package/dist/tests/parser/helpers.test.js.map +1 -0
- package/dist/tests/parser/literals.test.d.ts +1 -0
- package/dist/tests/parser/literals.test.js +70 -0
- package/dist/tests/parser/literals.test.js.map +1 -0
- package/dist/tests/parser/module-syntax.test.d.ts +1 -0
- package/dist/tests/parser/module-syntax.test.js +91 -0
- package/dist/tests/parser/module-syntax.test.js.map +1 -0
- package/dist/tests/parser/statements.test.d.ts +1 -0
- package/dist/tests/parser/statements.test.js +85 -0
- package/dist/tests/parser/statements.test.js.map +1 -0
- package/dist/tests/parser/type-members.test.d.ts +1 -0
- package/dist/tests/parser/type-members.test.js +86 -0
- package/dist/tests/parser/type-members.test.js.map +1 -0
- package/dist/tests/parser/types.test.d.ts +1 -0
- package/dist/tests/parser/types.test.js +143 -0
- package/dist/tests/parser/types.test.js.map +1 -0
- package/package.json +6 -1
- package/runtime/ext_node/fs/fs_ops.c +36 -1
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +88 -2
- package/runtime/xt_builtins.c +174 -6
- package/runtime/xt_containers.c +234 -42
- package/runtime/xt_generator.c +304 -0
- package/runtime/xt_io.c +170 -30
- package/runtime/xt_promise.c +6 -1
- package/runtime/xt_regex.h +105 -35
- package/runtime/xt_stdlib/dispatch.inc +255 -4
- package/runtime/xt_stdlib/strings.inc +17 -13
- package/runtime/xt_stdlib2/arrays.inc +126 -19
- package/runtime/xt_stdlib2/date.inc +9 -2
- package/runtime/xt_stdlib2/error.inc +162 -12
- package/runtime/xt_stdlib2/helpers.inc +4 -0
- package/runtime/xt_stdlib2/json.inc +19 -6
- package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
- package/runtime/xt_stdlib2/numbers.inc +4 -5
- package/runtime/xt_stdlib2/objects.inc +29 -0
- package/runtime/xt_stdlib2/regexp-match.inc +293 -51
- package/runtime/xt_stdlib2/set.inc +58 -2
- package/runtime/xt_stdlib2/strings.inc +38 -0
- package/runtime/xt_symbol.c +149 -0
- package/runtime/xt_values.c +221 -15
- package/scripts/build-runtime.ts +1 -1
- package/src/ast/expressions.ts +8 -0
- package/src/binder/binder.ts +1 -1
- package/src/binder/references.ts +47 -4
- package/src/binder/types.ts +4 -0
- package/src/cli/main.ts +1 -2
- package/src/codegen/generator/calls/access.ts +158 -13
- package/src/codegen/generator/calls/closures.ts +24 -0
- package/src/codegen/generator/calls/invocation.ts +11 -1
- package/src/codegen/generator/calls/literals.ts +10 -4
- package/src/codegen/generator/calls/super.ts +6 -9
- package/src/codegen/generator/context.ts +44 -7
- package/src/codegen/generator/expressions/assignment.ts +1 -3
- package/src/codegen/generator/expressions/primary.ts +108 -2
- package/src/codegen/generator/generator.ts +0 -1
- package/src/codegen/generator/module.ts +24 -18
- package/src/codegen/generator/state.ts +44 -1
- package/src/codegen/generator/statements/control-flow.ts +86 -0
- package/src/codegen/generator/statements/dispatch.ts +18 -6
- package/src/codegen/generator/statements/loops.ts +29 -11
- package/src/codegen/generator/tables.ts +44 -1
- package/src/driver/compiler.ts +32 -2
- package/src/driver/modules.ts +64 -4
- package/src/driver/toolchain.ts +1 -1
- package/src/lexer/scanner.ts +0 -1
- package/src/parser/declarations/classes.ts +4 -0
- package/src/parser/declarations/type-members.ts +0 -2
- package/src/parser/expressions.ts +13 -1
- package/src/parser/helpers.ts +2 -2
- package/src/parser/literals.ts +82 -7
- package/src/parser/modules.ts +0 -1
- package/src/parser/parser.ts +0 -1
- package/src/parser/statements.ts +14 -0
- package/src/parser/types.ts +2 -2
package/runtime/xt_regex.h
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* literals, '.', '^', '$', '[abc]', '[^abc]', '[a-z]', '*', '+', '?',
|
|
8
8
|
* alternation '|', groups '(...)', and escapes ('\d', '\w', '\s', ...).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Capturing groups are reported through `regmatch_t` the same way POSIX does:
|
|
11
|
+
* `pmatch[0]` is the whole match and `pmatch[k]` the k-th group in pattern
|
|
12
|
+
* order, with unmatched groups left at -1. The implementation is a recursive
|
|
13
|
+
* backtracking matcher over a small AST; captures are carried on an immutable
|
|
14
|
+
* chain of stack frames so abandoned search paths are undone automatically.
|
|
14
15
|
* Patterns are tiny, so this is fast enough and keeps the runtime free of an
|
|
15
16
|
* external dependency.
|
|
16
17
|
*/
|
|
@@ -56,6 +57,7 @@ typedef struct {
|
|
|
56
57
|
unsigned char ch;
|
|
57
58
|
unsigned char *classbits; /* 256 bits == 32 bytes, for XT_RE_CLASS */
|
|
58
59
|
xt_re_alt *group; /* for XT_RE_GROUP */
|
|
60
|
+
int groupIndex; /* 1-based capture number, for XT_RE_GROUP */
|
|
59
61
|
} xt_re_node;
|
|
60
62
|
|
|
61
63
|
typedef struct {
|
|
@@ -155,6 +157,7 @@ typedef struct {
|
|
|
155
157
|
const char *s;
|
|
156
158
|
size_t i;
|
|
157
159
|
int error;
|
|
160
|
+
int groupCount;
|
|
158
161
|
} xt_re_parser;
|
|
159
162
|
|
|
160
163
|
static void xt_re_free_alt(xt_re_alt *alt);
|
|
@@ -293,7 +296,11 @@ static xt_re_node *xt_re_parse_atom(xt_re_parser *p) {
|
|
|
293
296
|
if (c == '(') {
|
|
294
297
|
xt_re_node *n;
|
|
295
298
|
xt_re_alt *group;
|
|
299
|
+
int index;
|
|
296
300
|
p->i++;
|
|
301
|
+
/* Number the group as soon as its '(' is seen so nested groups keep
|
|
302
|
+
* POSIX/JavaScript numbering (opening-paren order). */
|
|
303
|
+
index = ++p->groupCount;
|
|
297
304
|
group = xt_re_parse_alt(p);
|
|
298
305
|
if (p->error) return NULL;
|
|
299
306
|
if (p->s[p->i] != ')') {
|
|
@@ -308,6 +315,7 @@ static xt_re_node *xt_re_parse_atom(xt_re_parser *p) {
|
|
|
308
315
|
return NULL;
|
|
309
316
|
}
|
|
310
317
|
n->group = group;
|
|
318
|
+
n->groupIndex = index;
|
|
311
319
|
return n;
|
|
312
320
|
}
|
|
313
321
|
if (c == '[') return xt_re_parse_class(p);
|
|
@@ -388,7 +396,17 @@ typedef struct {
|
|
|
388
396
|
int icase;
|
|
389
397
|
} xt_re_input;
|
|
390
398
|
|
|
391
|
-
|
|
399
|
+
/* One capture on the immutable chain of the current search path. `parent`
|
|
400
|
+
* points to the captures that were live when the group was entered, so an
|
|
401
|
+
* abandoned backtrack simply drops the frame instead of restoring it. */
|
|
402
|
+
typedef struct xt_re_cap {
|
|
403
|
+
const struct xt_re_cap *parent;
|
|
404
|
+
int index; /* 1-based capture group number */
|
|
405
|
+
int start;
|
|
406
|
+
int end;
|
|
407
|
+
} xt_re_cap;
|
|
408
|
+
|
|
409
|
+
typedef int (*xt_re_cont)(void *ctx, size_t pos, const xt_re_cap *caps);
|
|
392
410
|
|
|
393
411
|
static void xt_re_free_alt(xt_re_alt *alt);
|
|
394
412
|
|
|
@@ -429,28 +447,54 @@ static int xt_re_class_match(const unsigned char *bits, unsigned char c, int ica
|
|
|
429
447
|
}
|
|
430
448
|
|
|
431
449
|
static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
|
|
432
|
-
xt_re_cont cont, void *ctx);
|
|
450
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps);
|
|
451
|
+
|
|
452
|
+
/* Resumes the outer continuation once a group's body has matched, recording
|
|
453
|
+
* where the group ended on the frame it pushed. */
|
|
454
|
+
typedef struct {
|
|
455
|
+
xt_re_cap *frame;
|
|
456
|
+
xt_re_cont cont;
|
|
457
|
+
void *ctx;
|
|
458
|
+
} xt_re_group_state;
|
|
459
|
+
|
|
460
|
+
static int xt_re_group_next(void *v, size_t pos, const xt_re_cap *caps) {
|
|
461
|
+
xt_re_group_state *s = (xt_re_group_state *)v;
|
|
462
|
+
/* `caps` already heads the chain that includes this group's own frame (and
|
|
463
|
+
* any groups nested inside it), so forward it as-is. */
|
|
464
|
+
s->frame->end = (int)pos;
|
|
465
|
+
return s->cont(s->ctx, pos, caps);
|
|
466
|
+
}
|
|
433
467
|
|
|
434
468
|
static int xt_re_match_atom(xt_re_node *node, const xt_re_input *in, size_t pos,
|
|
435
|
-
xt_re_cont cont, void *ctx) {
|
|
469
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps) {
|
|
436
470
|
switch (node->type) {
|
|
437
471
|
case XT_RE_CHAR:
|
|
438
472
|
if (pos < in->len && xt_re_char_eq(in, (unsigned char)in->text[pos], node->ch))
|
|
439
|
-
return cont(ctx, pos + 1);
|
|
473
|
+
return cont(ctx, pos + 1, caps);
|
|
440
474
|
return 0;
|
|
441
475
|
case XT_RE_ANY:
|
|
442
|
-
if (pos < in->len && in->text[pos] != '\n') return cont(ctx, pos + 1);
|
|
476
|
+
if (pos < in->len && in->text[pos] != '\n') return cont(ctx, pos + 1, caps);
|
|
443
477
|
return 0;
|
|
444
478
|
case XT_RE_CLASS:
|
|
445
479
|
if (pos < in->len && xt_re_class_match(node->classbits, (unsigned char)in->text[pos], in->icase))
|
|
446
|
-
return cont(ctx, pos + 1);
|
|
480
|
+
return cont(ctx, pos + 1, caps);
|
|
447
481
|
return 0;
|
|
448
482
|
case XT_RE_BOL:
|
|
449
|
-
return pos == 0 ? cont(ctx, pos) : 0;
|
|
483
|
+
return pos == 0 ? cont(ctx, pos, caps) : 0;
|
|
450
484
|
case XT_RE_EOL:
|
|
451
|
-
return pos == in->len ? cont(ctx, pos) : 0;
|
|
452
|
-
case XT_RE_GROUP:
|
|
453
|
-
|
|
485
|
+
return pos == in->len ? cont(ctx, pos, caps) : 0;
|
|
486
|
+
case XT_RE_GROUP: {
|
|
487
|
+
xt_re_cap frame;
|
|
488
|
+
xt_re_group_state state;
|
|
489
|
+
frame.parent = caps;
|
|
490
|
+
frame.index = node->groupIndex;
|
|
491
|
+
frame.start = (int)pos;
|
|
492
|
+
frame.end = -1;
|
|
493
|
+
state.frame = &frame;
|
|
494
|
+
state.cont = cont;
|
|
495
|
+
state.ctx = ctx;
|
|
496
|
+
return xt_re_match_alt(node->group, in, pos, xt_re_group_next, &state, &frame);
|
|
497
|
+
}
|
|
454
498
|
default:
|
|
455
499
|
return 0;
|
|
456
500
|
}
|
|
@@ -471,28 +515,28 @@ typedef struct {
|
|
|
471
515
|
int count;
|
|
472
516
|
} xt_re_repeat_step;
|
|
473
517
|
|
|
474
|
-
static int xt_re_repeat_try(xt_re_repeat *rep, size_t pos, int count);
|
|
518
|
+
static int xt_re_repeat_try(xt_re_repeat *rep, size_t pos, int count, const xt_re_cap *caps);
|
|
475
519
|
|
|
476
|
-
static int xt_re_repeat_next(void *v, size_t newpos) {
|
|
520
|
+
static int xt_re_repeat_next(void *v, size_t newpos, const xt_re_cap *caps) {
|
|
477
521
|
xt_re_repeat_step *s = (xt_re_repeat_step *)v;
|
|
478
522
|
if (newpos == s->pos) return 0; /* no progress -> stop to avoid loops */
|
|
479
|
-
return xt_re_repeat_try(s->rep, newpos, s->count + 1);
|
|
523
|
+
return xt_re_repeat_try(s->rep, newpos, s->count + 1, caps);
|
|
480
524
|
}
|
|
481
525
|
|
|
482
|
-
static int xt_re_repeat_try(xt_re_repeat *rep, size_t pos, int count) {
|
|
526
|
+
static int xt_re_repeat_try(xt_re_repeat *rep, size_t pos, int count, const xt_re_cap *caps) {
|
|
483
527
|
if (rep->max < 0 || count < rep->max) {
|
|
484
528
|
xt_re_repeat_step step;
|
|
485
529
|
step.rep = rep;
|
|
486
530
|
step.pos = pos;
|
|
487
531
|
step.count = count;
|
|
488
|
-
if (xt_re_match_atom(rep->atom, rep->in, pos, xt_re_repeat_next, &step)) return 1;
|
|
532
|
+
if (xt_re_match_atom(rep->atom, rep->in, pos, xt_re_repeat_next, &step, caps)) return 1;
|
|
489
533
|
}
|
|
490
|
-
if (count >= rep->min) return rep->cont(rep->ctx, pos);
|
|
534
|
+
if (count >= rep->min) return rep->cont(rep->ctx, pos, caps);
|
|
491
535
|
return 0;
|
|
492
536
|
}
|
|
493
537
|
|
|
494
538
|
static int xt_re_match_piece(xt_re_piece *piece, const xt_re_input *in, size_t pos,
|
|
495
|
-
xt_re_cont cont, void *ctx) {
|
|
539
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps) {
|
|
496
540
|
xt_re_repeat rep;
|
|
497
541
|
switch (piece->quant) {
|
|
498
542
|
case XT_RE_Q_STAR:
|
|
@@ -502,7 +546,7 @@ static int xt_re_match_piece(xt_re_piece *piece, const xt_re_input *in, size_t p
|
|
|
502
546
|
rep.max = -1;
|
|
503
547
|
rep.cont = cont;
|
|
504
548
|
rep.ctx = ctx;
|
|
505
|
-
return xt_re_repeat_try(&rep, pos, 0);
|
|
549
|
+
return xt_re_repeat_try(&rep, pos, 0, caps);
|
|
506
550
|
case XT_RE_Q_PLUS:
|
|
507
551
|
rep.atom = piece->node;
|
|
508
552
|
rep.in = in;
|
|
@@ -510,7 +554,7 @@ static int xt_re_match_piece(xt_re_piece *piece, const xt_re_input *in, size_t p
|
|
|
510
554
|
rep.max = -1;
|
|
511
555
|
rep.cont = cont;
|
|
512
556
|
rep.ctx = ctx;
|
|
513
|
-
return xt_re_repeat_try(&rep, pos, 0);
|
|
557
|
+
return xt_re_repeat_try(&rep, pos, 0, caps);
|
|
514
558
|
case XT_RE_Q_QUEST:
|
|
515
559
|
rep.atom = piece->node;
|
|
516
560
|
rep.in = in;
|
|
@@ -518,9 +562,9 @@ static int xt_re_match_piece(xt_re_piece *piece, const xt_re_input *in, size_t p
|
|
|
518
562
|
rep.max = 1;
|
|
519
563
|
rep.cont = cont;
|
|
520
564
|
rep.ctx = ctx;
|
|
521
|
-
return xt_re_repeat_try(&rep, pos, 0);
|
|
565
|
+
return xt_re_repeat_try(&rep, pos, 0, caps);
|
|
522
566
|
default:
|
|
523
|
-
return xt_re_match_atom(piece->node, in, pos, cont, ctx);
|
|
567
|
+
return xt_re_match_atom(piece->node, in, pos, cont, ctx, caps);
|
|
524
568
|
}
|
|
525
569
|
}
|
|
526
570
|
|
|
@@ -536,25 +580,25 @@ typedef struct {
|
|
|
536
580
|
int index;
|
|
537
581
|
} xt_re_concat_step;
|
|
538
582
|
|
|
539
|
-
static int xt_re_match_concat(xt_re_concat_state *state, int index, size_t pos);
|
|
583
|
+
static int xt_re_match_concat(xt_re_concat_state *state, int index, size_t pos, const xt_re_cap *caps);
|
|
540
584
|
|
|
541
585
|
/* The continuation for a concat piece resumes matching at the next piece. */
|
|
542
|
-
static int xt_re_concat_next(void *v, size_t pos) {
|
|
586
|
+
static int xt_re_concat_next(void *v, size_t pos, const xt_re_cap *caps) {
|
|
543
587
|
xt_re_concat_step *s = (xt_re_concat_step *)v;
|
|
544
|
-
return xt_re_match_concat(s->state, s->index + 1, pos);
|
|
588
|
+
return xt_re_match_concat(s->state, s->index + 1, pos, caps);
|
|
545
589
|
}
|
|
546
590
|
|
|
547
|
-
static int xt_re_match_concat(xt_re_concat_state *state, int index, size_t pos) {
|
|
591
|
+
static int xt_re_match_concat(xt_re_concat_state *state, int index, size_t pos, const xt_re_cap *caps) {
|
|
548
592
|
xt_re_concat_step step;
|
|
549
|
-
if (index >= state->concat->count) return state->cont(state->ctx, pos);
|
|
593
|
+
if (index >= state->concat->count) return state->cont(state->ctx, pos, caps);
|
|
550
594
|
step.state = state;
|
|
551
595
|
step.index = index;
|
|
552
596
|
return xt_re_match_piece(&state->concat->pieces[index], state->in, pos,
|
|
553
|
-
xt_re_concat_next, &step);
|
|
597
|
+
xt_re_concat_next, &step, caps);
|
|
554
598
|
}
|
|
555
599
|
|
|
556
600
|
static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
|
|
557
|
-
xt_re_cont cont, void *ctx) {
|
|
601
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps) {
|
|
558
602
|
int i;
|
|
559
603
|
for (i = 0; i < alt->count; i++) {
|
|
560
604
|
xt_re_concat_state state;
|
|
@@ -562,18 +606,34 @@ static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
|
|
|
562
606
|
state.in = in;
|
|
563
607
|
state.cont = cont;
|
|
564
608
|
state.ctx = ctx;
|
|
565
|
-
if (xt_re_match_concat(&state, 0, pos)) return 1;
|
|
609
|
+
if (xt_re_match_concat(&state, 0, pos, caps)) return 1;
|
|
566
610
|
}
|
|
567
611
|
return 0;
|
|
568
612
|
}
|
|
569
613
|
|
|
570
614
|
typedef struct {
|
|
571
615
|
size_t end;
|
|
616
|
+
regmatch_t *pmatch;
|
|
617
|
+
size_t nmatch;
|
|
572
618
|
} xt_re_match_result;
|
|
573
619
|
|
|
574
|
-
|
|
620
|
+
/* Final continuation: records the whole-match end offset and copies every
|
|
621
|
+
* capture on the successful path into `pmatch`. Walking from the head means
|
|
622
|
+
* the most recent frame wins, which is what a quantified group such as
|
|
623
|
+
* `(a)+` should report (its last iteration). */
|
|
624
|
+
static int xt_re_record_match(void *ctx, size_t pos, const xt_re_cap *caps) {
|
|
575
625
|
xt_re_match_result *result = (xt_re_match_result *)ctx;
|
|
626
|
+
const xt_re_cap *cap;
|
|
576
627
|
result->end = pos;
|
|
628
|
+
for (cap = caps; cap; cap = cap->parent) {
|
|
629
|
+
if (cap->index >= 1 && (size_t)cap->index < result->nmatch) {
|
|
630
|
+
regmatch_t *slot = &result->pmatch[cap->index];
|
|
631
|
+
if (slot->rm_so < 0) {
|
|
632
|
+
slot->rm_so = cap->start;
|
|
633
|
+
slot->rm_eo = cap->end;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
577
637
|
return 1;
|
|
578
638
|
}
|
|
579
639
|
|
|
@@ -587,6 +647,7 @@ static int regcomp(regex_t *preg, const char *pattern, int cflags) {
|
|
|
587
647
|
p.s = pattern;
|
|
588
648
|
p.i = 0;
|
|
589
649
|
p.error = 0;
|
|
650
|
+
p.groupCount = 0;
|
|
590
651
|
root = xt_re_parse_alt(&p);
|
|
591
652
|
if (!root || p.error || p.s[p.i] != '\0') {
|
|
592
653
|
if (root) xt_re_free_alt(root);
|
|
@@ -601,15 +662,24 @@ static int regexec(const regex_t *preg, const char *string, size_t nmatch,
|
|
|
601
662
|
regmatch_t pmatch[], int eflags) {
|
|
602
663
|
xt_re_input in;
|
|
603
664
|
size_t start;
|
|
665
|
+
size_t i;
|
|
604
666
|
(void)eflags;
|
|
605
667
|
if (!preg || !preg->root || !string) return 1;
|
|
668
|
+
if (pmatch) {
|
|
669
|
+
for (i = 0; i < nmatch; i++) {
|
|
670
|
+
pmatch[i].rm_so = -1;
|
|
671
|
+
pmatch[i].rm_eo = -1;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
606
674
|
in.text = string;
|
|
607
675
|
in.len = strlen(string);
|
|
608
676
|
in.icase = preg->icase;
|
|
609
677
|
for (start = 0; start <= in.len; start++) {
|
|
610
678
|
xt_re_match_result result;
|
|
611
679
|
result.end = 0;
|
|
612
|
-
|
|
680
|
+
result.pmatch = pmatch;
|
|
681
|
+
result.nmatch = nmatch;
|
|
682
|
+
if (xt_re_match_alt(preg->root, &in, start, xt_re_record_match, &result, NULL)) {
|
|
613
683
|
if (nmatch > 0 && pmatch) {
|
|
614
684
|
pmatch[0].rm_so = (int)start;
|
|
615
685
|
pmatch[0].rm_eo = (int)result.end;
|
|
@@ -25,10 +25,10 @@ static xt_value xt_array_method(xt_value target, const char *method, int32_t arg
|
|
|
25
25
|
}
|
|
26
26
|
if (strcmp(method, "reverse") == 0) {
|
|
27
27
|
xt_array *array = xt_as_array(target);
|
|
28
|
-
for (uint32_t i = 0; i + 1 < array->length; i
|
|
28
|
+
for (uint32_t i = 0; i + 1 < array->length - i; i++) {
|
|
29
29
|
xt_value tmp = array->items[i];
|
|
30
|
-
array->items[i] = array->items[array->length - 1];
|
|
31
|
-
array->items[array->length - 1] = tmp;
|
|
30
|
+
array->items[i] = array->items[array->length - 1 - i];
|
|
31
|
+
array->items[array->length - 1 - i] = tmp;
|
|
32
32
|
}
|
|
33
33
|
return target;
|
|
34
34
|
}
|
|
@@ -45,7 +45,7 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
|
|
|
45
45
|
if (strcmp(method, "slice") == 0) return xt_string_slice(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
46
46
|
if (strcmp(method, "substring") == 0) return xt_string_substring(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
47
47
|
if (strcmp(method, "substr") == 0) return xt_string_substr(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
48
|
-
if (strcmp(method, "split") == 0) return xt_string_split(target, xt_arg_at(argc, argv, 0));
|
|
48
|
+
if (strcmp(method, "split") == 0) return xt_string_split(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
49
49
|
if (strcmp(method, "toUpperCase") == 0) return xt_string_case(target, 1);
|
|
50
50
|
if (strcmp(method, "toLowerCase") == 0) return xt_string_case(target, 0);
|
|
51
51
|
if (strcmp(method, "trim") == 0) return xt_string_trim(target);
|
|
@@ -58,10 +58,208 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
|
|
|
58
58
|
return XT_UNDEFINED;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/* Backing trampoline for `fn.bind(thisArg, ...partial)`: the bound target,
|
|
62
|
+
* `this` value and partial argument array travel in the closure environment. */
|
|
63
|
+
static xt_value xt_bound_invoke(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
64
|
+
(void)thisValue;
|
|
65
|
+
xt_value target = xt_closure_env(env, 0);
|
|
66
|
+
xt_value boundThis = xt_closure_env(env, 1);
|
|
67
|
+
xt_value boundArgs = xt_closure_env(env, 2);
|
|
68
|
+
int32_t boundCount = XT_IS_ARRAY(boundArgs) ? (int32_t)xt_as_array(boundArgs)->length : 0;
|
|
69
|
+
int32_t total = boundCount + argc;
|
|
70
|
+
xt_value *combined = total > 0 ? (xt_value *)malloc(sizeof(xt_value) * (size_t)total) : NULL;
|
|
71
|
+
if (combined) {
|
|
72
|
+
for (int32_t i = 0; i < boundCount; i++) combined[i] = xt_as_array(boundArgs)->items[i];
|
|
73
|
+
for (int32_t i = 0; i < argc; i++) combined[boundCount + i] = argv[i];
|
|
74
|
+
}
|
|
75
|
+
xt_value result = xt_call_with_this(target, boundThis, total, combined);
|
|
76
|
+
free(combined);
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Backing trampoline for first-class built-in method values (`arr.map`,
|
|
81
|
+
* `"".slice`, `(1).toFixed`). The value is *unbound*, exactly like JavaScript:
|
|
82
|
+
* the receiver arrives through `this`, so a detached call
|
|
83
|
+
* (`const f = arr.map; f(cb)`) throws, while `arr.map(cb)` and
|
|
84
|
+
* `arr.map?.(cb)` (which pass the receiver) work. The method name lives in
|
|
85
|
+
* environment slot 0. */
|
|
86
|
+
static xt_value xt_method_value_invoke(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
87
|
+
xt_value name = xt_closure_env(env, 0);
|
|
88
|
+
if (thisValue == XT_UNDEFINED || thisValue == XT_NULL) {
|
|
89
|
+
const char *method = xt_string_data(name);
|
|
90
|
+
char message[256];
|
|
91
|
+
snprintf(message, sizeof(message),
|
|
92
|
+
"TypeError: Cannot read properties of undefined (reading '%s')",
|
|
93
|
+
method ? method : "method");
|
|
94
|
+
xt_throw(xt_string_from_cstr(message));
|
|
95
|
+
}
|
|
96
|
+
return xt_call_method(thisValue, name, argc, argv);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#define XT_NAME_COUNT(table) (sizeof(table) / sizeof((table)[0]))
|
|
100
|
+
|
|
101
|
+
typedef struct {
|
|
102
|
+
const char *name;
|
|
103
|
+
int32_t arity;
|
|
104
|
+
} xt_method_spec;
|
|
105
|
+
|
|
106
|
+
static const xt_method_spec XT_ARRAY_METHODS[] = {
|
|
107
|
+
{"push", 1}, {"pop", 0}, {"shift", 0}, {"unshift", 1}, {"join", 1}, {"slice", 2},
|
|
108
|
+
{"indexOf", 1}, {"lastIndexOf", 1}, {"includes", 1}, {"concat", 1}, {"forEach", 1},
|
|
109
|
+
{"map", 1}, {"filter", 1}, {"reduce", 1}, {"reduceRight", 1}, {"reverse", 0},
|
|
110
|
+
{"at", 1}, {"find", 1}, {"findIndex", 1}, {"findLast", 1}, {"findLastIndex", 1},
|
|
111
|
+
{"some", 1}, {"every", 1}, {"sort", 1}, {"flat", 0}, {"flatMap", 1}, {"fill", 1},
|
|
112
|
+
{"copyWithin", 2}, {"splice", 2}, {"toReversed", 0}, {"toSorted", 1}, {"toSpliced", 2},
|
|
113
|
+
{"with", 2}, {"keys", 0}, {"values", 0}, {"entries", 0}, {"toString", 0},
|
|
114
|
+
{"toLocaleString", 0},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
static const xt_method_spec XT_STRING_METHODS[] = {
|
|
118
|
+
{"charAt", 1}, {"charCodeAt", 1}, {"codePointAt", 1}, {"indexOf", 1}, {"lastIndexOf", 1},
|
|
119
|
+
{"includes", 1}, {"slice", 2}, {"substring", 2}, {"substr", 2}, {"split", 2},
|
|
120
|
+
{"toUpperCase", 0}, {"toLowerCase", 0}, {"trim", 0}, {"trimStart", 0}, {"trimEnd", 0},
|
|
121
|
+
{"replace", 2}, {"replaceAll", 2}, {"repeat", 1}, {"startsWith", 1}, {"endsWith", 1},
|
|
122
|
+
{"concat", 1}, {"at", 1}, {"padStart", 1}, {"padEnd", 1}, {"match", 1}, {"search", 1},
|
|
123
|
+
{"localeCompare", 1}, {"toString", 0}, {"valueOf", 0},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
static const xt_method_spec XT_NUMBER_METHODS[] = {
|
|
127
|
+
{"toFixed", 1}, {"toPrecision", 1}, {"toExponential", 1}, {"toString", 1},
|
|
128
|
+
{"valueOf", 0}, {"toLocaleString", 0},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
static const xt_method_spec XT_FUNCTION_METHODS[] = {
|
|
132
|
+
{"call", 1}, {"apply", 2}, {"bind", 1}, {"toString", 0},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
static const xt_method_spec XT_BIGINT_METHODS[] = {
|
|
136
|
+
{"toString", 0}, {"toLocaleString", 0}, {"valueOf", 0},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
static const xt_method_spec XT_BOOLEAN_METHODS[] = {
|
|
140
|
+
{"toString", 0}, {"valueOf", 0},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
static const xt_method_spec XT_DATE_METHODS[] = {
|
|
144
|
+
{"getTime", 0}, {"getFullYear", 0}, {"getMonth", 0}, {"getDate", 0}, {"getDay", 0},
|
|
145
|
+
{"getHours", 0}, {"getMinutes", 0}, {"getSeconds", 0}, {"getMilliseconds", 0},
|
|
146
|
+
{"getUTCFullYear", 0}, {"getUTCMonth", 0}, {"getUTCDate", 0}, {"getUTCDay", 0},
|
|
147
|
+
{"getUTCHours", 0}, {"getUTCMinutes", 0}, {"getUTCSeconds", 0}, {"getUTCMilliseconds", 0},
|
|
148
|
+
{"toISOString", 0}, {"toJSON", 1}, {"toString", 0}, {"valueOf", 0},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
static const xt_method_spec XT_MAP_METHODS[] = {
|
|
152
|
+
{"get", 1}, {"set", 2}, {"has", 1}, {"delete", 1}, {"clear", 0}, {"keys", 0},
|
|
153
|
+
{"values", 0}, {"entries", 0}, {"forEach", 1},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
static const xt_method_spec XT_SET_METHODS[] = {
|
|
157
|
+
{"add", 1}, {"has", 1}, {"delete", 1}, {"clear", 0}, {"keys", 0}, {"values", 0},
|
|
158
|
+
{"entries", 0}, {"forEach", 1},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
static const xt_method_spec XT_REGEXP_METHODS[] = {
|
|
162
|
+
{"test", 1}, {"exec", 1}, {"toString", 0},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
static const xt_method_spec XT_PROMISE_METHODS[] = {
|
|
166
|
+
{"then", 2}, {"catch", 1}, {"finally", 1},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
static const xt_method_spec XT_ERROR_METHODS[] = {
|
|
170
|
+
{"toString", 0},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
static const xt_method_spec XT_GENERATOR_METHODS[] = {
|
|
174
|
+
{"next", 1}, {"return", 1}, {"throw", 1},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
static const xt_method_spec XT_SYMBOL_METHODS[] = {
|
|
178
|
+
{"toString", 0}, {"valueOf", 0},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
static int xt_method_lookup(const xt_method_spec *specs, size_t count, const char *method, int32_t *arity) {
|
|
182
|
+
for (size_t i = 0; i < count; i++) {
|
|
183
|
+
if (strcmp(method, specs[i].name) == 0) {
|
|
184
|
+
if (arity) *arity = specs[i].arity;
|
|
185
|
+
return 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
xt_value xt_method_value(xt_value target, xt_value name) {
|
|
192
|
+
if (!XT_IS_STRING(name)) return XT_UNDEFINED;
|
|
193
|
+
const char *method = xt_string_data(name);
|
|
194
|
+
if (!method) return XT_UNDEFINED;
|
|
195
|
+
int32_t arity = 0;
|
|
196
|
+
int known = 0;
|
|
197
|
+
if (XT_IS_ARRAY(target)) {
|
|
198
|
+
known = xt_method_lookup(XT_ARRAY_METHODS, XT_NAME_COUNT(XT_ARRAY_METHODS), method, &arity);
|
|
199
|
+
} else if (XT_IS_STRING(target)) {
|
|
200
|
+
known = xt_method_lookup(XT_STRING_METHODS, XT_NAME_COUNT(XT_STRING_METHODS), method, &arity);
|
|
201
|
+
} else if (XT_IS_FUNCTION(target)) {
|
|
202
|
+
known = xt_method_lookup(XT_FUNCTION_METHODS, XT_NAME_COUNT(XT_FUNCTION_METHODS), method, &arity);
|
|
203
|
+
} else if (XT_IS_BIGINT(target)) {
|
|
204
|
+
known = xt_method_lookup(XT_BIGINT_METHODS, XT_NAME_COUNT(XT_BIGINT_METHODS), method, &arity);
|
|
205
|
+
} else if (XT_IS_NUMBER(target)) {
|
|
206
|
+
known = xt_method_lookup(XT_NUMBER_METHODS, XT_NAME_COUNT(XT_NUMBER_METHODS), method, &arity);
|
|
207
|
+
} else if (target == XT_TRUE || target == XT_FALSE) {
|
|
208
|
+
known = xt_method_lookup(XT_BOOLEAN_METHODS, XT_NAME_COUNT(XT_BOOLEAN_METHODS), method, &arity);
|
|
209
|
+
} else if (XT_IS_OBJECT(target)) {
|
|
210
|
+
int kind = ((xt_object *)XT_GET_PTR(target))->header.kind;
|
|
211
|
+
if (kind == XT_OBJECT_KIND_DATE) {
|
|
212
|
+
known = xt_method_lookup(XT_DATE_METHODS, XT_NAME_COUNT(XT_DATE_METHODS), method, &arity);
|
|
213
|
+
} else if (kind == XT_OBJECT_KIND_MAP) {
|
|
214
|
+
known = xt_method_lookup(XT_MAP_METHODS, XT_NAME_COUNT(XT_MAP_METHODS), method, &arity);
|
|
215
|
+
} else if (kind == XT_OBJECT_KIND_SET) {
|
|
216
|
+
known = xt_method_lookup(XT_SET_METHODS, XT_NAME_COUNT(XT_SET_METHODS), method, &arity);
|
|
217
|
+
} else if (kind == XT_OBJECT_KIND_REGEXP) {
|
|
218
|
+
known = xt_method_lookup(XT_REGEXP_METHODS, XT_NAME_COUNT(XT_REGEXP_METHODS), method, &arity);
|
|
219
|
+
} else if (kind == XT_OBJECT_KIND_PROMISE) {
|
|
220
|
+
known = xt_method_lookup(XT_PROMISE_METHODS, XT_NAME_COUNT(XT_PROMISE_METHODS), method, &arity);
|
|
221
|
+
} else if (kind == XT_OBJECT_KIND_ERROR) {
|
|
222
|
+
known = xt_method_lookup(XT_ERROR_METHODS, XT_NAME_COUNT(XT_ERROR_METHODS), method, &arity);
|
|
223
|
+
} else if (kind == XT_OBJECT_KIND_GENERATOR) {
|
|
224
|
+
known = xt_method_lookup(XT_GENERATOR_METHODS, XT_NAME_COUNT(XT_GENERATOR_METHODS), method, &arity);
|
|
225
|
+
} else if (kind == XT_OBJECT_KIND_SYMBOL) {
|
|
226
|
+
known = xt_method_lookup(XT_SYMBOL_METHODS, XT_NAME_COUNT(XT_SYMBOL_METHODS), method, &arity);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (!known) return XT_UNDEFINED;
|
|
230
|
+
xt_value env[1];
|
|
231
|
+
env[0] = name;
|
|
232
|
+
xt_value closure = xt_closure_new((void *)xt_method_value_invoke, 1, env);
|
|
233
|
+
xt_function_set_metadata(closure, name, arity);
|
|
234
|
+
return closure;
|
|
235
|
+
}
|
|
236
|
+
|
|
61
237
|
xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *argv) {
|
|
238
|
+
/* A symbol-named method (`obj[Symbol.iterator]()`) is never a built-in, so
|
|
239
|
+
* resolve it as an ordinary property and call it with `target` as `this`. */
|
|
240
|
+
if (xt_is_symbol(name)) {
|
|
241
|
+
if (target == XT_UNDEFINED || target == XT_NULL) {
|
|
242
|
+
xt_throw(xt_string_from_cstr("TypeError: Cannot read properties of null (reading a Symbol)"));
|
|
243
|
+
}
|
|
244
|
+
xt_value fn = xt_get(target, name);
|
|
245
|
+
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
246
|
+
return XT_UNDEFINED;
|
|
247
|
+
}
|
|
62
248
|
const char *method = xt_string_data(name);
|
|
63
249
|
if (!method) return XT_UNDEFINED;
|
|
250
|
+
if (target == XT_UNDEFINED || target == XT_NULL) {
|
|
251
|
+
const char *base = target == XT_NULL ? "null" : "undefined";
|
|
252
|
+
char message[256];
|
|
253
|
+
snprintf(message, sizeof(message),
|
|
254
|
+
"TypeError: Cannot read properties of %s (reading '%s')", base, method);
|
|
255
|
+
xt_throw(xt_string_from_cstr(message));
|
|
256
|
+
}
|
|
64
257
|
int handled = 0;
|
|
258
|
+
if (xt_is_symbol(target)) {
|
|
259
|
+
if (strcmp(method, "toString") == 0) return xt_symbol_to_string(target);
|
|
260
|
+
if (strcmp(method, "valueOf") == 0) return target;
|
|
261
|
+
return XT_UNDEFINED;
|
|
262
|
+
}
|
|
65
263
|
if (XT_IS_OBJECT(target)) {
|
|
66
264
|
xt_object *obj = (xt_object *)XT_GET_PTR(target);
|
|
67
265
|
int kind = obj->header.kind;
|
|
@@ -69,6 +267,14 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
69
267
|
xt_value result = xt_promise_method(target, method, argc, argv, &handled);
|
|
70
268
|
if (handled) return result;
|
|
71
269
|
}
|
|
270
|
+
if (kind == XT_OBJECT_KIND_ERROR && strcmp(method, "toString") == 0) {
|
|
271
|
+
return xt_error_to_string(target);
|
|
272
|
+
}
|
|
273
|
+
if (kind == XT_OBJECT_KIND_GENERATOR) {
|
|
274
|
+
if (strcmp(method, "next") == 0) return xt_generator_next(target, xt_arg_at(argc, argv, 0));
|
|
275
|
+
if (strcmp(method, "throw") == 0) return xt_generator_throw(target, xt_arg_at(argc, argv, 0));
|
|
276
|
+
if (strcmp(method, "return") == 0) return xt_generator_return(target, xt_arg_at(argc, argv, 0));
|
|
277
|
+
}
|
|
72
278
|
if (kind != XT_OBJECT_KIND_OBJECT && kind != XT_OBJECT_KIND_FUNCTION) {
|
|
73
279
|
xt_value result = xt_ext_container_method(target, method, argc, argv, &handled);
|
|
74
280
|
if (handled) return result;
|
|
@@ -85,8 +291,48 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
85
291
|
return XT_UNDEFINED;
|
|
86
292
|
}
|
|
87
293
|
if (XT_IS_FUNCTION(target)) {
|
|
294
|
+
/* Function prototype methods, resolved before the property bag. */
|
|
295
|
+
if (strcmp(method, "call") == 0) {
|
|
296
|
+
return xt_call_with_this(target, xt_arg_at(argc, argv, 0),
|
|
297
|
+
argc > 0 ? argc - 1 : 0, argc > 0 ? argv + 1 : NULL);
|
|
298
|
+
}
|
|
299
|
+
if (strcmp(method, "apply") == 0) {
|
|
300
|
+
xt_value thisArg = xt_arg_at(argc, argv, 0);
|
|
301
|
+
xt_value args = xt_arg_at(argc, argv, 1);
|
|
302
|
+
if (XT_IS_ARRAY(args)) {
|
|
303
|
+
xt_array *array = xt_as_array(args);
|
|
304
|
+
return xt_call_with_this(target, thisArg, (int32_t)array->length, array->items);
|
|
305
|
+
}
|
|
306
|
+
return xt_call_with_this(target, thisArg, 0, NULL);
|
|
307
|
+
}
|
|
308
|
+
if (strcmp(method, "bind") == 0) {
|
|
309
|
+
xt_value env[3];
|
|
310
|
+
env[0] = target;
|
|
311
|
+
env[1] = xt_arg_at(argc, argv, 0);
|
|
312
|
+
env[2] = argc > 1 ? xt_array_new(argc - 1, argv + 1) : xt_array_new(0, NULL);
|
|
313
|
+
xt_value bound = xt_closure_new((void *)xt_bound_invoke, 3, env);
|
|
314
|
+
/* Bound functions report `name` as "bound <target>" and reduce `length`
|
|
315
|
+
* by the number of pre-bound arguments, like the specification. */
|
|
316
|
+
xt_function *targetFn = (xt_function *)XT_GET_PTR(target);
|
|
317
|
+
const char *base = targetFn->name ? targetFn->name->data : "";
|
|
318
|
+
char boundName[256];
|
|
319
|
+
snprintf(boundName, sizeof(boundName), "bound %s", base);
|
|
320
|
+
int32_t prebound = argc > 1 ? argc - 1 : 0;
|
|
321
|
+
int32_t arity = targetFn->arity >= 0 ? targetFn->arity - prebound : 0;
|
|
322
|
+
if (arity < 0) arity = 0;
|
|
323
|
+
xt_function_set_metadata(bound, xt_string_from_cstr(boundName), arity);
|
|
324
|
+
return bound;
|
|
325
|
+
}
|
|
88
326
|
xt_value fn = xt_object_get(target, name);
|
|
89
327
|
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
328
|
+
if (strcmp(method, "toString") == 0) {
|
|
329
|
+
/* Source text is not retained; report a native-style representation. */
|
|
330
|
+
xt_function *self = (xt_function *)XT_GET_PTR(target);
|
|
331
|
+
const char *selfName = self->name ? self->name->data : "";
|
|
332
|
+
char buffer[256];
|
|
333
|
+
snprintf(buffer, sizeof(buffer), "function %s() { [native code] }", selfName);
|
|
334
|
+
return xt_string_from_cstr(buffer);
|
|
335
|
+
}
|
|
90
336
|
{
|
|
91
337
|
char message[256];
|
|
92
338
|
snprintf(message, sizeof(message), "TypeError: function has no callable method '%s'", method);
|
|
@@ -112,5 +358,10 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
112
358
|
xt_value result = xt_ext_number_method(target, method, argc, argv, &handled);
|
|
113
359
|
if (handled) return result;
|
|
114
360
|
}
|
|
361
|
+
if (target == XT_TRUE || target == XT_FALSE) {
|
|
362
|
+
if (strcmp(method, "toString") == 0) return target == XT_TRUE ? xt_string_from_cstr("true") : xt_string_from_cstr("false");
|
|
363
|
+
if (strcmp(method, "valueOf") == 0) return target;
|
|
364
|
+
return XT_UNDEFINED;
|
|
365
|
+
}
|
|
115
366
|
return XT_UNDEFINED;
|
|
116
367
|
}
|
|
@@ -76,10 +76,12 @@ static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value l
|
|
|
76
76
|
return xt_string_new(s->data + start, (size_t)length);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
static xt_value xt_string_split(xt_value value, xt_value separatorValue) {
|
|
79
|
+
static xt_value xt_string_split(xt_value value, xt_value separatorValue, xt_value limitValue) {
|
|
80
80
|
xt_value result = xt_array_new(0, NULL);
|
|
81
81
|
xt_string *s = xt_as_string(value);
|
|
82
82
|
if (!s) return result;
|
|
83
|
+
int64_t limit = limitValue == XT_UNDEFINED ? 0x7fffffff : (int64_t)xt_to_int32(limitValue);
|
|
84
|
+
if (limit <= 0) return result;
|
|
83
85
|
if (separatorValue == XT_UNDEFINED) {
|
|
84
86
|
xt_array_push(result, value);
|
|
85
87
|
return result;
|
|
@@ -87,20 +89,22 @@ static xt_value xt_string_split(xt_value value, xt_value separatorValue) {
|
|
|
87
89
|
xt_string *sep = xt_as_string(xt_to_string(separatorValue));
|
|
88
90
|
if (sep->length == 0) {
|
|
89
91
|
for (uint32_t i = 0; i < s->length; i++) xt_array_push(result, xt_string_new(s->data + i, 1));
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
} else {
|
|
93
|
+
uint32_t start = 0;
|
|
94
|
+
uint32_t i = 0;
|
|
95
|
+
while (i + sep->length <= s->length) {
|
|
96
|
+
if (memcmp(s->data + i, sep->data, sep->length) == 0) {
|
|
97
|
+
xt_array_push(result, xt_string_new(s->data + start, i - start));
|
|
98
|
+
i += sep->length;
|
|
99
|
+
start = i;
|
|
100
|
+
} else {
|
|
101
|
+
i++;
|
|
102
|
+
}
|
|
101
103
|
}
|
|
104
|
+
xt_array_push(result, xt_string_new(s->data + start, s->length - start));
|
|
102
105
|
}
|
|
103
|
-
|
|
106
|
+
xt_array *array = xt_as_array(result);
|
|
107
|
+
if ((int64_t)array->length > limit) array->length = (uint32_t)limit;
|
|
104
108
|
return result;
|
|
105
109
|
}
|
|
106
110
|
|