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
@@ -0,0 +1,149 @@
1
+ /*
2
+ * xbintsc runtime — Symbol primitives.
3
+ *
4
+ * Symbols are heap objects (`XT_OBJECT_KIND_SYMBOL`) tagged as `XT_TAG_OBJECT`
5
+ * so they can travel through the generic value plumbing, but they are
6
+ * distinguished by their header kind. Property keys are either strings or
7
+ * symbols; see `xt_to_property_key` in `xt_containers.c`.
8
+ */
9
+
10
+ #include "rt_internal.h"
11
+
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+
16
+ typedef struct {
17
+ xt_header header;
18
+ /* `XT_UNDEFINED` for `Symbol()` without a description, otherwise a string. */
19
+ xt_value description;
20
+ uint64_t id;
21
+ } xt_symbol_data;
22
+
23
+ static uint64_t g_symbol_counter = 0;
24
+
25
+ int xt_is_symbol(xt_value value) {
26
+ if (!XT_IS_OBJECT(value)) return 0;
27
+ return ((xt_header *)XT_GET_PTR(value))->kind == XT_OBJECT_KIND_SYMBOL;
28
+ }
29
+
30
+ static xt_symbol_data *xt_as_symbol(xt_value value) {
31
+ if (!xt_is_symbol(value)) return NULL;
32
+ return (xt_symbol_data *)XT_GET_PTR(value);
33
+ }
34
+
35
+ xt_value xt_symbol_new(xt_value description) {
36
+ xt_symbol_data *symbol =
37
+ (xt_symbol_data *)xt_alloc(sizeof(xt_symbol_data), XT_OBJECT_KIND_SYMBOL);
38
+ symbol->description = description;
39
+ symbol->id = ++g_symbol_counter;
40
+ return XT_FROM_PTR(XT_TAG_OBJECT, symbol);
41
+ }
42
+
43
+ /* -- well-known symbols --------------------------------------------------- */
44
+
45
+ static const char *const XT_WELL_KNOWN_NAMES[] = {
46
+ "asyncIterator", "hasInstance", "isConcatSpreadable", "iterator",
47
+ "match", "matchAll", "replace", "search",
48
+ "species", "split", "toPrimitive", "toStringTag",
49
+ "unscopables",
50
+ };
51
+ #define XT_WELL_KNOWN_COUNT \
52
+ ((int)(sizeof(XT_WELL_KNOWN_NAMES) / sizeof(XT_WELL_KNOWN_NAMES[0])))
53
+
54
+ static xt_value g_well_known[XT_WELL_KNOWN_COUNT];
55
+ static int g_well_known_ready = 0;
56
+
57
+ static void xt_symbol_init_well_known(void) {
58
+ if (g_well_known_ready) return;
59
+ g_well_known_ready = 1;
60
+ for (int i = 0; i < XT_WELL_KNOWN_COUNT; i++) {
61
+ char buffer[64];
62
+ snprintf(buffer, sizeof(buffer), "Symbol.%s", XT_WELL_KNOWN_NAMES[i]);
63
+ g_well_known[i] = xt_symbol_new(xt_string_from_cstr(buffer));
64
+ }
65
+ }
66
+
67
+ xt_value xt_symbol_well_known(const char *name) {
68
+ xt_symbol_init_well_known();
69
+ for (int i = 0; i < XT_WELL_KNOWN_COUNT; i++) {
70
+ if (strcmp(name, XT_WELL_KNOWN_NAMES[i]) == 0) return g_well_known[i];
71
+ }
72
+ return XT_UNDEFINED;
73
+ }
74
+
75
+ xt_value xt_symbol_get(xt_value name) {
76
+ const char *text = xt_string_data(name);
77
+ if (!text) return XT_UNDEFINED;
78
+ return xt_symbol_well_known(text);
79
+ }
80
+
81
+ /* -- description / toString ---------------------------------------------- */
82
+
83
+ xt_value xt_symbol_description(xt_value value) {
84
+ xt_symbol_data *symbol = xt_as_symbol(value);
85
+ return symbol ? symbol->description : XT_UNDEFINED;
86
+ }
87
+
88
+ xt_value xt_symbol_to_string(xt_value value) {
89
+ xt_symbol_data *symbol = xt_as_symbol(value);
90
+ if (!symbol) {
91
+ xt_throw(xt_string_from_cstr(
92
+ "TypeError: Symbol.prototype.toString requires that 'this' be a Symbol"));
93
+ }
94
+ xt_string *description =
95
+ XT_IS_STRING(symbol->description) ? xt_as_string(symbol->description) : NULL;
96
+ size_t length = description ? description->length : 0;
97
+ char *buffer = (char *)malloc(8 + length);
98
+ if (!buffer) abort();
99
+ memcpy(buffer, "Symbol(", 7);
100
+ if (description) memcpy(buffer + 7, description->data, length);
101
+ buffer[7 + length] = ')';
102
+ xt_value result = xt_string_new(buffer, 8 + length);
103
+ free(buffer);
104
+ return result;
105
+ }
106
+
107
+ /* -- constructor and statics ---------------------------------------------- */
108
+
109
+ xt_value xt_symbol(int32_t argc, xt_value *argv) {
110
+ xt_value description = xt_arg_at(argc, argv, 0);
111
+ if (description != XT_UNDEFINED) description = xt_to_string(description);
112
+ return xt_symbol_new(description);
113
+ }
114
+
115
+ /* Global symbol registry backing `Symbol.for` / `Symbol.keyFor`. */
116
+ static xt_value g_symbol_registry = XT_UNDEFINED;
117
+
118
+ xt_value xt_symbol_for(xt_value key) {
119
+ xt_value keyString = xt_to_string(key);
120
+ if (g_symbol_registry == XT_UNDEFINED) g_symbol_registry = xt_object_new();
121
+ xt_value existing = xt_object_get(g_symbol_registry, keyString);
122
+ if (existing != XT_UNDEFINED) return existing;
123
+ xt_value symbol = xt_symbol_new(keyString);
124
+ xt_object_set(g_symbol_registry, keyString, symbol);
125
+ return symbol;
126
+ }
127
+
128
+ xt_value xt_symbol_key_for(xt_value value) {
129
+ if (!xt_is_symbol(value)) {
130
+ xt_throw(xt_string_from_cstr("TypeError: Symbol.keyFor requires a symbol"));
131
+ }
132
+ if (g_symbol_registry == XT_UNDEFINED) return XT_UNDEFINED;
133
+ xt_value keys = xt_object_keys(g_symbol_registry);
134
+ xt_array *array = xt_as_array(keys);
135
+ if (!array) return XT_UNDEFINED;
136
+ for (uint32_t i = 0; i < array->length; i++) {
137
+ xt_value key = array->items[i];
138
+ if (xt_object_get(g_symbol_registry, key) == value) return key;
139
+ }
140
+ return XT_UNDEFINED;
141
+ }
142
+
143
+ xt_value xt_symbol_static(xt_value name, int32_t argc, xt_value *argv) {
144
+ const char *fn = xt_string_data(name);
145
+ if (!fn) return XT_UNDEFINED;
146
+ if (strcmp(fn, "for") == 0) return xt_symbol_for(xt_arg_at(argc, argv, 0));
147
+ if (strcmp(fn, "keyFor") == 0) return xt_symbol_key_for(xt_arg_at(argc, argv, 0));
148
+ return XT_UNDEFINED;
149
+ }
@@ -8,6 +8,7 @@
8
8
  #include "rt_internal.h"
9
9
 
10
10
  #include <math.h>
11
+ #include <ctype.h>
11
12
  #include <stdio.h>
12
13
  #include <stdlib.h>
13
14
  #include <string.h>
@@ -78,11 +79,57 @@ int xt_string_equals(xt_string *a, xt_string *b) {
78
79
  }
79
80
 
80
81
  /*
81
- * Format a double the way JavaScript's ToString does for the common cases:
82
- * integers print without a decimal point, everything else uses the shortest
83
- * representation that round-trips.
82
+ * ECMAScript number formatting.
83
+ *
84
+ * A double is rendered as the shortest decimal string that round-trips, then
85
+ * laid out with JavaScript's notation rules: fixed notation unless the decimal
86
+ * exponent is < -6 or >= 21, and a bare (unpadded, explicitly signed) exponent.
87
+ * The `toFixed`/`toExponential`/`toPrecision` variants reuse the same renderer.
84
88
  */
85
- static void xt_format_double(double d, char *buffer, size_t size) {
89
+ static void xt_render_decimal(int negative, const char *digits, int nd, int exp10, int expo, char *buffer, size_t size) {
90
+ size_t o = 0;
91
+ if (negative && o + 1 < size) buffer[o++] = '-';
92
+ if (expo) {
93
+ if (o + 1 < size) buffer[o++] = digits[0];
94
+ if (nd > 1) {
95
+ if (o + 1 < size) buffer[o++] = '.';
96
+ for (int i = 1; i < nd && o + 1 < size; i++) buffer[o++] = digits[i];
97
+ }
98
+ int written = snprintf(buffer + o, size - o, "e%c%d", exp10 < 0 ? '-' : '+', exp10 < 0 ? -exp10 : exp10);
99
+ if (written > 0) o += (size_t)written;
100
+ } else if (exp10 >= 0) {
101
+ int intDigits = exp10 + 1;
102
+ if (nd <= intDigits) {
103
+ for (int i = 0; i < nd && o + 1 < size; i++) buffer[o++] = digits[i];
104
+ for (int i = nd; i < intDigits && o + 1 < size; i++) buffer[o++] = '0';
105
+ } else {
106
+ for (int i = 0; i < intDigits && o + 1 < size; i++) buffer[o++] = digits[i];
107
+ if (o + 1 < size) buffer[o++] = '.';
108
+ for (int i = intDigits; i < nd && o + 1 < size; i++) buffer[o++] = digits[i];
109
+ }
110
+ } else {
111
+ if (o + 1 < size) buffer[o++] = '0';
112
+ if (o + 1 < size) buffer[o++] = '.';
113
+ for (int i = 0; i < -exp10 - 1 && o + 1 < size; i++) buffer[o++] = '0';
114
+ for (int i = 0; i < nd && o + 1 < size; i++) buffer[o++] = digits[i];
115
+ }
116
+ buffer[o] = '\0';
117
+ }
118
+
119
+ /* Split a `%e`-formatted string into its significant digits and exponent. */
120
+ static void xt_parse_scientific(const char *sci, char *digits, int *nd, int *exp10) {
121
+ int n = 0;
122
+ *exp10 = 0;
123
+ const char *q = sci;
124
+ while (*q && *q != 'e' && *q != 'E') {
125
+ if (*q >= '0' && *q <= '9') digits[n++] = *q;
126
+ q++;
127
+ }
128
+ if (*q == 'e' || *q == 'E') *exp10 = atoi(q + 1);
129
+ *nd = n;
130
+ }
131
+
132
+ void xt_number_to_js_string(double d, char *buffer, size_t size) {
86
133
  if (isnan(d)) {
87
134
  snprintf(buffer, size, "NaN");
88
135
  return;
@@ -95,14 +142,121 @@ static void xt_format_double(double d, char *buffer, size_t size) {
95
142
  snprintf(buffer, size, "0");
96
143
  return;
97
144
  }
98
- if (d == floor(d) && fabs(d) < 1e21) {
99
- snprintf(buffer, size, "%.0f", d);
145
+ int negative = d < 0;
146
+ double x = negative ? -d : d;
147
+ char sci[64];
148
+ for (int p = 1; p <= 17; p++) {
149
+ snprintf(sci, sizeof(sci), "%.*e", p - 1, x);
150
+ if (strtod(sci, NULL) == x) break;
151
+ }
152
+ char digits[32];
153
+ int nd = 0;
154
+ int exp10 = 0;
155
+ xt_parse_scientific(sci, digits, &nd, &exp10);
156
+ while (nd > 1 && digits[nd - 1] == '0') nd--;
157
+ xt_render_decimal(negative, digits, nd, exp10, exp10 < -6 || exp10 >= 21, buffer, size);
158
+ }
159
+
160
+ void xt_number_to_exponential(double d, int precision, char *buffer, size_t size) {
161
+ if (isnan(d)) {
162
+ snprintf(buffer, size, "NaN");
163
+ return;
164
+ }
165
+ if (isinf(d)) {
166
+ snprintf(buffer, size, d < 0 ? "-Infinity" : "Infinity");
167
+ return;
168
+ }
169
+ int negative = d < 0;
170
+ double x = negative ? -d : d;
171
+ char sci[64];
172
+ if (precision < 0) {
173
+ /* No fractionDigits argument: use the shortest round-tripping form. */
174
+ for (int p = 1; p <= 17; p++) {
175
+ snprintf(sci, sizeof(sci), "%.*e", p - 1, x);
176
+ if (strtod(sci, NULL) == x) break;
177
+ }
178
+ } else {
179
+ snprintf(sci, sizeof(sci), "%.*e", precision, x);
180
+ }
181
+ char digits[64];
182
+ int nd = 0;
183
+ int exp10 = 0;
184
+ xt_parse_scientific(sci, digits, &nd, &exp10);
185
+ if (precision < 0) while (nd > 1 && digits[nd - 1] == '0') nd--;
186
+ xt_render_decimal(negative, digits, nd, exp10, 1, buffer, size);
187
+ }
188
+
189
+ void xt_number_to_precision(double d, int precision, char *buffer, size_t size) {
190
+ if (isnan(d)) {
191
+ snprintf(buffer, size, "NaN");
192
+ return;
193
+ }
194
+ if (isinf(d)) {
195
+ snprintf(buffer, size, d < 0 ? "-Infinity" : "Infinity");
196
+ return;
197
+ }
198
+ int negative = d < 0;
199
+ double x = negative ? -d : d;
200
+ char sci[512];
201
+ snprintf(sci, sizeof(sci), "%.*e", precision - 1, x);
202
+ char digits[512];
203
+ int nd = 0;
204
+ int exp10 = 0;
205
+ xt_parse_scientific(sci, digits, &nd, &exp10);
206
+ xt_render_decimal(negative, digits, nd, exp10, exp10 < -6 || exp10 >= precision, buffer, size);
207
+ }
208
+
209
+ void xt_number_to_fixed(double d, int precision, char *buffer, size_t size) {
210
+ if (isnan(d)) {
211
+ snprintf(buffer, size, "NaN");
212
+ return;
213
+ }
214
+ if (isinf(d)) {
215
+ snprintf(buffer, size, d < 0 ? "-Infinity" : "Infinity");
216
+ return;
217
+ }
218
+ if (fabs(d) >= 1e21) {
219
+ xt_number_to_js_string(d, buffer, size);
100
220
  return;
101
221
  }
102
- for (int precision = 1; precision <= 17; precision++) {
103
- snprintf(buffer, size, "%.*g", precision, d);
104
- if (strtod(buffer, NULL) == d) return;
222
+ int negative = d < 0;
223
+ double x = negative ? -d : d;
224
+ /* 20 guard digits let us round exactly half away from zero, which is what
225
+ ECMAScript specifies and what `printf`'s round-half-to-even does not. */
226
+ char raw[1024];
227
+ snprintf(raw, sizeof(raw), "%.*f", precision + 20, x);
228
+ const char *dot = strchr(raw, '.');
229
+ int intLen = dot ? (int)(dot - raw) : (int)strlen(raw);
230
+ int len = intLen;
231
+ if (precision > 0) len += 1 + precision;
232
+ if (len >= (int)sizeof(raw)) len = (int)sizeof(raw) - 1;
233
+ char work[1024];
234
+ memcpy(work, raw, (size_t)len);
235
+ work[len] = '\0';
236
+ /* The digit that decides rounding is the first one we dropped. With a
237
+ precision of zero that is the character after the decimal point. */
238
+ int firstDropped = precision > 0 ? len : (dot ? intLen + 1 : len);
239
+ if (dot && raw[firstDropped] >= '5') {
240
+ int i = len - 1;
241
+ while (i >= 0) {
242
+ if (work[i] == '.') {
243
+ i--;
244
+ continue;
245
+ }
246
+ if (work[i] < '9') {
247
+ work[i]++;
248
+ break;
249
+ }
250
+ work[i] = '0';
251
+ i--;
252
+ }
253
+ if (i < 0) {
254
+ memmove(work + 1, work, strlen(work) + 1);
255
+ work[0] = '1';
256
+ }
105
257
  }
258
+ if (negative) snprintf(buffer, size, "-%s", work);
259
+ else snprintf(buffer, size, "%s", work);
106
260
  }
107
261
 
108
262
  /* ------------------------------------------------------------------------- */
@@ -131,18 +285,39 @@ double xt_to_number(xt_value v) {
131
285
  if (XT_IS_BIGINT(v)) return xt_bigint_to_double_value(v);
132
286
  if (XT_IS_STRING(v)) {
133
287
  xt_string *s = xt_as_string(v);
134
- if (s->length == 0) return 0.0;
135
- char *end = NULL;
136
- double parsed = strtod(s->data, &end);
137
- if (end != s->data + s->length) return NAN;
138
- return parsed;
288
+ const char *begin = s->data;
289
+ const char *end = s->data + s->length;
290
+ while (begin < end && isspace((unsigned char)*begin)) begin++;
291
+ while (end > begin && isspace((unsigned char)end[-1])) end--;
292
+ size_t length = (size_t)(end - begin);
293
+ if (length == 0) return 0.0;
294
+ char stackbuf[128];
295
+ char *tmp = length < sizeof(stackbuf) ? stackbuf : (char *)malloc(length + 1);
296
+ memcpy(tmp, begin, length);
297
+ tmp[length] = '\0';
298
+ double result;
299
+ int base = 0;
300
+ if (length > 2 && tmp[0] == '0' && (tmp[1] == 'x' || tmp[1] == 'X')) base = 16;
301
+ else if (length > 2 && tmp[0] == '0' && (tmp[1] == 'o' || tmp[1] == 'O')) base = 8;
302
+ else if (length > 2 && tmp[0] == '0' && (tmp[1] == 'b' || tmp[1] == 'B')) base = 2;
303
+ if (base != 0) {
304
+ char *stop = NULL;
305
+ unsigned long long parsed = strtoull(tmp + 2, &stop, base);
306
+ result = (*stop == '\0' && stop != tmp + 2) ? (double)parsed : NAN;
307
+ } else {
308
+ char *stop = NULL;
309
+ result = strtod(tmp, &stop);
310
+ if (stop != tmp + length) result = NAN;
311
+ }
312
+ if (tmp != stackbuf) free(tmp);
313
+ return result;
139
314
  }
140
315
  return NAN;
141
316
  }
142
317
 
143
318
  static xt_value xt_number_to_string_value(double d) {
144
319
  char buffer[64];
145
- xt_format_double(d, buffer, sizeof(buffer));
320
+ xt_number_to_js_string(d, buffer, sizeof(buffer));
146
321
  return xt_string_from_cstr(buffer);
147
322
  }
148
323
 
@@ -178,6 +353,13 @@ xt_value xt_to_string(xt_value v) {
178
353
  return result;
179
354
  }
180
355
  if (XT_IS_FUNCTION(v)) return xt_string_from_cstr("function () { [native code] }");
356
+ if (xt_is_symbol(v)) {
357
+ xt_throw(xt_string_from_cstr("TypeError: Cannot convert a Symbol value to a string"));
358
+ }
359
+ if (XT_IS_OBJECT(v) &&
360
+ ((xt_object *)XT_GET_PTR(v))->header.kind == XT_OBJECT_KIND_ERROR) {
361
+ return xt_error_to_string(v);
362
+ }
181
363
  return xt_string_from_cstr("[object Object]");
182
364
  }
183
365
 
@@ -189,6 +371,7 @@ xt_value xt_typeof(xt_value v) {
189
371
  if (v == XT_UNDEFINED) return xt_string_from_cstr("undefined");
190
372
  if (XT_IS_FUNCTION(v)) return xt_string_from_cstr("function");
191
373
  if (v == XT_NULL) return xt_string_from_cstr("object");
374
+ if (xt_is_symbol(v)) return xt_string_from_cstr("symbol");
192
375
  return xt_string_from_cstr("object");
193
376
  }
194
377
 
@@ -200,7 +383,15 @@ static void xt_throw_mixed_bigint(void) {
200
383
  xt_throw(xt_string_from_cstr("TypeError: Cannot mix BigInt and other types, use explicit conversions"));
201
384
  }
202
385
 
386
+ /* ToPrimitive for the operators: arrays/objects/functions stringify (their
387
+ `valueOf` is the object itself, so `toString` decides). */
388
+ static int xt_is_object_like(xt_value v) { return XT_IS_OBJECT(v) || XT_IS_ARRAY(v) || XT_IS_FUNCTION(v); }
389
+
390
+ static xt_value xt_to_primitive(xt_value v) { return xt_is_object_like(v) ? xt_to_string(v) : v; }
391
+
203
392
  xt_value xt_add(xt_value a, xt_value b) {
393
+ a = xt_to_primitive(a);
394
+ b = xt_to_primitive(b);
204
395
  if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) {
205
396
  return xt_number(xt_to_double(a) + xt_to_double(b));
206
397
  }
@@ -364,6 +555,8 @@ static int xt_compare_numeric(xt_value a, xt_value b) {
364
555
  }
365
556
 
366
557
  xt_value xt_lt(xt_value a, xt_value b) {
558
+ a = xt_to_primitive(a);
559
+ b = xt_to_primitive(b);
367
560
  if (XT_IS_STRING(a) && XT_IS_STRING(b)) {
368
561
  xt_string *x = xt_as_string(a);
369
562
  xt_string *y = xt_as_string(b);
@@ -377,6 +570,8 @@ xt_value xt_lt(xt_value a, xt_value b) {
377
570
  }
378
571
 
379
572
  xt_value xt_le(xt_value a, xt_value b) {
573
+ a = xt_to_primitive(a);
574
+ b = xt_to_primitive(b);
380
575
  if (XT_IS_STRING(a) && XT_IS_STRING(b)) {
381
576
  xt_string *x = xt_as_string(a);
382
577
  xt_string *y = xt_as_string(b);
@@ -396,10 +591,21 @@ xt_value xt_gt(xt_value a, xt_value b) { return xt_lt(b, a); }
396
591
  xt_value xt_ge(xt_value a, xt_value b) { return xt_le(b, a); }
397
592
 
398
593
  static int xt_loose_equals(xt_value a, xt_value b) {
594
+ if (xt_is_symbol(a) || xt_is_symbol(b)) return a == b;
399
595
  if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) return xt_to_double(a) == xt_to_double(b);
400
596
  if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_string_equals(xt_as_string(a), xt_as_string(b));
401
597
  if (a == b) return 1;
402
598
  if ((a == XT_NULL && b == XT_UNDEFINED) || (a == XT_UNDEFINED && b == XT_NULL)) return 1;
599
+ /* Abstract equality converts an object operand to a primitive (via
600
+ ToPrimitive) before comparing, so `[] == 0`, `[1] == 1` and
601
+ `{} == "[object Object]"` are all true. */
602
+ if (XT_IS_OBJECT(a) || XT_IS_ARRAY(a) || XT_IS_FUNCTION(a)) {
603
+ if (XT_IS_OBJECT(b) || XT_IS_ARRAY(b) || XT_IS_FUNCTION(b)) return 0;
604
+ return xt_loose_equals(xt_to_string(a), b);
605
+ }
606
+ if (XT_IS_OBJECT(b) || XT_IS_ARRAY(b) || XT_IS_FUNCTION(b)) {
607
+ return xt_loose_equals(a, xt_to_string(b));
608
+ }
403
609
  if (XT_IS_BIGINT(a) || XT_IS_BIGINT(b)) {
404
610
  xt_value big = XT_IS_BIGINT(a) ? a : b;
405
611
  xt_value other = XT_IS_BIGINT(a) ? b : a;
@@ -73,7 +73,7 @@ function compile(sources: readonly string[], extension: string): string[] {
73
73
  const object = join(objDir, `${extension}_${index}_${basename(source, ".c")}.o`);
74
74
  const result = spawnSync(
75
75
  clang,
76
- ["-O2", "-D_CRT_SECURE_NO_WARNINGS", "-c", source, "-o", object, `-I${runtimeDir}`],
76
+ ["-O2", "-Wall", "-Wextra", "-D_CRT_SECURE_NO_WARNINGS", "-c", source, "-o", object, `-I${runtimeDir}`],
77
77
  { stdio: "inherit", env: childEnv },
78
78
  );
79
79
  if (result.status !== 0) {
@@ -55,11 +55,15 @@ export interface NoSubstitutionTemplateLiteral extends Node {
55
55
  readonly kind: SyntaxKind.NoSubstitutionTemplateLiteral;
56
56
  readonly text: string;
57
57
  readonly value: string;
58
+ /** Raw text between the backticks (escapes preserved), for tagged templates. */
59
+ readonly raw?: string;
58
60
  }
59
61
 
60
62
  export interface TemplateLiteral extends Node {
61
63
  readonly kind: SyntaxKind.TemplateLiteral;
62
64
  readonly head: string;
65
+ /** Raw text of the head chunk, for tagged templates. */
66
+ readonly raw?: string;
63
67
  readonly spans: TemplateSpan[];
64
68
  }
65
69
 
@@ -67,6 +71,8 @@ export interface TemplateSpan extends Node {
67
71
  readonly kind: SyntaxKind.TemplateSpan;
68
72
  readonly expression: Expression;
69
73
  readonly literal: string;
74
+ /** Raw text of the chunk that follows this substitution. */
75
+ readonly raw?: string;
70
76
  /** True when this span terminates the template (tail) rather than continues it. */
71
77
  readonly isTail: boolean;
72
78
  }
@@ -108,6 +114,8 @@ export interface PropertyAssignment extends Node {
108
114
  readonly kind: SyntaxKind.PropertyAssignment;
109
115
  readonly name: PropertyName;
110
116
  readonly initializer: Expression;
117
+ /** `get x() {}` / `set x(v) {}` in an object literal. */
118
+ readonly accessor?: "get" | "set";
111
119
  }
112
120
 
113
121
  export interface ShorthandPropertyAssignment extends Node {
@@ -108,6 +108,7 @@ export class Binder {
108
108
  isModule,
109
109
  isArrow,
110
110
  isAsync: (((node as { flags?: number }).flags ?? 0) & NodeFlags.Async) !== 0,
111
+ isGenerator: (((node as { flags?: number }).flags ?? 0) & NodeFlags.Generator) !== 0,
111
112
  usesThis: false,
112
113
  capturesThis: false,
113
114
  };
@@ -120,7 +121,6 @@ export class Binder {
120
121
  }
121
122
  }
122
123
 
123
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
124
124
  export interface Binder extends DeclarationMethods, ReferenceMethods {}
125
125
 
126
126
  Object.assign(Binder.prototype, declarationMethods, referenceMethods);
@@ -49,7 +49,7 @@ export interface ReferenceMethods {
49
49
  bindNode(this: Binder, node: Node | undefined, scope: Scope): void;
50
50
  bindVariableList(this: Binder, list: VariableDeclarationList, scope: Scope): void;
51
51
  bindBindingPattern(this: Binder, name: BindingName, scope: Scope): void;
52
- bindFunction(this: Binder, node: FunctionDeclaration | FunctionExpression | ArrowFunction, parentScope: Scope, symbol: SymbolInfo | undefined): void;
52
+ bindFunction(this: Binder, node: FunctionDeclaration | FunctionExpression | ArrowFunction, parentScope: Scope, symbol: SymbolInfo | undefined, inferredName?: string): void;
53
53
  bindClass(this: Binder, node: ClassDeclaration | ClassExpression, scope: Scope): void;
54
54
  createClassFunction(this: Binder, member: Node, name: string, parameters: readonly Parameter[], body: Block | undefined, parentScope: Scope, info: ClassInfo, isStatic: boolean, isConstructor: boolean): FunctionInfo;
55
55
  reference(this: Binder, identifier: Identifier, scope: Scope): void;
@@ -80,7 +80,6 @@ export const referenceMethods: ReferenceMethods = {
80
80
  case SyntaxKind.FunctionDeclaration: {
81
81
  const fn = node as FunctionDeclaration;
82
82
  const symbol = this.symbolOfDeclaration.get(fn);
83
- if (symbol) (symbol.declarations as Node[]).length; // keep declaration mapping warm
84
83
  this.bindFunction(fn, scope, symbol);
85
84
  return;
86
85
  }
@@ -108,7 +107,24 @@ export const referenceMethods: ReferenceMethods = {
108
107
  if (property.name.kind === SyntaxKind.ComputedPropertyName) {
109
108
  this.bindNode((property.name as unknown as { expression: Expression }).expression, scope);
110
109
  }
111
- this.bindNode(property.initializer, scope);
110
+ const initializer = property.initializer;
111
+ if (
112
+ initializer.kind === SyntaxKind.FunctionExpression ||
113
+ initializer.kind === SyntaxKind.ArrowFunction
114
+ ) {
115
+ // Infer the function name from the property key (`{ sum() {} }`).
116
+ const accessor = (property as unknown as { accessor?: "get" | "set" }).accessor;
117
+ const key = classMemberName(property.name);
118
+ const inferred = accessor ? `${accessor} ${key}` : key;
119
+ this.bindFunction(
120
+ initializer as FunctionExpression | ArrowFunction,
121
+ scope,
122
+ undefined,
123
+ inferred,
124
+ );
125
+ } else {
126
+ this.bindNode(initializer, scope);
127
+ }
112
128
  return;
113
129
  }
114
130
  case SyntaxKind.ShorthandPropertyAssignment: {
@@ -202,7 +218,23 @@ export const referenceMethods: ReferenceMethods = {
202
218
  } else {
203
219
  this.bindBindingPattern(declaration.name, scope);
204
220
  }
205
- this.bindNode(declaration.initializer, scope);
221
+ const initializer = declaration.initializer;
222
+ if (
223
+ declaration.name.kind === SyntaxKind.Identifier &&
224
+ initializer &&
225
+ (initializer.kind === SyntaxKind.FunctionExpression ||
226
+ initializer.kind === SyntaxKind.ArrowFunction)
227
+ ) {
228
+ // Infer the function name from the variable it is assigned to.
229
+ this.bindFunction(
230
+ initializer as FunctionExpression | ArrowFunction,
231
+ scope,
232
+ undefined,
233
+ (declaration.name as Identifier).text,
234
+ );
235
+ } else {
236
+ this.bindNode(initializer, scope);
237
+ }
206
238
  }
207
239
  },
208
240
 
@@ -221,6 +253,7 @@ export const referenceMethods: ReferenceMethods = {
221
253
  node: FunctionDeclaration | FunctionExpression | ArrowFunction,
222
254
  parentScope: Scope,
223
255
  symbol: SymbolInfo | undefined,
256
+ inferredName?: string,
224
257
  ): void {
225
258
  const name =
226
259
  node.kind === SyntaxKind.FunctionDeclaration
@@ -230,6 +263,9 @@ export const referenceMethods: ReferenceMethods = {
230
263
  : "(arrow)";
231
264
  const parentFn = this.current;
232
265
  const fn = this.createFunction(name, node, parentFn, false, node.kind === SyntaxKind.ArrowFunction);
266
+ if (inferredName && (name === "(anonymous)" || name === "(arrow)")) {
267
+ (fn as { name: string }).name = inferredName;
268
+ }
233
269
  this.current = fn;
234
270
 
235
271
  const scope = this.createScope(ScopeKind.Function, node, parentScope);
@@ -309,6 +345,13 @@ export const referenceMethods: ReferenceMethods = {
309
345
  const method = member as MethodDeclaration;
310
346
  const isStatic = method.modifiers.some((modifier) => modifier.modifierKind === ModifierKind.Static);
311
347
  const fn = this.createClassFunction(method, classMemberName(method.name), method.parameters, method.body, classScope, info, isStatic, false);
348
+ if (method.name.kind === SyntaxKind.ComputedPropertyName) {
349
+ // Computed member names are evaluated in the enclosing scope when
350
+ // the class is defined, so bind them there.
351
+ const keyExpression = (method.name as unknown as { expression: Expression }).expression;
352
+ this.bindNode(keyExpression, scope);
353
+ (fn as MutableFunction & { computedKey: Expression }).computedKey = keyExpression;
354
+ }
312
355
  if (isStatic) (info.statics as FunctionInfo[]).push(fn);
313
356
  else (info.methods as FunctionInfo[]).push(fn);
314
357
  }
@@ -61,12 +61,16 @@ export interface FunctionInfo {
61
61
  readonly isArrow: boolean;
62
62
  /** True when the function was declared `async`. */
63
63
  readonly isAsync: boolean;
64
+ /** True for `function*` / generator methods; calling one creates a generator. */
65
+ readonly isGenerator: boolean;
64
66
  /** True when this function's body mentions `this` directly. */
65
67
  usesThis: boolean;
66
68
  /** True when an arrow function must receive `this` from its enclosing scope. */
67
69
  capturesThis: boolean;
68
70
  /** Set for class methods/constructors; carries the owning class. */
69
71
  classInfo?: ClassInfo;
72
+ /** Computed member name (`[expr]() {}`), evaluated at class definition. */
73
+ computedKey?: Expression;
70
74
  /** True for `static` class members. */
71
75
  isStatic?: boolean;
72
76
  /** True for an explicit `constructor`. */
package/src/cli/main.ts CHANGED
@@ -13,9 +13,8 @@
13
13
 
14
14
  import { spawnSync } from "node:child_process";
15
15
  import { readFileSync } from "node:fs";
16
- import { resolve } from "node:path";
17
16
  import { pathToFileURL } from "node:url";
18
- import { DiagnosticBag, formatDiagnostic, type Diagnostic } from "../diagnostics/diagnostic.js";
17
+ import { formatDiagnostic, type Diagnostic } from "../diagnostics/diagnostic.js";
19
18
  import { SourceFile } from "../diagnostics/source.js";
20
19
  import { build, compileEntry, COMPILER_VERSION, type EmitKind } from "../driver/compiler.js";
21
20
  import { findRuntimeDir, platformSlug } from "../driver/paths.js";