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.
Files changed (215) hide show
  1. package/dist/src/ast/expressions.d.ts +8 -0
  2. package/dist/src/binder/binder.js +1 -0
  3. package/dist/src/binder/binder.js.map +1 -1
  4. package/dist/src/binder/references.d.ts +1 -1
  5. package/dist/src/binder/references.js +34 -5
  6. package/dist/src/binder/references.js.map +1 -1
  7. package/dist/src/binder/types.d.ts +4 -0
  8. package/dist/src/binder/types.js.map +1 -1
  9. package/dist/src/cli/main.js.map +1 -1
  10. package/dist/src/codegen/generator/calls/access.d.ts +9 -1
  11. package/dist/src/codegen/generator/calls/access.js +146 -13
  12. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  13. package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
  14. package/dist/src/codegen/generator/calls/closures.js +25 -0
  15. package/dist/src/codegen/generator/calls/closures.js.map +1 -1
  16. package/dist/src/codegen/generator/calls/invocation.js +12 -1
  17. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  18. package/dist/src/codegen/generator/calls/literals.js +12 -4
  19. package/dist/src/codegen/generator/calls/literals.js.map +1 -1
  20. package/dist/src/codegen/generator/calls/super.js +4 -7
  21. package/dist/src/codegen/generator/calls/super.js.map +1 -1
  22. package/dist/src/codegen/generator/context.d.ts +21 -2
  23. package/dist/src/codegen/generator/context.js +42 -5
  24. package/dist/src/codegen/generator/context.js.map +1 -1
  25. package/dist/src/codegen/generator/expressions/assignment.js +1 -3
  26. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
  27. package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
  28. package/dist/src/codegen/generator/expressions/primary.js +99 -1
  29. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  30. package/dist/src/codegen/generator/generator.js.map +1 -1
  31. package/dist/src/codegen/generator/module.js +25 -15
  32. package/dist/src/codegen/generator/module.js.map +1 -1
  33. package/dist/src/codegen/generator/state.d.ts +15 -0
  34. package/dist/src/codegen/generator/state.js +28 -1
  35. package/dist/src/codegen/generator/state.js.map +1 -1
  36. package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
  37. package/dist/src/codegen/generator/statements/control-flow.js +85 -0
  38. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
  39. package/dist/src/codegen/generator/statements/dispatch.js +15 -6
  40. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
  41. package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
  42. package/dist/src/codegen/generator/statements/loops.js +25 -11
  43. package/dist/src/codegen/generator/statements/loops.js.map +1 -1
  44. package/dist/src/codegen/generator/tables.d.ts +7 -0
  45. package/dist/src/codegen/generator/tables.js +41 -1
  46. package/dist/src/codegen/generator/tables.js.map +1 -1
  47. package/dist/src/driver/compiler.d.ts +1 -1
  48. package/dist/src/driver/compiler.js +33 -1
  49. package/dist/src/driver/compiler.js.map +1 -1
  50. package/dist/src/driver/modules.d.ts +4 -3
  51. package/dist/src/driver/modules.js +61 -4
  52. package/dist/src/driver/modules.js.map +1 -1
  53. package/dist/src/driver/toolchain.js +1 -1
  54. package/dist/src/driver/toolchain.js.map +1 -1
  55. package/dist/src/lexer/scanner.js.map +1 -1
  56. package/dist/src/parser/declarations/classes.js +4 -0
  57. package/dist/src/parser/declarations/classes.js.map +1 -1
  58. package/dist/src/parser/declarations/type-members.js.map +1 -1
  59. package/dist/src/parser/expressions.d.ts +2 -1
  60. package/dist/src/parser/expressions.js +10 -1
  61. package/dist/src/parser/expressions.js.map +1 -1
  62. package/dist/src/parser/helpers.js +2 -2
  63. package/dist/src/parser/helpers.js.map +1 -1
  64. package/dist/src/parser/literals.js +79 -6
  65. package/dist/src/parser/literals.js.map +1 -1
  66. package/dist/src/parser/modules.js.map +1 -1
  67. package/dist/src/parser/parser.js +0 -1
  68. package/dist/src/parser/parser.js.map +1 -1
  69. package/dist/src/parser/statements.d.ts +1 -0
  70. package/dist/src/parser/statements.js +13 -1
  71. package/dist/src/parser/statements.js.map +1 -1
  72. package/dist/src/parser/types.js +2 -2
  73. package/dist/src/parser/types.js.map +1 -1
  74. package/dist/tests/ast/factory.test.d.ts +1 -0
  75. package/dist/tests/ast/factory.test.js +35 -0
  76. package/dist/tests/ast/factory.test.js.map +1 -0
  77. package/dist/tests/ast/visitor.test.d.ts +1 -0
  78. package/dist/tests/ast/visitor.test.js +53 -0
  79. package/dist/tests/ast/visitor.test.js.map +1 -0
  80. package/dist/tests/cli/main.test.js +109 -9
  81. package/dist/tests/cli/main.test.js.map +1 -1
  82. package/dist/tests/codegen/closures.test.d.ts +1 -0
  83. package/dist/tests/codegen/closures.test.js +32 -0
  84. package/dist/tests/codegen/closures.test.js.map +1 -0
  85. package/dist/tests/codegen/expressions.test.d.ts +1 -0
  86. package/dist/tests/codegen/expressions.test.js +149 -0
  87. package/dist/tests/codegen/expressions.test.js.map +1 -0
  88. package/dist/tests/codegen/llvm.test.js +14 -0
  89. package/dist/tests/codegen/llvm.test.js.map +1 -1
  90. package/dist/tests/codegen/values.test.d.ts +1 -0
  91. package/dist/tests/codegen/values.test.js +48 -0
  92. package/dist/tests/codegen/values.test.js.map +1 -0
  93. package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
  94. package/dist/tests/diagnostics/diagnostic.test.js +117 -0
  95. package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
  96. package/dist/tests/diagnostics/source.test.d.ts +1 -0
  97. package/dist/tests/diagnostics/source.test.js +58 -0
  98. package/dist/tests/diagnostics/source.test.js.map +1 -0
  99. package/dist/tests/driver/modules.test.d.ts +1 -0
  100. package/dist/tests/driver/modules.test.js +140 -0
  101. package/dist/tests/driver/modules.test.js.map +1 -0
  102. package/dist/tests/driver/paths.test.js +17 -1
  103. package/dist/tests/driver/paths.test.js.map +1 -1
  104. package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
  105. package/dist/tests/driver/runtime-lib.test.js +19 -0
  106. package/dist/tests/driver/runtime-lib.test.js.map +1 -0
  107. package/dist/tests/driver/toolchain-provider.test.js +91 -0
  108. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  109. package/dist/tests/driver/toolchain.test.js +11 -1
  110. package/dist/tests/driver/toolchain.test.js.map +1 -1
  111. package/dist/tests/e2e/compile-node.test.js +14 -1
  112. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  113. package/dist/tests/e2e/compile.test.js +465 -2
  114. package/dist/tests/e2e/compile.test.js.map +1 -1
  115. package/dist/tests/e2e/differential.test.d.ts +7 -0
  116. package/dist/tests/e2e/differential.test.js +581 -0
  117. package/dist/tests/e2e/differential.test.js.map +1 -0
  118. package/dist/tests/e2e/harness.d.ts +21 -0
  119. package/dist/tests/e2e/harness.js +26 -0
  120. package/dist/tests/e2e/harness.js.map +1 -1
  121. package/dist/tests/extensions/node.test.d.ts +1 -0
  122. package/dist/tests/extensions/node.test.js +32 -0
  123. package/dist/tests/extensions/node.test.js.map +1 -0
  124. package/dist/tests/lexer/identifiers.test.d.ts +1 -0
  125. package/dist/tests/lexer/identifiers.test.js +13 -0
  126. package/dist/tests/lexer/identifiers.test.js.map +1 -0
  127. package/dist/tests/lexer/numbers.test.d.ts +1 -0
  128. package/dist/tests/lexer/numbers.test.js +23 -0
  129. package/dist/tests/lexer/numbers.test.js.map +1 -0
  130. package/dist/tests/lexer/strings.test.d.ts +1 -0
  131. package/dist/tests/lexer/strings.test.js +72 -0
  132. package/dist/tests/lexer/strings.test.js.map +1 -0
  133. package/dist/tests/parser/declarations.test.d.ts +1 -0
  134. package/dist/tests/parser/declarations.test.js +70 -0
  135. package/dist/tests/parser/declarations.test.js.map +1 -0
  136. package/dist/tests/parser/expressions.test.d.ts +1 -0
  137. package/dist/tests/parser/expressions.test.js +47 -0
  138. package/dist/tests/parser/expressions.test.js.map +1 -0
  139. package/dist/tests/parser/helpers.test.d.ts +1 -0
  140. package/dist/tests/parser/helpers.test.js +58 -0
  141. package/dist/tests/parser/helpers.test.js.map +1 -0
  142. package/dist/tests/parser/literals.test.d.ts +1 -0
  143. package/dist/tests/parser/literals.test.js +70 -0
  144. package/dist/tests/parser/literals.test.js.map +1 -0
  145. package/dist/tests/parser/module-syntax.test.d.ts +1 -0
  146. package/dist/tests/parser/module-syntax.test.js +91 -0
  147. package/dist/tests/parser/module-syntax.test.js.map +1 -0
  148. package/dist/tests/parser/statements.test.d.ts +1 -0
  149. package/dist/tests/parser/statements.test.js +85 -0
  150. package/dist/tests/parser/statements.test.js.map +1 -0
  151. package/dist/tests/parser/type-members.test.d.ts +1 -0
  152. package/dist/tests/parser/type-members.test.js +86 -0
  153. package/dist/tests/parser/type-members.test.js.map +1 -0
  154. package/dist/tests/parser/types.test.d.ts +1 -0
  155. package/dist/tests/parser/types.test.js +143 -0
  156. package/dist/tests/parser/types.test.js.map +1 -0
  157. package/package.json +6 -1
  158. package/runtime/ext_node/fs/fs_ops.c +36 -1
  159. package/runtime/rt.h +17 -0
  160. package/runtime/rt_internal.h +88 -2
  161. package/runtime/xt_builtins.c +174 -6
  162. package/runtime/xt_containers.c +234 -42
  163. package/runtime/xt_generator.c +304 -0
  164. package/runtime/xt_io.c +170 -30
  165. package/runtime/xt_promise.c +6 -1
  166. package/runtime/xt_regex.h +105 -35
  167. package/runtime/xt_stdlib/dispatch.inc +255 -4
  168. package/runtime/xt_stdlib/strings.inc +17 -13
  169. package/runtime/xt_stdlib2/arrays.inc +126 -19
  170. package/runtime/xt_stdlib2/date.inc +9 -2
  171. package/runtime/xt_stdlib2/error.inc +162 -12
  172. package/runtime/xt_stdlib2/helpers.inc +4 -0
  173. package/runtime/xt_stdlib2/json.inc +19 -6
  174. package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
  175. package/runtime/xt_stdlib2/numbers.inc +4 -5
  176. package/runtime/xt_stdlib2/objects.inc +29 -0
  177. package/runtime/xt_stdlib2/regexp-match.inc +293 -51
  178. package/runtime/xt_stdlib2/set.inc +58 -2
  179. package/runtime/xt_stdlib2/strings.inc +38 -0
  180. package/runtime/xt_symbol.c +149 -0
  181. package/runtime/xt_values.c +221 -15
  182. package/scripts/build-runtime.ts +1 -1
  183. package/src/ast/expressions.ts +8 -0
  184. package/src/binder/binder.ts +1 -1
  185. package/src/binder/references.ts +47 -4
  186. package/src/binder/types.ts +4 -0
  187. package/src/cli/main.ts +1 -2
  188. package/src/codegen/generator/calls/access.ts +158 -13
  189. package/src/codegen/generator/calls/closures.ts +24 -0
  190. package/src/codegen/generator/calls/invocation.ts +11 -1
  191. package/src/codegen/generator/calls/literals.ts +10 -4
  192. package/src/codegen/generator/calls/super.ts +6 -9
  193. package/src/codegen/generator/context.ts +44 -7
  194. package/src/codegen/generator/expressions/assignment.ts +1 -3
  195. package/src/codegen/generator/expressions/primary.ts +108 -2
  196. package/src/codegen/generator/generator.ts +0 -1
  197. package/src/codegen/generator/module.ts +24 -18
  198. package/src/codegen/generator/state.ts +44 -1
  199. package/src/codegen/generator/statements/control-flow.ts +86 -0
  200. package/src/codegen/generator/statements/dispatch.ts +18 -6
  201. package/src/codegen/generator/statements/loops.ts +29 -11
  202. package/src/codegen/generator/tables.ts +44 -1
  203. package/src/driver/compiler.ts +32 -2
  204. package/src/driver/modules.ts +64 -4
  205. package/src/driver/toolchain.ts +1 -1
  206. package/src/lexer/scanner.ts +0 -1
  207. package/src/parser/declarations/classes.ts +4 -0
  208. package/src/parser/declarations/type-members.ts +0 -2
  209. package/src/parser/expressions.ts +13 -1
  210. package/src/parser/helpers.ts +2 -2
  211. package/src/parser/literals.ts +82 -7
  212. package/src/parser/modules.ts +0 -1
  213. package/src/parser/parser.ts +0 -1
  214. package/src/parser/statements.ts +14 -0
  215. package/src/parser/types.ts +2 -2
@@ -7,10 +7,11 @@
7
7
  * literals, '.', '^', '$', '[abc]', '[^abc]', '[a-z]', '*', '+', '?',
8
8
  * alternation '|', groups '(...)', and escapes ('\d', '\w', '\s', ...).
9
9
  *
10
- * Only the boolean "does the pattern match a substring" result is produced;
11
- * capture groups are not reported (the caller passes nmatch = 0).
12
- *
13
- * The implementation is a recursive backtracking matcher over a small AST.
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
- typedef int (*xt_re_cont)(void *ctx, size_t pos);
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
- return xt_re_match_alt(node->group, in, pos, cont, ctx);
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
- static int xt_re_record_match(void *ctx, size_t pos) {
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
- if (xt_re_match_alt(preg->root, &in, start, xt_re_record_match, &result)) {
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++, array->length--) {
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
- return result;
91
- }
92
- uint32_t start = 0;
93
- uint32_t i = 0;
94
- while (i + sep->length <= s->length) {
95
- if (memcmp(s->data + i, sep->data, sep->length) == 0) {
96
- xt_array_push(result, xt_string_new(s->data + start, i - start));
97
- i += sep->length;
98
- start = i;
99
- } else {
100
- i++;
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
- xt_array_push(result, xt_string_new(s->data + start, s->length - start));
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