xbintsc 0.2.4 → 0.2.6
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/declarations.d.ts +20 -1
- package/dist/src/ast/expressions.d.ts +6 -2
- package/dist/src/ast/kinds.d.ts +6 -1
- package/dist/src/ast/kinds.js +5 -0
- package/dist/src/ast/kinds.js.map +1 -1
- package/dist/src/ast/statements.d.ts +2 -1
- package/dist/src/binder/binder.js +73 -9
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/cli/main.js +2 -4
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls.d.ts +1 -1
- package/dist/src/codegen/generator/calls.js +57 -14
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +1 -1
- package/dist/src/codegen/generator/expressions.js +39 -3
- package/dist/src/codegen/generator/expressions.js.map +1 -1
- package/dist/src/codegen/generator/module.js +73 -7
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +16 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements.d.ts +4 -1
- package/dist/src/codegen/generator/statements.js +141 -10
- package/dist/src/codegen/generator/statements.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +3 -0
- package/dist/src/codegen/generator/tables.js +13 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/codegen/values.d.ts +18 -11
- package/dist/src/codegen/values.js +92 -15
- package/dist/src/codegen/values.js.map +1 -1
- package/dist/src/diagnostics/source.d.ts +1 -1
- package/dist/src/diagnostics/source.js +12 -7
- package/dist/src/diagnostics/source.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +5 -0
- package/dist/src/driver/compiler.js +42 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.js +29 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +7 -0
- package/dist/src/extensions/node/child_process/index.js +19 -0
- package/dist/src/extensions/node/child_process/index.js.map +1 -0
- package/dist/src/extensions/node/crypto/index.d.ts +6 -0
- package/dist/src/extensions/node/crypto/index.js +18 -0
- package/dist/src/extensions/node/crypto/index.js.map +1 -0
- package/dist/src/extensions/node/index.js +6 -0
- package/dist/src/extensions/node/index.js.map +1 -1
- package/dist/src/extensions/node/module.d.ts +9 -1
- package/dist/src/extensions/node/module.js +14 -1
- package/dist/src/extensions/node/module.js.map +1 -1
- package/dist/src/extensions/node/url/index.d.ts +7 -0
- package/dist/src/extensions/node/url/index.js +21 -0
- package/dist/src/extensions/node/url/index.js.map +1 -0
- package/dist/src/lexer/scanner.js +4 -5
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/lexer/token.d.ts +10 -0
- package/dist/src/lexer/token.js +69 -0
- package/dist/src/lexer/token.js.map +1 -1
- package/dist/src/parser/context.js +4 -16
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/declarations.d.ts +2 -0
- package/dist/src/parser/declarations.js +146 -33
- package/dist/src/parser/declarations.js.map +1 -1
- package/dist/src/parser/expressions.js +20 -3
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -0
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/modules.js +7 -2
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/statements.js +28 -3
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +5 -0
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +16 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +13 -1
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +57 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/parser/parser.test.js +53 -0
- package/dist/tests/parser/parser.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/ext_node/child_process/child_process.c +213 -0
- package/runtime/ext_node/crypto/crypto.c +177 -0
- package/runtime/ext_node/process/process.c +37 -3
- package/runtime/ext_node/url/url.c +85 -0
- package/runtime/rt.h +20 -0
- package/runtime/rt_internal.h +11 -0
- package/runtime/xt_builtins.c +23 -0
- package/runtime/xt_containers.c +136 -10
- package/runtime/xt_io.c +53 -0
- package/runtime/xt_regex.h +16 -6
- package/runtime/xt_stdlib.c +11 -2
- package/runtime/xt_stdlib2.c +250 -13
- package/runtime/xt_values.c +2 -1
- package/src/ast/declarations.ts +26 -1
- package/src/ast/expressions.ts +7 -2
- package/src/ast/kinds.ts +5 -0
- package/src/ast/statements.ts +2 -1
- package/src/binder/binder.ts +75 -9
- package/src/cli/main.ts +2 -3
- package/src/codegen/generator/calls.ts +59 -16
- package/src/codegen/generator/context.ts +1 -1
- package/src/codegen/generator/expressions.ts +39 -3
- package/src/codegen/generator/module.ts +79 -7
- package/src/codegen/generator/state.ts +16 -0
- package/src/codegen/generator/statements.ts +139 -11
- package/src/codegen/generator/tables.ts +14 -0
- package/src/codegen/values.ts +85 -16
- package/src/diagnostics/source.ts +12 -7
- package/src/driver/compiler.ts +44 -2
- package/src/driver/modules.ts +28 -3
- package/src/extensions/node/child_process/index.ts +24 -0
- package/src/extensions/node/crypto/index.ts +23 -0
- package/src/extensions/node/index.ts +6 -0
- package/src/extensions/node/module.ts +13 -1
- package/src/extensions/node/url/index.ts +26 -0
- package/src/lexer/scanner.ts +4 -4
- package/src/lexer/token.ts +73 -0
- package/src/parser/context.ts +4 -15
- package/src/parser/declarations.ts +140 -29
- package/src/parser/expressions.ts +20 -4
- package/src/parser/helpers.ts +2 -0
- package/src/parser/modules.ts +7 -2
- package/src/parser/statements.ts +27 -3
- package/src/parser/types.ts +5 -0
package/runtime/xt_builtins.c
CHANGED
|
@@ -100,6 +100,29 @@ xt_value xt_boolean_ctor(int32_t argc, xt_value *argv) {
|
|
|
100
100
|
return xt_bool(xt_truthy(argv[0]));
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/*
|
|
104
|
+
* Builtin function values. Global constructors like `Boolean` are usually
|
|
105
|
+
* called directly, but JavaScript also allows passing them around
|
|
106
|
+
* (`arr.filter(Boolean)`). These trampolines adapt the uniform `(argc, argv)`
|
|
107
|
+
* builtin ABI to the closure ABI so the codegen can wrap them with
|
|
108
|
+
* `xt_closure_new`.
|
|
109
|
+
*/
|
|
110
|
+
#define XT_BUILTIN_TRAMPOLINE(name, target) \
|
|
111
|
+
xt_value name(xt_value thisValue, xt_value env, int32_t argc, \
|
|
112
|
+
xt_value *argv) { \
|
|
113
|
+
(void)thisValue; \
|
|
114
|
+
(void)env; \
|
|
115
|
+
return target(argc, argv); \
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_boolean, xt_boolean_ctor)
|
|
119
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_number, xt_number_ctor)
|
|
120
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_string, xt_string_ctor)
|
|
121
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_parseInt, xt_parse_int)
|
|
122
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_parseFloat, xt_parse_float)
|
|
123
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_isNaN, xt_is_nan)
|
|
124
|
+
XT_BUILTIN_TRAMPOLINE(xt_builtin_value_isFinite, xt_is_finite)
|
|
125
|
+
|
|
103
126
|
xt_value xt_in(xt_value key, xt_value value) {
|
|
104
127
|
if (XT_IS_OBJECT(value)) return xt_object_has(value, key);
|
|
105
128
|
if (XT_IS_ARRAY(value)) {
|
package/runtime/xt_containers.c
CHANGED
|
@@ -63,16 +63,40 @@ xt_property *xt_object_find_property(xt_object *obj, xt_string *key) {
|
|
|
63
63
|
return obj ? xt_object_find(obj, key) : NULL;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
static int xt_key_is_prototype(xt_value key) {
|
|
67
|
+
xt_string *k = xt_as_string(xt_to_string(key));
|
|
68
|
+
return k && k->length == 9 && memcmp(k->data, "prototype", 9) == 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
xt_value xt_object_get(xt_value value, xt_value key) {
|
|
72
|
+
/* A function's `.prototype` lives in a dedicated field (set by
|
|
73
|
+
`xt_function_set_prototype`), not in its property bag. Expose it as a
|
|
74
|
+
normal property so `Class.prototype` returns the object instances use. */
|
|
75
|
+
if (XT_IS_FUNCTION(value) && xt_key_is_prototype(key)) {
|
|
76
|
+
return ((xt_function *)XT_GET_PTR(value))->prototype;
|
|
77
|
+
}
|
|
67
78
|
xt_object *obj = xt_as_object(value);
|
|
68
79
|
if (!obj) return XT_UNDEFINED;
|
|
80
|
+
/* Only plain objects (and function property bags) use the `xt_object`
|
|
81
|
+
layout. Containers such as Map/Set/Date/RegExp/Promise/Array must not be
|
|
82
|
+
interpreted as property tables, or property lookup reads past the end of
|
|
83
|
+
their real representation. */
|
|
84
|
+
if (obj->header.kind != XT_OBJECT_KIND_OBJECT) return XT_UNDEFINED;
|
|
69
85
|
xt_value keyString = xt_to_string(key);
|
|
70
86
|
xt_string *k = xt_as_string(keyString);
|
|
71
87
|
/* Walk the prototype chain like JavaScript property lookup. */
|
|
72
88
|
xt_object *cur = obj;
|
|
73
89
|
while (cur) {
|
|
74
90
|
xt_property *prop = xt_object_find(cur, k);
|
|
75
|
-
if (prop)
|
|
91
|
+
if (prop) {
|
|
92
|
+
/* Invoke accessors with the original receiver as `this`, so getters
|
|
93
|
+
inherited from a prototype still see the instance. */
|
|
94
|
+
if (prop->getter != XT_UNDEFINED && XT_IS_FUNCTION(prop->getter)) {
|
|
95
|
+
return xt_call_with_this(prop->getter, value, 0, NULL);
|
|
96
|
+
}
|
|
97
|
+
if (prop->getter != XT_UNDEFINED) return XT_UNDEFINED;
|
|
98
|
+
return prop->value;
|
|
99
|
+
}
|
|
76
100
|
if (!XT_IS_OBJECT(cur->prototype)) break;
|
|
77
101
|
cur = (xt_object *)XT_GET_PTR(cur->prototype);
|
|
78
102
|
}
|
|
@@ -80,22 +104,82 @@ xt_value xt_object_get(xt_value value, xt_value key) {
|
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
107
|
+
if (XT_IS_FUNCTION(value) && xt_key_is_prototype(key)) {
|
|
108
|
+
return xt_function_set_prototype(value, newValue);
|
|
109
|
+
}
|
|
83
110
|
xt_object *obj = xt_as_object(value);
|
|
84
111
|
if (!obj) return newValue;
|
|
112
|
+
if (obj->header.kind != XT_OBJECT_KIND_OBJECT) return newValue;
|
|
85
113
|
if (obj->frozen) return newValue;
|
|
86
114
|
xt_value keyString = xt_to_string(key);
|
|
87
|
-
|
|
115
|
+
xt_string *k = xt_as_string(keyString);
|
|
116
|
+
xt_property *prop = xt_object_find(obj, k);
|
|
88
117
|
if (prop) {
|
|
118
|
+
if (prop->setter != XT_UNDEFINED) {
|
|
119
|
+
if (XT_IS_FUNCTION(prop->setter)) {
|
|
120
|
+
xt_value argument = newValue;
|
|
121
|
+
xt_call_with_this(prop->setter, value, 1, &argument);
|
|
122
|
+
}
|
|
123
|
+
return newValue;
|
|
124
|
+
}
|
|
125
|
+
if (prop->getter != XT_UNDEFINED) return newValue; /* accessor without setter */
|
|
89
126
|
prop->value = newValue;
|
|
90
127
|
return newValue;
|
|
91
128
|
}
|
|
129
|
+
/* No own property: an inherited accessor still intercepts the assignment
|
|
130
|
+
(JavaScript assigns through prototype setters instead of shadowing). */
|
|
131
|
+
xt_object *ancestor = XT_IS_OBJECT(obj->prototype) ? (xt_object *)XT_GET_PTR(obj->prototype) : NULL;
|
|
132
|
+
while (ancestor) {
|
|
133
|
+
xt_property *inherited = xt_object_find(ancestor, k);
|
|
134
|
+
if (inherited) {
|
|
135
|
+
if (inherited->setter != XT_UNDEFINED) {
|
|
136
|
+
if (XT_IS_FUNCTION(inherited->setter)) {
|
|
137
|
+
xt_value argument = newValue;
|
|
138
|
+
xt_call_with_this(inherited->setter, value, 1, &argument);
|
|
139
|
+
}
|
|
140
|
+
return newValue;
|
|
141
|
+
}
|
|
142
|
+
if (inherited->getter != XT_UNDEFINED) return newValue; /* getter-only accessor */
|
|
143
|
+
break; /* plain inherited data property: create an own shadowing property */
|
|
144
|
+
}
|
|
145
|
+
ancestor = XT_IS_OBJECT(ancestor->prototype) ? (xt_object *)XT_GET_PTR(ancestor->prototype) : NULL;
|
|
146
|
+
}
|
|
92
147
|
xt_object_reserve(obj, obj->count + 1);
|
|
93
|
-
obj->properties[obj->count].key =
|
|
148
|
+
obj->properties[obj->count].key = k;
|
|
94
149
|
obj->properties[obj->count].value = newValue;
|
|
150
|
+
obj->properties[obj->count].getter = XT_UNDEFINED;
|
|
151
|
+
obj->properties[obj->count].setter = XT_UNDEFINED;
|
|
95
152
|
obj->count++;
|
|
96
153
|
return newValue;
|
|
97
154
|
}
|
|
98
155
|
|
|
156
|
+
static xt_property *xt_object_ensure_property(xt_object *obj, xt_value keyString) {
|
|
157
|
+
xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
|
|
158
|
+
if (prop) return prop;
|
|
159
|
+
xt_object_reserve(obj, obj->count + 1);
|
|
160
|
+
prop = &obj->properties[obj->count];
|
|
161
|
+
prop->key = xt_as_string(keyString);
|
|
162
|
+
prop->value = XT_UNDEFINED;
|
|
163
|
+
prop->getter = XT_UNDEFINED;
|
|
164
|
+
prop->setter = XT_UNDEFINED;
|
|
165
|
+
obj->count++;
|
|
166
|
+
return prop;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
xt_value xt_object_define_getter(xt_value value, xt_value key, xt_value getter) {
|
|
170
|
+
xt_object *obj = xt_as_object(value);
|
|
171
|
+
if (!obj) return value;
|
|
172
|
+
xt_object_ensure_property(obj, xt_to_string(key))->getter = getter;
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
xt_value xt_object_define_setter(xt_value value, xt_value key, xt_value setter) {
|
|
177
|
+
xt_object *obj = xt_as_object(value);
|
|
178
|
+
if (!obj) return value;
|
|
179
|
+
xt_object_ensure_property(obj, xt_to_string(key))->setter = setter;
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
|
|
99
183
|
xt_value xt_object_get_prototype(xt_value value) {
|
|
100
184
|
xt_object *obj = xt_as_object(value);
|
|
101
185
|
if (obj) return obj->prototype;
|
|
@@ -248,21 +332,47 @@ xt_value xt_array_length(xt_value value) {
|
|
|
248
332
|
}
|
|
249
333
|
|
|
250
334
|
xt_value xt_array_spread(xt_value target, xt_value source) {
|
|
251
|
-
if (!XT_IS_ARRAY(target)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
335
|
+
if (!XT_IS_ARRAY(target)) return target;
|
|
336
|
+
if (XT_IS_ARRAY(source)) {
|
|
337
|
+
xt_array *dst = (xt_array *)XT_GET_PTR(target);
|
|
338
|
+
xt_array *src = (xt_array *)XT_GET_PTR(source);
|
|
339
|
+
xt_array_reserve(dst, dst->length + src->length);
|
|
340
|
+
for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
|
|
341
|
+
return target;
|
|
342
|
+
}
|
|
343
|
+
/* Spreading a string/Map/Set iterates exactly like `for...of`. */
|
|
344
|
+
int32_t n = xt_to_int32(xt_iter_length(source));
|
|
345
|
+
for (int32_t i = 0; i < n; i++) {
|
|
346
|
+
xt_array_push(target, xt_iter_value(source, xt_number((double)i)));
|
|
347
|
+
}
|
|
256
348
|
return target;
|
|
257
349
|
}
|
|
258
350
|
|
|
351
|
+
int32_t xt_array_size(xt_value value) {
|
|
352
|
+
if (!XT_IS_ARRAY(value)) return 0;
|
|
353
|
+
return (int32_t)((xt_array *)XT_GET_PTR(value))->length;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
xt_value *xt_array_items(xt_value value) {
|
|
357
|
+
if (!XT_IS_ARRAY(value)) return NULL;
|
|
358
|
+
return ((xt_array *)XT_GET_PTR(value))->items;
|
|
359
|
+
}
|
|
360
|
+
|
|
259
361
|
|
|
260
362
|
/* ------------------------------------------------------------------------- */
|
|
261
363
|
/* Generic member access */
|
|
262
364
|
/* ------------------------------------------------------------------------- */
|
|
263
365
|
|
|
264
366
|
xt_value xt_get(xt_value target, xt_value key) {
|
|
265
|
-
if (XT_IS_ARRAY(target))
|
|
367
|
+
if (XT_IS_ARRAY(target)) {
|
|
368
|
+
/* `arr.length` must read the array's length, never an element. */
|
|
369
|
+
if (XT_IS_STRING(key)) {
|
|
370
|
+
xt_string *k = xt_as_string(key);
|
|
371
|
+
if (k->length == 6 && memcmp(k->data, "length", 6) == 0)
|
|
372
|
+
return xt_number((double)((xt_array *)XT_GET_PTR(target))->length);
|
|
373
|
+
}
|
|
374
|
+
return xt_array_get(target, key);
|
|
375
|
+
}
|
|
266
376
|
if (XT_IS_FUNCTION(target)) return xt_object_get(target, key);
|
|
267
377
|
if (XT_IS_OBJECT(target)) {
|
|
268
378
|
/* Map/Set expose `.size`; promises, dates and regexps have their own
|
|
@@ -277,6 +387,7 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
277
387
|
}
|
|
278
388
|
}
|
|
279
389
|
}
|
|
390
|
+
if (obj->header.kind == XT_OBJECT_KIND_REGEXP) return xt_regexp_get_property(target, key);
|
|
280
391
|
return xt_object_get(target, key);
|
|
281
392
|
}
|
|
282
393
|
if (XT_IS_STRING(target)) {
|
|
@@ -294,7 +405,22 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
294
405
|
}
|
|
295
406
|
|
|
296
407
|
xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
297
|
-
if (XT_IS_ARRAY(target))
|
|
408
|
+
if (XT_IS_ARRAY(target)) {
|
|
409
|
+
/* Assigning `arr.length` truncates or extends the array. */
|
|
410
|
+
if (XT_IS_STRING(key)) {
|
|
411
|
+
xt_string *k = xt_as_string(key);
|
|
412
|
+
if (k->length == 6 && memcmp(k->data, "length", 6) == 0) {
|
|
413
|
+
xt_array *array = (xt_array *)XT_GET_PTR(target);
|
|
414
|
+
int32_t n = xt_to_int32(value);
|
|
415
|
+
if (n < 0) n = 0;
|
|
416
|
+
xt_array_reserve(array, (uint32_t)n);
|
|
417
|
+
for (uint32_t j = array->length; j < (uint32_t)n; j++) array->items[j] = XT_UNDEFINED;
|
|
418
|
+
array->length = (uint32_t)n;
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return xt_array_set(target, key, value);
|
|
423
|
+
}
|
|
298
424
|
if (XT_IS_FUNCTION(target)) return xt_object_set(target, key, value);
|
|
299
425
|
if (XT_IS_OBJECT(target)) return xt_object_set(target, key, value);
|
|
300
426
|
return value;
|
package/runtime/xt_io.c
CHANGED
|
@@ -69,6 +69,59 @@ void xt_set_program_args(int32_t argc, char **argv) {
|
|
|
69
69
|
xt_program_argv = argv;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/*
|
|
73
|
+
* `import.meta` support. The compiler resolves module URLs at build time, but
|
|
74
|
+
* a self-hosted binary only knows where *it* lives, so `import.meta.url` is the
|
|
75
|
+
* executable's own `file://` URL (and `dirname`/`filename` its path parts).
|
|
76
|
+
* This lets the compiler locate an adjacent `runtime/` directory at run time.
|
|
77
|
+
*/
|
|
78
|
+
static xt_value xt_file_url(const char *path) {
|
|
79
|
+
char url[4300];
|
|
80
|
+
size_t j = 0;
|
|
81
|
+
const char *prefix = "file://";
|
|
82
|
+
for (; *prefix; prefix++) url[j++] = *prefix;
|
|
83
|
+
#if defined(_WIN32)
|
|
84
|
+
url[j++] = '/';
|
|
85
|
+
#endif
|
|
86
|
+
for (const char *p = path; *p && j < sizeof(url) - 1; p++) url[j++] = (*p == '\\') ? '/' : *p;
|
|
87
|
+
url[j] = 0;
|
|
88
|
+
return xt_string_from_cstr(url);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
xt_value xt_import_meta(xt_value name) {
|
|
92
|
+
const char *prop = xt_string_data(name);
|
|
93
|
+
const char *raw = (xt_program_argv && xt_program_argv[0]) ? xt_program_argv[0] : "";
|
|
94
|
+
char resolved[4096];
|
|
95
|
+
resolved[0] = 0;
|
|
96
|
+
if (raw[0]) {
|
|
97
|
+
#if defined(_WIN32)
|
|
98
|
+
if (_fullpath(resolved, raw, sizeof(resolved)) == NULL) resolved[0] = 0;
|
|
99
|
+
#else
|
|
100
|
+
if (realpath(raw, resolved) == NULL) resolved[0] = 0;
|
|
101
|
+
#endif
|
|
102
|
+
}
|
|
103
|
+
const char *abs = resolved[0] ? resolved : raw;
|
|
104
|
+
if (prop && strcmp(prop, "dirname") == 0) {
|
|
105
|
+
char dir[4096];
|
|
106
|
+
size_t n = strlen(abs);
|
|
107
|
+
if (n >= sizeof(dir)) n = sizeof(dir) - 1;
|
|
108
|
+
memcpy(dir, abs, n);
|
|
109
|
+
dir[n] = 0;
|
|
110
|
+
char *slash = strrchr(dir, '/');
|
|
111
|
+
#if defined(_WIN32)
|
|
112
|
+
char *back = strrchr(dir, '\\');
|
|
113
|
+
if (back && (!slash || back > slash)) slash = back;
|
|
114
|
+
#endif
|
|
115
|
+
if (slash) {
|
|
116
|
+
if (slash == dir) slash[1] = 0;
|
|
117
|
+
else *slash = 0;
|
|
118
|
+
}
|
|
119
|
+
return xt_string_from_cstr(dir);
|
|
120
|
+
}
|
|
121
|
+
if (prop && strcmp(prop, "filename") == 0) return xt_string_from_cstr(abs);
|
|
122
|
+
return xt_file_url(abs);
|
|
123
|
+
}
|
|
124
|
+
|
|
72
125
|
void *xt_try_enter(void) {
|
|
73
126
|
xt_try_frame *frame = (xt_try_frame *)calloc(1, sizeof(xt_try_frame));
|
|
74
127
|
if (!frame) abort();
|
package/runtime/xt_regex.h
CHANGED
|
@@ -567,9 +567,13 @@ static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
|
|
|
567
567
|
return 0;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
570
|
+
typedef struct {
|
|
571
|
+
size_t end;
|
|
572
|
+
} xt_re_match_result;
|
|
573
|
+
|
|
574
|
+
static int xt_re_record_match(void *ctx, size_t pos) {
|
|
575
|
+
xt_re_match_result *result = (xt_re_match_result *)ctx;
|
|
576
|
+
result->end = pos;
|
|
573
577
|
return 1;
|
|
574
578
|
}
|
|
575
579
|
|
|
@@ -597,15 +601,21 @@ static int regexec(const regex_t *preg, const char *string, size_t nmatch,
|
|
|
597
601
|
regmatch_t pmatch[], int eflags) {
|
|
598
602
|
xt_re_input in;
|
|
599
603
|
size_t start;
|
|
600
|
-
(void)nmatch;
|
|
601
|
-
(void)pmatch;
|
|
602
604
|
(void)eflags;
|
|
603
605
|
if (!preg || !preg->root || !string) return 1;
|
|
604
606
|
in.text = string;
|
|
605
607
|
in.len = strlen(string);
|
|
606
608
|
in.icase = preg->icase;
|
|
607
609
|
for (start = 0; start <= in.len; start++) {
|
|
608
|
-
|
|
610
|
+
xt_re_match_result result;
|
|
611
|
+
result.end = 0;
|
|
612
|
+
if (xt_re_match_alt(preg->root, &in, start, xt_re_record_match, &result)) {
|
|
613
|
+
if (nmatch > 0 && pmatch) {
|
|
614
|
+
pmatch[0].rm_so = (int)start;
|
|
615
|
+
pmatch[0].rm_eo = (int)result.end;
|
|
616
|
+
}
|
|
617
|
+
return 0;
|
|
618
|
+
}
|
|
609
619
|
}
|
|
610
620
|
return 1;
|
|
611
621
|
}
|
package/runtime/xt_stdlib.c
CHANGED
|
@@ -327,6 +327,7 @@ static xt_value xt_string_trim(xt_value value) {
|
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value replacementValue) {
|
|
330
|
+
if (xt_is_regexp(searchValue)) return xt_regexp_replace(value, searchValue, replacementValue);
|
|
330
331
|
xt_string *s = xt_as_string(value);
|
|
331
332
|
if (!s) return value;
|
|
332
333
|
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
@@ -456,13 +457,21 @@ xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *
|
|
|
456
457
|
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
457
458
|
xt_value result = xt_ext_object_method(target, method, argc, argv, &handled);
|
|
458
459
|
if (handled) return result;
|
|
459
|
-
|
|
460
|
+
{
|
|
461
|
+
char message[256];
|
|
462
|
+
snprintf(message, sizeof(message), "TypeError: object has no callable method '%s'", method);
|
|
463
|
+
xt_throw(xt_string_from_cstr(message));
|
|
464
|
+
}
|
|
460
465
|
return XT_UNDEFINED;
|
|
461
466
|
}
|
|
462
467
|
if (XT_IS_FUNCTION(target)) {
|
|
463
468
|
xt_value fn = xt_object_get(target, name);
|
|
464
469
|
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
465
|
-
|
|
470
|
+
{
|
|
471
|
+
char message[256];
|
|
472
|
+
snprintf(message, sizeof(message), "TypeError: function has no callable method '%s'", method);
|
|
473
|
+
xt_throw(xt_string_from_cstr(message));
|
|
474
|
+
}
|
|
466
475
|
return XT_UNDEFINED;
|
|
467
476
|
}
|
|
468
477
|
if (XT_IS_ARRAY(target)) {
|
package/runtime/xt_stdlib2.c
CHANGED
|
@@ -294,6 +294,7 @@ static xt_value xt_string_at(xt_value value, xt_value indexValue) {
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
static xt_value xt_string_replace_all(xt_value value, xt_value searchValue, xt_value replacementValue) {
|
|
297
|
+
if (xt_is_regexp(searchValue)) return xt_regexp_replace(value, searchValue, replacementValue);
|
|
297
298
|
xt_string *s = xt_as_string(value);
|
|
298
299
|
if (!s) return value;
|
|
299
300
|
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
@@ -856,6 +857,35 @@ int32_t xt_set_size(xt_value value) {
|
|
|
856
857
|
return (int32_t)((xt_set_object *)XT_GET_PTR(value))->count;
|
|
857
858
|
}
|
|
858
859
|
|
|
860
|
+
/* -- for...of iteration --------------------------------------------------- */
|
|
861
|
+
|
|
862
|
+
/* `for...of` lowers to a length check plus an index lookup. Arrays and strings
|
|
863
|
+
index directly; Maps yield `[key, value]` pairs and Sets yield their values. */
|
|
864
|
+
xt_value xt_iter_length(xt_value value) {
|
|
865
|
+
if (xt_is_map(value)) return xt_number((double)xt_map_size(value));
|
|
866
|
+
if (xt_is_set(value)) return xt_number((double)xt_set_size(value));
|
|
867
|
+
return xt_array_length(value);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
xt_value xt_iter_value(xt_value value, xt_value index) {
|
|
871
|
+
if (xt_is_map(value)) {
|
|
872
|
+
xt_map *map = (xt_map *)XT_GET_PTR(value);
|
|
873
|
+
int64_t i = (int64_t)xt_to_int32(index);
|
|
874
|
+
if (i < 0 || i >= (int64_t)map->count) return XT_UNDEFINED;
|
|
875
|
+
xt_value pair = xt_array_new(0, NULL);
|
|
876
|
+
xt_array_push(pair, map->keys[i]);
|
|
877
|
+
xt_array_push(pair, map->values[i]);
|
|
878
|
+
return pair;
|
|
879
|
+
}
|
|
880
|
+
if (xt_is_set(value)) {
|
|
881
|
+
xt_set_object *set = (xt_set_object *)XT_GET_PTR(value);
|
|
882
|
+
int64_t i = (int64_t)xt_to_int32(index);
|
|
883
|
+
if (i < 0 || i >= (int64_t)set->count) return XT_UNDEFINED;
|
|
884
|
+
return set->values[i];
|
|
885
|
+
}
|
|
886
|
+
return xt_get(value, index);
|
|
887
|
+
}
|
|
888
|
+
|
|
859
889
|
static int32_t xt_set_index(xt_set_object *set, xt_value key) {
|
|
860
890
|
for (uint32_t i = 0; i < set->count; i++) if (xt_value_equals(set->values[i], key)) return (int32_t)i;
|
|
861
891
|
return -1;
|
|
@@ -1016,18 +1046,232 @@ xt_value xt_regexp_ctor(int32_t argc, xt_value *argv) {
|
|
|
1016
1046
|
return XT_FROM_PTR(XT_TAG_OBJECT, regexp);
|
|
1017
1047
|
}
|
|
1018
1048
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1049
|
+
xt_value xt_error_ctor(int32_t argc, xt_value *argv) {
|
|
1050
|
+
xt_value error = xt_object_new();
|
|
1051
|
+
xt_value name = xt_arg_at(argc, argv, 1);
|
|
1052
|
+
xt_value message = xt_arg_at(argc, argv, 0);
|
|
1053
|
+
xt_object_set(error, xt_string_from_cstr("name"),
|
|
1054
|
+
name == XT_UNDEFINED ? xt_string_from_cstr("Error") : name);
|
|
1055
|
+
xt_object_set(error, xt_string_from_cstr("message"),
|
|
1056
|
+
message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
|
|
1057
|
+
return error;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/* `super(message)` inside a subclass of the builtin `Error`: initialize the
|
|
1061
|
+
* already-allocated instance instead of creating a fresh object. */
|
|
1062
|
+
xt_value xt_error_init(xt_value thisValue, int32_t argc, xt_value *argv) {
|
|
1063
|
+
xt_value name = xt_arg_at(argc, argv, 1);
|
|
1064
|
+
xt_value message = xt_arg_at(argc, argv, 0);
|
|
1065
|
+
xt_object_set(thisValue, xt_string_from_cstr("name"),
|
|
1066
|
+
name == XT_UNDEFINED ? xt_string_from_cstr("Error") : name);
|
|
1067
|
+
xt_object_set(thisValue, xt_string_from_cstr("message"),
|
|
1068
|
+
message == XT_UNDEFINED ? xt_string_from_cstr("") : message);
|
|
1069
|
+
return thisValue;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* Compile `regexp` and search `input` starting at byte offset `start`.
|
|
1073
|
+
* Returns 1 and fills `matchStart`/`matchEnd` (absolute offsets into `input`)
|
|
1074
|
+
* when a match is found, otherwise 0. */
|
|
1075
|
+
|
|
1076
|
+
static int xt_regex_hex_digit(char c) {
|
|
1077
|
+
if (c >= '0' && c <= '9') return c - '0';
|
|
1078
|
+
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
1079
|
+
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
1080
|
+
return -1;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/* JavaScript regexes use escapes POSIX ERE does not understand (`\r`, `\n`,
|
|
1084
|
+
* `\d`, `\w`, `\s`, `(?:...)`, ...). Rewrite the common ones into their POSIX
|
|
1085
|
+
* equivalents before handing the pattern to the engine. Returns a malloc'd,
|
|
1086
|
+
* NUL-terminated string the caller must free. */
|
|
1087
|
+
static char *xt_regexp_translate_js(const char *pattern) {
|
|
1088
|
+
size_t length = strlen(pattern);
|
|
1089
|
+
size_t capacity = length * 16 + 16;
|
|
1090
|
+
char *out = (char *)malloc(capacity);
|
|
1091
|
+
if (!out) abort();
|
|
1092
|
+
size_t written = 0;
|
|
1093
|
+
for (size_t i = 0; i < length; i++) {
|
|
1094
|
+
char c = pattern[i];
|
|
1095
|
+
const char *replacement = NULL;
|
|
1096
|
+
char literal = 0;
|
|
1097
|
+
int consume = 0;
|
|
1098
|
+
if (c == '\\') {
|
|
1099
|
+
char next = (i + 1 < length) ? pattern[i + 1] : '\0';
|
|
1100
|
+
switch (next) {
|
|
1101
|
+
case 'r': literal = '\r'; consume = 1; break;
|
|
1102
|
+
case 'n': literal = '\n'; consume = 1; break;
|
|
1103
|
+
case 't': literal = '\t'; consume = 1; break;
|
|
1104
|
+
case 'f': literal = '\f'; consume = 1; break;
|
|
1105
|
+
case 'v': literal = '\v'; consume = 1; break;
|
|
1106
|
+
case 'd': replacement = "[0-9]"; consume = 1; break;
|
|
1107
|
+
case 'D': replacement = "[^0-9]"; consume = 1; break;
|
|
1108
|
+
case 'w': replacement = "[A-Za-z0-9_]"; consume = 1; break;
|
|
1109
|
+
case 'W': replacement = "[^A-Za-z0-9_]"; consume = 1; break;
|
|
1110
|
+
case 's': replacement = "[ \t\n\r\f\v]"; consume = 1; break;
|
|
1111
|
+
case 'S': replacement = "[^ \t\n\r\f\v]"; consume = 1; break;
|
|
1112
|
+
case 'x': {
|
|
1113
|
+
int hi = (i + 2 < length) ? xt_regex_hex_digit(pattern[i + 2]) : -1;
|
|
1114
|
+
int lo = (i + 3 < length) ? xt_regex_hex_digit(pattern[i + 3]) : -1;
|
|
1115
|
+
if (hi >= 0 && lo >= 0) { literal = (char)(hi * 16 + lo); consume = 3; }
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
1118
|
+
default: break;
|
|
1119
|
+
}
|
|
1120
|
+
} else if (c == '(' && i + 2 < length && pattern[i + 1] == '?' && pattern[i + 2] == ':') {
|
|
1121
|
+
/* POSIX ERE has no non-capturing groups; a capturing group matches the
|
|
1122
|
+
* same text, only the capture bookkeeping differs. */
|
|
1123
|
+
literal = '(';
|
|
1124
|
+
consume = 2;
|
|
1125
|
+
}
|
|
1126
|
+
if (replacement) {
|
|
1127
|
+
size_t addition = strlen(replacement);
|
|
1128
|
+
if (written + addition + 1 > capacity) { capacity = (written + addition) * 2 + 16; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
1129
|
+
memcpy(out + written, replacement, addition);
|
|
1130
|
+
written += addition;
|
|
1131
|
+
} else if (consume || literal) {
|
|
1132
|
+
if (written + 2 > capacity) { capacity = capacity * 2 + 16; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
1133
|
+
out[written++] = literal;
|
|
1134
|
+
} else {
|
|
1135
|
+
if (written + 2 > capacity) { capacity = capacity * 2 + 16; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
1136
|
+
out[written++] = c;
|
|
1137
|
+
}
|
|
1138
|
+
i += (size_t)consume;
|
|
1139
|
+
}
|
|
1140
|
+
out[written] = '\0';
|
|
1141
|
+
return out;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
int xt_regexp_find(xt_value regexpValue, xt_value input, int32_t start, int32_t *matchStart, int32_t *matchEnd) {
|
|
1145
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
1021
1146
|
xt_string *pattern = xt_as_string(regexp->source);
|
|
1022
1147
|
xt_string *text = xt_as_string(xt_to_string(input));
|
|
1148
|
+
if (!pattern || !text) return 0;
|
|
1149
|
+
if (start < 0) start = 0;
|
|
1150
|
+
if ((uint32_t)start > text->length) return 0;
|
|
1023
1151
|
int flags = REG_EXTENDED;
|
|
1024
1152
|
xt_string *f = xt_as_string(regexp->flags);
|
|
1025
1153
|
if (f) for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == 'i') flags |= REG_ICASE;
|
|
1154
|
+
char *translated = xt_regexp_translate_js(pattern->data);
|
|
1155
|
+
if (!translated) return 0;
|
|
1026
1156
|
regex_t compiled;
|
|
1027
|
-
if (
|
|
1028
|
-
|
|
1157
|
+
if (regcomp(&compiled, translated, flags) != 0) {
|
|
1158
|
+
free(translated);
|
|
1159
|
+
return 0;
|
|
1160
|
+
}
|
|
1161
|
+
free(translated);
|
|
1162
|
+
regmatch_t match;
|
|
1163
|
+
match.rm_so = 0;
|
|
1164
|
+
match.rm_eo = 0;
|
|
1165
|
+
int ok = regexec(&compiled, text->data + start, 1, &match, 0) == 0;
|
|
1029
1166
|
regfree(&compiled);
|
|
1030
|
-
|
|
1167
|
+
if (!ok) return 0;
|
|
1168
|
+
if (matchStart) *matchStart = start + (int32_t)match.rm_so;
|
|
1169
|
+
if (matchEnd) *matchEnd = start + (int32_t)match.rm_eo;
|
|
1170
|
+
return 1;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
static int xt_regexp_is_global(xt_value regexpValue) {
|
|
1174
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
1175
|
+
xt_string *f = xt_as_string(regexp->flags);
|
|
1176
|
+
if (!f) return 0;
|
|
1177
|
+
for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == 'g') return 1;
|
|
1178
|
+
return 0;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
static xt_value xt_regexp_test(xt_value target, xt_value input) {
|
|
1182
|
+
return xt_bool(xt_regexp_find(target, input, 0, NULL, NULL));
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/* JavaScript-style match object: `[0]` is the matched text, `index` is the
|
|
1186
|
+
* match offset and `input` is the searched string. */
|
|
1187
|
+
xt_value xt_regexp_exec(xt_value target, xt_value input) {
|
|
1188
|
+
int32_t start = 0;
|
|
1189
|
+
int32_t end = 0;
|
|
1190
|
+
if (!xt_regexp_find(target, input, 0, &start, &end)) return XT_NULL;
|
|
1191
|
+
xt_value textValue = xt_to_string(input);
|
|
1192
|
+
xt_string *text = xt_as_string(textValue);
|
|
1193
|
+
xt_value out = xt_object_new();
|
|
1194
|
+
xt_object_set(out, xt_string_from_cstr("0"), xt_string_new(text->data + start, (int64_t)(end - start)));
|
|
1195
|
+
xt_object_set(out, xt_string_from_cstr("index"), xt_number((double)start));
|
|
1196
|
+
xt_object_set(out, xt_string_from_cstr("input"), textValue);
|
|
1197
|
+
xt_object_set(out, xt_string_from_cstr("length"), xt_number(1));
|
|
1198
|
+
return out;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/* `String.prototype.replace`/`replaceAll` with a RegExp search value. */
|
|
1202
|
+
xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replacementValue) {
|
|
1203
|
+
xt_string *text = xt_as_string(xt_to_string(value));
|
|
1204
|
+
if (!text) return value;
|
|
1205
|
+
xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
|
|
1206
|
+
if (!replacement) return value;
|
|
1207
|
+
int global = xt_regexp_is_global(regexpValue);
|
|
1208
|
+
size_t capacity = text->length + replacement->length + 16;
|
|
1209
|
+
char *out = (char *)malloc(capacity);
|
|
1210
|
+
if (!out) abort();
|
|
1211
|
+
size_t length = 0;
|
|
1212
|
+
int32_t cursor = 0;
|
|
1213
|
+
int replaced = 0;
|
|
1214
|
+
for (;;) {
|
|
1215
|
+
int32_t start = 0;
|
|
1216
|
+
int32_t end = 0;
|
|
1217
|
+
if (!xt_regexp_find(regexpValue, value, cursor, &start, &end)) break;
|
|
1218
|
+
size_t prefix = (size_t)(start - cursor);
|
|
1219
|
+
size_t needed = length + prefix + replacement->length + 2;
|
|
1220
|
+
if (needed > capacity) {
|
|
1221
|
+
capacity = needed * 2;
|
|
1222
|
+
out = (char *)realloc(out, capacity);
|
|
1223
|
+
if (!out) abort();
|
|
1224
|
+
}
|
|
1225
|
+
memcpy(out + length, text->data + cursor, prefix);
|
|
1226
|
+
length += prefix;
|
|
1227
|
+
memcpy(out + length, replacement->data, replacement->length);
|
|
1228
|
+
length += replacement->length;
|
|
1229
|
+
replaced = 1;
|
|
1230
|
+
if (end > start) {
|
|
1231
|
+
cursor = end;
|
|
1232
|
+
} else {
|
|
1233
|
+
/* Zero-width match: copy one byte so the scan always progresses. */
|
|
1234
|
+
if (cursor >= (int32_t)text->length) break;
|
|
1235
|
+
out[length++] = text->data[cursor++];
|
|
1236
|
+
}
|
|
1237
|
+
if (!global) break;
|
|
1238
|
+
}
|
|
1239
|
+
if (!replaced) {
|
|
1240
|
+
free(out);
|
|
1241
|
+
return value;
|
|
1242
|
+
}
|
|
1243
|
+
size_t tail = text->length - (size_t)cursor;
|
|
1244
|
+
if (length + tail + 1 > capacity) {
|
|
1245
|
+
capacity = length + tail + 1;
|
|
1246
|
+
out = (char *)realloc(out, capacity);
|
|
1247
|
+
if (!out) abort();
|
|
1248
|
+
}
|
|
1249
|
+
memcpy(out + length, text->data + cursor, tail);
|
|
1250
|
+
length += tail;
|
|
1251
|
+
xt_value result = xt_string_new(out, (int64_t)length);
|
|
1252
|
+
free(out);
|
|
1253
|
+
return result;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/* Property access (`re.source`, `re.flags`, `re.global`, ...). */
|
|
1257
|
+
xt_value xt_regexp_get_property(xt_value target, xt_value key) {
|
|
1258
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(target);
|
|
1259
|
+
if (!XT_IS_STRING(key)) return XT_UNDEFINED;
|
|
1260
|
+
xt_string *k = xt_as_string(key);
|
|
1261
|
+
xt_string *f = xt_as_string(regexp->flags);
|
|
1262
|
+
int flag = 0;
|
|
1263
|
+
if (k->length == 6 && memcmp(k->data, "source", 6) == 0) return regexp->source;
|
|
1264
|
+
if (k->length == 5 && memcmp(k->data, "flags", 5) == 0) return regexp->flags;
|
|
1265
|
+
if (k->length == 9 && memcmp(k->data, "lastIndex", 9) == 0) return xt_number(0);
|
|
1266
|
+
if (k->length == 6 && memcmp(k->data, "global", 6) == 0) flag = 'g';
|
|
1267
|
+
else if (k->length == 10 && memcmp(k->data, "ignoreCase", 10) == 0) flag = 'i';
|
|
1268
|
+
else if (k->length == 9 && memcmp(k->data, "multiline", 9) == 0) flag = 'm';
|
|
1269
|
+
else if (k->length == 6 && memcmp(k->data, "sticky", 6) == 0) flag = 'y';
|
|
1270
|
+
if (flag) {
|
|
1271
|
+
if (f) for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == flag) return XT_TRUE;
|
|
1272
|
+
return XT_FALSE;
|
|
1273
|
+
}
|
|
1274
|
+
return XT_UNDEFINED;
|
|
1031
1275
|
}
|
|
1032
1276
|
|
|
1033
1277
|
xt_value xt_ext_container_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
|
|
@@ -1039,14 +1283,7 @@ xt_value xt_ext_container_method(xt_value target, const char *method, int32_t ar
|
|
|
1039
1283
|
xt_regexp *regexp = (xt_regexp *)obj;
|
|
1040
1284
|
*handled = 1;
|
|
1041
1285
|
if (strcmp(method, "test") == 0) return xt_regexp_test(target, xt_arg_at(argc, argv, 0));
|
|
1042
|
-
if (strcmp(method, "exec") == 0)
|
|
1043
|
-
if (xt_truthy(xt_regexp_test(target, xt_arg_at(argc, argv, 0)))) {
|
|
1044
|
-
xt_value out = xt_array_new(0, NULL);
|
|
1045
|
-
xt_array_push(out, xt_arg_at(argc, argv, 0));
|
|
1046
|
-
return out;
|
|
1047
|
-
}
|
|
1048
|
-
return XT_NULL;
|
|
1049
|
-
}
|
|
1286
|
+
if (strcmp(method, "exec") == 0) return xt_regexp_exec(target, xt_arg_at(argc, argv, 0));
|
|
1050
1287
|
if (strcmp(method, "toString") == 0) {
|
|
1051
1288
|
xt_value out = xt_string_from_cstr("/");
|
|
1052
1289
|
out = xt_add(out, regexp->source);
|
package/runtime/xt_values.c
CHANGED
|
@@ -117,7 +117,8 @@ int xt_truthy(xt_value v) {
|
|
|
117
117
|
if (v == XT_UNDEFINED || v == XT_NULL) return 0;
|
|
118
118
|
if (v == XT_FALSE) return 0;
|
|
119
119
|
if (v == XT_TRUE) return 1;
|
|
120
|
-
|
|
120
|
+
if (XT_IS_STRING(v)) return xt_as_string(v)->length != 0;
|
|
121
|
+
return 1; /* objects, arrays and functions are truthy */
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
double xt_to_number(xt_value v) {
|