xbintsc 0.3.81 → 0.5.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/AGENTS.md +8 -4
- package/README.md +16 -5
- package/README.zh-CN.md +14 -6
- package/dist/src/ast/common.d.ts +23 -0
- package/dist/src/cli/main.js +68 -24
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +6 -2
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.js +4 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +9 -4
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +50 -0
- package/dist/src/codegen/generator/context.js +124 -13
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/debug.d.ts +33 -0
- package/dist/src/codegen/generator/debug.js +97 -0
- package/dist/src/codegen/generator/debug.js.map +1 -0
- package/dist/src/codegen/generator/expressions/assignment.js +9 -5
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/operators.js +35 -9
- package/dist/src/codegen/generator/expressions/operators.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.js +11 -3
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.d.ts +3 -1
- package/dist/src/codegen/generator/generator.js +3 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -1
- package/dist/src/codegen/generator/module.js +82 -12
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/numeric.d.ts +26 -0
- package/dist/src/codegen/generator/numeric.js +127 -0
- package/dist/src/codegen/generator/numeric.js.map +1 -0
- package/dist/src/codegen/generator/state.d.ts +17 -0
- package/dist/src/codegen/generator/state.js +9 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.js +2 -4
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +3 -0
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +8 -2
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +16 -0
- package/dist/src/codegen/generator/tables.js +99 -2
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +41 -5
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +23 -4
- package/dist/src/driver/compiler.js +46 -57
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/fingerprint.d.ts +27 -0
- package/dist/src/driver/fingerprint.js +88 -0
- package/dist/src/driver/fingerprint.js.map +1 -0
- package/dist/src/driver/toolchain-provider.d.ts +9 -2
- package/dist/src/driver/toolchain-provider.js +15 -15
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +58 -1
- package/dist/src/driver/toolchain.js +169 -22
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/parser/expressions.js +20 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/cli/main.test.js +41 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.js +8 -0
- package/dist/tests/codegen/closures.test.js.map +1 -1
- package/dist/tests/codegen/expressions.test.js +14 -3
- package/dist/tests/codegen/expressions.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +23 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +49 -2
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +62 -3
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/async-await.test.d.ts +10 -0
- package/dist/tests/e2e/async-await.test.js +247 -0
- package/dist/tests/e2e/async-await.test.js.map +1 -0
- package/dist/tests/e2e/child-process.test.d.ts +10 -0
- package/dist/tests/e2e/child-process.test.js +59 -0
- package/dist/tests/e2e/child-process.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +47 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/error-stack.test.d.ts +9 -0
- package/dist/tests/e2e/error-stack.test.js +94 -0
- package/dist/tests/e2e/error-stack.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +46 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -1
- package/dist/tests/e2e/string-concat.test.d.ts +9 -0
- package/dist/tests/e2e/string-concat.test.js +102 -0
- package/dist/tests/e2e/string-concat.test.js.map +1 -0
- package/dist/tests/e2e/string-utf16.test.d.ts +6 -0
- package/dist/tests/e2e/string-utf16.test.js +133 -0
- package/dist/tests/e2e/string-utf16.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +9 -0
- package/dist/tests/helpers.js +26 -3
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +7 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/version.test.d.ts +1 -0
- package/dist/tests/version.test.js +24 -0
- package/dist/tests/version.test.js.map +1 -0
- package/doc/DESIGN.md +20 -12
- package/doc/ai/cli.md +2 -0
- package/doc/ai/contributing.md +16 -8
- package/doc/ai/extensions.md +3 -2
- package/doc/ai/language-support.md +15 -12
- package/doc/ai/troubleshooting.md +43 -5
- package/doc/ai/zh-CN/cli.md +2 -0
- package/doc/ai/zh-CN/contributing.md +11 -7
- package/doc/ai/zh-CN/extensions.md +2 -1
- package/doc/ai/zh-CN/language-support.md +15 -12
- package/doc/ai/zh-CN/troubleshooting.md +38 -4
- package/doc/implemented.md +16 -13
- package/doc/requirements.md +13 -4
- package/doc/unimplemented.md +20 -18
- package/doc/zh-CN/DESIGN.md +15 -9
- package/doc/zh-CN/implemented.md +15 -12
- package/doc/zh-CN/requirements.md +12 -3
- package/doc/zh-CN/unimplemented.md +19 -17
- package/package.json +3 -1
- package/runtime/ext_node/buffer/parts/construction.inc +2 -2
- package/runtime/ext_node/buffer/parts/methods.inc +1 -1
- package/runtime/ext_node/buffer/parts/search.inc +1 -1
- package/runtime/ext_node/child_process/child_process.c +25 -0
- package/runtime/ext_node/fs/fd_ops.c +1 -1
- package/runtime/rt.h +49 -1
- package/runtime/rt_internal.h +136 -4
- package/runtime/xt_alloc.c +20 -2
- package/runtime/xt_async.c +197 -0
- package/runtime/xt_bigint/conversions.inc +1 -1
- package/runtime/xt_builtins.c +169 -43
- package/runtime/xt_constructors.c +389 -0
- package/runtime/xt_containers/access.inc +61 -24
- package/runtime/xt_containers/arrays.inc +26 -2
- package/runtime/xt_containers/objects.inc +206 -8
- package/runtime/xt_coroutine.c +212 -0
- package/runtime/xt_generator.c +31 -162
- package/runtime/xt_io/exception.inc +171 -2
- package/runtime/xt_io/inspect.inc +48 -13
- package/runtime/xt_io.c +5 -0
- package/runtime/xt_promise.c +62 -17
- package/runtime/xt_stdlib/arrays.inc +2 -2
- package/runtime/xt_stdlib/dispatch.inc +20 -4
- package/runtime/xt_stdlib/strings.inc +98 -29
- package/runtime/xt_stdlib2/array-statics.inc +3 -2
- package/runtime/xt_stdlib2/arrays.inc +1 -1
- package/runtime/xt_stdlib2/error.inc +33 -6
- package/runtime/xt_stdlib2/json.inc +46 -16
- package/runtime/xt_stdlib2/number-string-statics.inc +24 -13
- package/runtime/xt_stdlib2/objects.inc +1 -1
- package/runtime/xt_stdlib2/regexp-match.inc +50 -35
- package/runtime/xt_stdlib2/set.inc +6 -0
- package/runtime/xt_stdlib2/strings.inc +81 -42
- package/runtime/xt_symbol.c +1 -1
- package/runtime/xt_typed_array/construction.inc +43 -2
- package/runtime/xt_typed_array.c +4 -0
- package/runtime/xt_values/comparison.inc +2 -2
- package/runtime/xt_values/constructors.inc +18 -5
- package/runtime/xt_values/conversions.inc +4 -3
- package/runtime/xt_values/strings.inc +94 -4
- package/runtime/xt_values/utf16.inc +340 -0
- package/runtime/xt_values.c +1 -0
- package/scripts/bench.ts +172 -0
- package/scripts/build-runtime.ts +2 -1
- package/scripts/check-version.ts +53 -0
- package/src/ast/common.ts +23 -0
- package/src/cli/main.ts +68 -26
- package/src/codegen/generator/calls/access.ts +7 -2
- package/src/codegen/generator/calls/closures.ts +3 -0
- package/src/codegen/generator/calls/invocation.ts +9 -3
- package/src/codegen/generator/context.ts +129 -18
- package/src/codegen/generator/debug.ts +132 -0
- package/src/codegen/generator/expressions/assignment.ts +9 -5
- package/src/codegen/generator/expressions/operators.ts +47 -8
- package/src/codegen/generator/expressions/primary.ts +11 -3
- package/src/codegen/generator/generator.ts +18 -2
- package/src/codegen/generator/module.ts +83 -13
- package/src/codegen/generator/numeric.ts +158 -0
- package/src/codegen/generator/state.ts +26 -0
- package/src/codegen/generator/statements/control-flow.ts +2 -3
- package/src/codegen/generator/statements/dispatch.ts +3 -0
- package/src/codegen/generator/statements/variables.ts +8 -2
- package/src/codegen/generator/tables.ts +110 -2
- package/src/driver/bundler/merge.ts +39 -5
- package/src/driver/compiler.ts +65 -55
- package/src/driver/fingerprint.ts +85 -0
- package/src/driver/toolchain-provider.ts +17 -16
- package/src/driver/toolchain.ts +210 -21
- package/src/parser/expressions.ts +20 -1
|
@@ -36,14 +36,31 @@ static xt_value xt_json_parse_string(xt_json_cursor *c) {
|
|
|
36
36
|
else if (h >= 'a' && h <= 'f') code |= (unsigned)(h - 'a' + 10);
|
|
37
37
|
else if (h >= 'A' && h <= 'F') code |= (unsigned)(h - 'A' + 10);
|
|
38
38
|
}
|
|
39
|
+
/* A high surrogate followed by `\u` and a low surrogate denotes one
|
|
40
|
+
* supplementary code point, exactly like a JavaScript string. */
|
|
41
|
+
if (code >= 0xD800 && code <= 0xDBFF && c->pos + 5 < c->length &&
|
|
42
|
+
c->data[c->pos] == '\\' && c->data[c->pos + 1] == 'u') {
|
|
43
|
+
unsigned low = 0;
|
|
44
|
+
int valid = 1;
|
|
45
|
+
for (int i = 0; i < 4; i++) {
|
|
46
|
+
char h = c->data[c->pos + 2 + (size_t)i];
|
|
47
|
+
low <<= 4;
|
|
48
|
+
if (h >= '0' && h <= '9') low |= (unsigned)(h - '0');
|
|
49
|
+
else if (h >= 'a' && h <= 'f') low |= (unsigned)(h - 'a' + 10);
|
|
50
|
+
else if (h >= 'A' && h <= 'F') low |= (unsigned)(h - 'A' + 10);
|
|
51
|
+
else valid = 0;
|
|
52
|
+
}
|
|
53
|
+
if (valid && low >= 0xDC00 && low <= 0xDFFF) {
|
|
54
|
+
code = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00);
|
|
55
|
+
c->pos += 6;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
39
58
|
if (code >= 0x80) {
|
|
40
59
|
char tmp[4];
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
memcpy(out + length, tmp, (size_t)bytes);
|
|
46
|
-
length += (size_t)bytes;
|
|
60
|
+
size_t bytes = xt_utf8_encode(code, tmp);
|
|
61
|
+
if (length + bytes + 1 > capacity) { capacity = (length + bytes + 1) * 2; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
62
|
+
memcpy(out + length, tmp, bytes);
|
|
63
|
+
length += bytes;
|
|
47
64
|
continue;
|
|
48
65
|
}
|
|
49
66
|
ch = (char)code;
|
|
@@ -120,7 +137,7 @@ xt_value xt_json_parse(int32_t argc, xt_value *argv) {
|
|
|
120
137
|
xt_value input = xt_to_string(xt_arg_at(argc, argv, 0));
|
|
121
138
|
xt_string *s = xt_as_string(input);
|
|
122
139
|
if (!s) return XT_UNDEFINED;
|
|
123
|
-
xt_json_cursor cursor = {s
|
|
140
|
+
xt_json_cursor cursor = {xt_string_bytes(s), s->length, 0};
|
|
124
141
|
return xt_json_parse_value(&cursor);
|
|
125
142
|
}
|
|
126
143
|
|
|
@@ -193,15 +210,28 @@ static void xt_json_stringify_value(xt_value value, int indent, int depth, xt_va
|
|
|
193
210
|
}
|
|
194
211
|
if (XT_IS_STRING(value)) {
|
|
195
212
|
xt_string *s = xt_as_string(value);
|
|
213
|
+
const char *bytes = xt_string_bytes(s);
|
|
196
214
|
*out = xt_add(*out, xt_string_from_cstr("\""));
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
else
|
|
215
|
+
size_t offset = 0;
|
|
216
|
+
while (offset < s->length) {
|
|
217
|
+
size_t width = 0;
|
|
218
|
+
uint32_t code = xt_utf8_decode(bytes + offset, s->length - offset, &width);
|
|
219
|
+
const char *escape = NULL;
|
|
220
|
+
char buffer[8];
|
|
221
|
+
if (code == '"') escape = "\\\"";
|
|
222
|
+
else if (code == '\\') escape = "\\\\";
|
|
223
|
+
else if (code == '\b') escape = "\\b";
|
|
224
|
+
else if (code == '\f') escape = "\\f";
|
|
225
|
+
else if (code == '\n') escape = "\\n";
|
|
226
|
+
else if (code == '\r') escape = "\\r";
|
|
227
|
+
else if (code == '\t') escape = "\\t";
|
|
228
|
+
else if (code < 0x20 || (code >= 0xD800 && code <= 0xDFFF)) {
|
|
229
|
+
snprintf(buffer, sizeof(buffer), "\\u%04x", (unsigned)code);
|
|
230
|
+
escape = buffer;
|
|
231
|
+
}
|
|
232
|
+
if (escape) *out = xt_add(*out, xt_string_from_cstr(escape));
|
|
233
|
+
else *out = xt_add(*out, xt_string_new(bytes + offset, width));
|
|
234
|
+
offset += width;
|
|
205
235
|
}
|
|
206
236
|
*out = xt_add(*out, xt_string_from_cstr("\""));
|
|
207
237
|
return;
|
|
@@ -215,7 +245,7 @@ xt_value xt_json_stringify(int32_t argc, xt_value *argv) {
|
|
|
215
245
|
int indent = 0;
|
|
216
246
|
xt_value space = xt_arg_at(argc, argv, 1);
|
|
217
247
|
if (XT_IS_NUMBER(space)) indent = xt_to_int32(space);
|
|
218
|
-
else if (XT_IS_STRING(space)) indent = (int)((xt_string *)XT_GET_PTR(space))
|
|
248
|
+
else if (XT_IS_STRING(space)) indent = (int)xt_string_utf16_length((xt_string *)XT_GET_PTR(space));
|
|
219
249
|
if (indent < 0) indent = 0;
|
|
220
250
|
if (indent > 10) indent = 10;
|
|
221
251
|
xt_value value = xt_arg_at(argc, argv, 0);
|
|
@@ -41,22 +41,33 @@ xt_value xt_number_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
41
41
|
xt_value xt_string_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
42
42
|
const char *fn = xt_string_data(name);
|
|
43
43
|
if (!fn) return XT_UNDEFINED;
|
|
44
|
-
if (strcmp(fn, "fromCharCode") == 0
|
|
45
|
-
|
|
44
|
+
if (strcmp(fn, "fromCharCode") == 0) {
|
|
45
|
+
/* `fromCharCode` takes UTF-16 code units (modulo 65536) and joins adjacent
|
|
46
|
+
* high/low surrogates into one code point, like JavaScript. */
|
|
47
|
+
if (argc <= 0) return xt_string_from_cstr("");
|
|
48
|
+
uint16_t *units = (uint16_t *)malloc(sizeof(uint16_t) * (size_t)argc);
|
|
49
|
+
if (!units) abort();
|
|
50
|
+
for (int32_t i = 0; i < argc; i++) units[i] = (uint16_t)(xt_to_int32(argv[i]) & 0xFFFF);
|
|
51
|
+
xt_value result = xt_string_from_units(units, (size_t)argc);
|
|
52
|
+
free(units);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
if (strcmp(fn, "fromCodePoint") == 0) {
|
|
56
|
+
char *out = (char *)malloc((size_t)argc * 4 + 1);
|
|
57
|
+
if (!out) abort();
|
|
46
58
|
size_t length = 0;
|
|
47
59
|
for (int32_t i = 0; i < argc; i++) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
memcpy(out->data + length, buffer, (size_t)bytes);
|
|
56
|
-
length += (size_t)bytes;
|
|
60
|
+
int32_t code = xt_to_int32(argv[i]);
|
|
61
|
+
if (code < 0 || code > 0x10FFFF) {
|
|
62
|
+
free(out);
|
|
63
|
+
xt_throw(xt_string_from_cstr("RangeError: Invalid code point"));
|
|
64
|
+
return XT_UNDEFINED;
|
|
65
|
+
}
|
|
66
|
+
length += xt_utf8_encode((uint32_t)code, out + length);
|
|
57
67
|
}
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
xt_value result = xt_string_new(out, length);
|
|
69
|
+
free(out);
|
|
70
|
+
return result;
|
|
60
71
|
}
|
|
61
72
|
if (strcmp(fn, "raw") == 0) {
|
|
62
73
|
xt_value strings = xt_arg_at(argc, argv, 0);
|
|
@@ -64,7 +64,7 @@ xt_value xt_object_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
64
64
|
}
|
|
65
65
|
if (strcmp(fn, "create") == 0) {
|
|
66
66
|
xt_value proto = xt_arg_at(argc, argv, 0);
|
|
67
|
-
return xt_object_new_with_proto(proto == XT_NULL ?
|
|
67
|
+
return xt_object_new_with_proto(proto == XT_NULL ? XT_NULL : proto);
|
|
68
68
|
}
|
|
69
69
|
return XT_UNDEFINED;
|
|
70
70
|
}
|
|
@@ -109,8 +109,11 @@ static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
|
|
|
109
109
|
if ((uint32_t)start > text->length) return 0;
|
|
110
110
|
int flags = REG_EXTENDED;
|
|
111
111
|
xt_string *f = xt_as_string(regexp->flags);
|
|
112
|
-
if (f)
|
|
113
|
-
|
|
112
|
+
if (f) {
|
|
113
|
+
const char *flagBytes = xt_string_bytes(f);
|
|
114
|
+
for (uint32_t i = 0; i < f->length; i++) if (flagBytes[i] == 'i') flags |= REG_ICASE;
|
|
115
|
+
}
|
|
116
|
+
char *translated = xt_regexp_translate_js(xt_string_bytes(pattern), jsGroups, groupCount);
|
|
114
117
|
if (!translated) return 0;
|
|
115
118
|
regex_t compiled;
|
|
116
119
|
if (regcomp(&compiled, translated, flags) != 0) {
|
|
@@ -118,7 +121,7 @@ static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
|
|
|
118
121
|
return 0;
|
|
119
122
|
}
|
|
120
123
|
free(translated);
|
|
121
|
-
int ok = regexec(&compiled, text
|
|
124
|
+
int ok = regexec(&compiled, xt_string_bytes(text) + start, nmatch, matches, 0) == 0;
|
|
122
125
|
regfree(&compiled);
|
|
123
126
|
if (!ok) return 0;
|
|
124
127
|
for (size_t i = 0; i < nmatch; i++) {
|
|
@@ -142,7 +145,8 @@ static int xt_regexp_is_global(xt_value regexpValue) {
|
|
|
142
145
|
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
143
146
|
xt_string *f = xt_as_string(regexp->flags);
|
|
144
147
|
if (!f) return 0;
|
|
145
|
-
|
|
148
|
+
const char *flags = xt_string_bytes(f);
|
|
149
|
+
for (uint32_t i = 0; i < f->length; i++) if (flags[i] == 'g') return 1;
|
|
146
150
|
return 0;
|
|
147
151
|
}
|
|
148
152
|
|
|
@@ -154,7 +158,7 @@ static int xt_regexp_js_group_count(xt_value regexpValue) {
|
|
|
154
158
|
int count = (int)pattern->length + 1;
|
|
155
159
|
int *groups = (int *)malloc(sizeof(int) * (size_t)count);
|
|
156
160
|
int groupCount = 0;
|
|
157
|
-
char *translated = xt_regexp_translate_js(pattern
|
|
161
|
+
char *translated = xt_regexp_translate_js(xt_string_bytes(pattern), groups, &groupCount);
|
|
158
162
|
free(translated);
|
|
159
163
|
int total = 0;
|
|
160
164
|
for (int i = 0; i < groupCount; i++) if (groups[i] > total) total = groups[i];
|
|
@@ -177,6 +181,7 @@ xt_value xt_regexp_exec(xt_value target, xt_value input) {
|
|
|
177
181
|
if (!matches || !groups) { free(matches); free(groups); return XT_NULL; }
|
|
178
182
|
xt_value textValue = xt_to_string(input);
|
|
179
183
|
xt_string *text = xt_as_string(textValue);
|
|
184
|
+
const char *textBytes = xt_string_bytes(text);
|
|
180
185
|
int groupCount = 0;
|
|
181
186
|
if (!xt_regexp_run(target, input, 0, matches, cap, groups, &groupCount)) {
|
|
182
187
|
free(matches);
|
|
@@ -186,19 +191,19 @@ xt_value xt_regexp_exec(xt_value target, xt_value input) {
|
|
|
186
191
|
int jsTotal = 0;
|
|
187
192
|
for (int i = 0; i < groupCount; i++) if (groups[i] > jsTotal) jsTotal = groups[i];
|
|
188
193
|
xt_value out = xt_array_new(0, NULL);
|
|
189
|
-
xt_array_push(out, xt_string_new(
|
|
194
|
+
xt_array_push(out, xt_string_new(textBytes + matches[0].rm_so, (int64_t)(matches[0].rm_eo - matches[0].rm_so)));
|
|
190
195
|
for (int g = 1; g <= jsTotal; g++) {
|
|
191
196
|
xt_value groupValue = XT_UNDEFINED;
|
|
192
197
|
for (int i = 0; i < groupCount; i++) {
|
|
193
198
|
if (groups[i] != g) continue;
|
|
194
199
|
if (matches[i + 1].rm_so >= 0) {
|
|
195
|
-
groupValue = xt_string_new(
|
|
200
|
+
groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
196
201
|
}
|
|
197
202
|
break;
|
|
198
203
|
}
|
|
199
204
|
xt_array_push(out, groupValue);
|
|
200
205
|
}
|
|
201
|
-
xt_set(out, xt_string_from_cstr("index"), xt_number((double)matches[0].rm_so));
|
|
206
|
+
xt_set(out, xt_string_from_cstr("index"), xt_number((double)xt_string_unit_offset(text, matches[0].rm_so)));
|
|
202
207
|
xt_set(out, xt_string_from_cstr("input"), textValue);
|
|
203
208
|
xt_set(out, xt_string_from_cstr("groups"), XT_UNDEFINED);
|
|
204
209
|
free(matches);
|
|
@@ -212,6 +217,7 @@ xt_value xt_regexp_match(xt_value input, xt_value regexp) {
|
|
|
212
217
|
if (!xt_regexp_is_global(regexp)) return xt_regexp_exec(regexp, input);
|
|
213
218
|
xt_value textValue = xt_to_string(input);
|
|
214
219
|
xt_string *text = xt_as_string(textValue);
|
|
220
|
+
const char *textBytes = xt_string_bytes(text);
|
|
215
221
|
xt_value results = xt_array_new(0, NULL);
|
|
216
222
|
int32_t cursor = 0;
|
|
217
223
|
int found = 0;
|
|
@@ -220,7 +226,7 @@ xt_value xt_regexp_match(xt_value input, xt_value regexp) {
|
|
|
220
226
|
int32_t end = 0;
|
|
221
227
|
if (!xt_regexp_find(regexp, input, cursor, &start, &end)) break;
|
|
222
228
|
found = 1;
|
|
223
|
-
xt_array_push(results, xt_string_new(
|
|
229
|
+
xt_array_push(results, xt_string_new(textBytes + start, (int64_t)(end - start)));
|
|
224
230
|
cursor = end > start ? end : end + 1;
|
|
225
231
|
}
|
|
226
232
|
return found ? results : XT_NULL;
|
|
@@ -241,10 +247,11 @@ static void xt_regexp_append(char **out, size_t *length, size_t *capacity, const
|
|
|
241
247
|
static void xt_regexp_append_group(char **out, size_t *length, size_t *capacity,
|
|
242
248
|
xt_string *text, regmatch_t *matches, int *groups,
|
|
243
249
|
int groupCount, int group) {
|
|
250
|
+
const char *textBytes = xt_string_bytes(text);
|
|
244
251
|
for (int i = 0; i < groupCount; i++) {
|
|
245
252
|
if (groups[i] != group) continue;
|
|
246
253
|
if (matches[i + 1].rm_so >= 0) {
|
|
247
|
-
xt_regexp_append(out, length, capacity,
|
|
254
|
+
xt_regexp_append(out, length, capacity, textBytes + matches[i + 1].rm_so,
|
|
248
255
|
(size_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
249
256
|
}
|
|
250
257
|
return;
|
|
@@ -255,32 +262,34 @@ static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *cap
|
|
|
255
262
|
xt_string *replacement, xt_string *text,
|
|
256
263
|
regmatch_t *matches, int *groups, int groupCount,
|
|
257
264
|
int jsTotal) {
|
|
265
|
+
const char *repl = xt_string_bytes(replacement);
|
|
266
|
+
const char *textBytes = xt_string_bytes(text);
|
|
258
267
|
size_t start = (size_t)matches[0].rm_so;
|
|
259
268
|
size_t end = (size_t)matches[0].rm_eo;
|
|
260
269
|
for (size_t i = 0; i < replacement->length; i++) {
|
|
261
|
-
char c =
|
|
270
|
+
char c = repl[i];
|
|
262
271
|
if (c != '$' || i + 1 >= replacement->length) {
|
|
263
272
|
xt_regexp_append(out, length, capacity, &c, 1);
|
|
264
273
|
continue;
|
|
265
274
|
}
|
|
266
|
-
char next =
|
|
275
|
+
char next = repl[i + 1];
|
|
267
276
|
if (next == '$') {
|
|
268
277
|
xt_regexp_append(out, length, capacity, "$", 1);
|
|
269
278
|
i++;
|
|
270
279
|
} else if (next == '&') {
|
|
271
|
-
xt_regexp_append(out, length, capacity,
|
|
280
|
+
xt_regexp_append(out, length, capacity, textBytes + start, end - start);
|
|
272
281
|
i++;
|
|
273
282
|
} else if (next == '`') {
|
|
274
|
-
xt_regexp_append(out, length, capacity,
|
|
283
|
+
xt_regexp_append(out, length, capacity, textBytes, start);
|
|
275
284
|
i++;
|
|
276
285
|
} else if (next == '\'') {
|
|
277
|
-
xt_regexp_append(out, length, capacity,
|
|
286
|
+
xt_regexp_append(out, length, capacity, textBytes + end, text->length - end);
|
|
278
287
|
i++;
|
|
279
288
|
} else if (next >= '0' && next <= '9') {
|
|
280
289
|
int value = next - '0';
|
|
281
290
|
int digits = 1;
|
|
282
|
-
if (i + 2 < replacement->length &&
|
|
283
|
-
int two = value * 10 + (
|
|
291
|
+
if (i + 2 < replacement->length && repl[i + 2] >= '0' && repl[i + 2] <= '9') {
|
|
292
|
+
int two = value * 10 + (repl[i + 2] - '0');
|
|
284
293
|
if (two <= jsTotal) { value = two; digits = 2; }
|
|
285
294
|
}
|
|
286
295
|
if (value >= 1 && value <= jsTotal) {
|
|
@@ -299,6 +308,7 @@ static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *cap
|
|
|
299
308
|
xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replacementValue) {
|
|
300
309
|
xt_string *text = xt_as_string(xt_to_string(value));
|
|
301
310
|
if (!text) return value;
|
|
311
|
+
const char *textBytes = xt_string_bytes(text);
|
|
302
312
|
int global = xt_regexp_is_global(regexpValue);
|
|
303
313
|
int useFunction = XT_IS_FUNCTION(replacementValue);
|
|
304
314
|
xt_string *replacement = NULL;
|
|
@@ -324,28 +334,28 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
324
334
|
if (!xt_regexp_run(regexpValue, value, cursor, matches, cap, groups, &groupCount)) break;
|
|
325
335
|
int32_t start = (int32_t)matches[0].rm_so;
|
|
326
336
|
int32_t end = (int32_t)matches[0].rm_eo;
|
|
327
|
-
xt_regexp_append(&out, &length, &capacity,
|
|
337
|
+
xt_regexp_append(&out, &length, &capacity, textBytes + cursor, (size_t)(start - cursor));
|
|
328
338
|
if (useFunction) {
|
|
329
339
|
/* Accumulate in an `xt_array` so the earlier match/group strings stay
|
|
330
340
|
reachable while later groups (and the callback itself) allocate. */
|
|
331
341
|
xt_value argArray = xt_array_new(0, NULL);
|
|
332
|
-
xt_array_push(argArray, xt_string_new(
|
|
342
|
+
xt_array_push(argArray, xt_string_new(textBytes + start, (int64_t)(end - start)));
|
|
333
343
|
for (int g = 1; g <= jsTotal; g++) {
|
|
334
344
|
xt_value groupValue = XT_UNDEFINED;
|
|
335
345
|
for (int i = 0; i < groupCount; i++) {
|
|
336
346
|
if (groups[i] != g) continue;
|
|
337
347
|
if (matches[i + 1].rm_so >= 0) {
|
|
338
|
-
groupValue = xt_string_new(
|
|
348
|
+
groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
339
349
|
}
|
|
340
350
|
break;
|
|
341
351
|
}
|
|
342
352
|
xt_array_push(argArray, groupValue);
|
|
343
353
|
}
|
|
344
|
-
xt_array_push(argArray, xt_number((double)start));
|
|
354
|
+
xt_array_push(argArray, xt_number((double)xt_string_unit_offset(text, start)));
|
|
345
355
|
xt_array_push(argArray, xt_to_string(value));
|
|
346
356
|
xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, xt_as_array(argArray)->items);
|
|
347
357
|
xt_string *producedText = xt_as_string(xt_to_string(produced));
|
|
348
|
-
if (producedText) xt_regexp_append(&out, &length, &capacity, producedText
|
|
358
|
+
if (producedText) xt_regexp_append(&out, &length, &capacity, xt_string_bytes(producedText), producedText->length);
|
|
349
359
|
} else {
|
|
350
360
|
xt_regexp_expand_replacement(&out, &length, &capacity, replacement, text, matches, groups, groupCount, jsTotal);
|
|
351
361
|
}
|
|
@@ -355,7 +365,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
355
365
|
} else {
|
|
356
366
|
/* Zero-width match: copy one byte so the scan always progresses. */
|
|
357
367
|
if (cursor >= (int32_t)text->length) break;
|
|
358
|
-
xt_regexp_append(&out, &length, &capacity,
|
|
368
|
+
xt_regexp_append(&out, &length, &capacity, textBytes + cursor, 1);
|
|
359
369
|
cursor++;
|
|
360
370
|
}
|
|
361
371
|
if (!global) break;
|
|
@@ -367,7 +377,7 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
367
377
|
return value;
|
|
368
378
|
}
|
|
369
379
|
size_t tail = text->length - (size_t)cursor;
|
|
370
|
-
xt_regexp_append(&out, &length, &capacity,
|
|
380
|
+
xt_regexp_append(&out, &length, &capacity, textBytes + cursor, tail);
|
|
371
381
|
xt_value result = xt_string_new(out, (int64_t)length);
|
|
372
382
|
free(out);
|
|
373
383
|
free(matches);
|
|
@@ -381,6 +391,7 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
|
|
|
381
391
|
xt_value result = xt_array_new(0, NULL);
|
|
382
392
|
xt_string *text = xt_as_string(xt_to_string(value));
|
|
383
393
|
if (!text) return result;
|
|
394
|
+
const char *textBytes = xt_string_bytes(text);
|
|
384
395
|
int64_t limit = limitValue == XT_UNDEFINED ? 0x7fffffff : (int64_t)xt_to_int32(limitValue);
|
|
385
396
|
if (limit <= 0) return result;
|
|
386
397
|
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
@@ -400,14 +411,14 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
|
|
|
400
411
|
int32_t end = (int32_t)matches[0].rm_eo;
|
|
401
412
|
if (start != q) { q++; continue; }
|
|
402
413
|
if (end == p) { q++; continue; }
|
|
403
|
-
xt_array_push(result, xt_string_new(
|
|
414
|
+
xt_array_push(result, xt_string_new(textBytes + p, (int64_t)(q - p)));
|
|
404
415
|
if ((int64_t)xt_as_array(result)->length >= limit) { free(matches); free(groups); return result; }
|
|
405
416
|
for (int g = 1; g <= jsTotal; g++) {
|
|
406
417
|
xt_value groupValue = XT_UNDEFINED;
|
|
407
418
|
for (int i = 0; i < groupCount; i++) {
|
|
408
419
|
if (groups[i] != g) continue;
|
|
409
420
|
if (matches[i + 1].rm_so >= 0) {
|
|
410
|
-
groupValue = xt_string_new(
|
|
421
|
+
groupValue = xt_string_new(textBytes + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
411
422
|
}
|
|
412
423
|
break;
|
|
413
424
|
}
|
|
@@ -418,7 +429,7 @@ xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitVal
|
|
|
418
429
|
q = p;
|
|
419
430
|
}
|
|
420
431
|
if ((int64_t)xt_as_array(result)->length < limit) {
|
|
421
|
-
xt_array_push(result, xt_string_new(
|
|
432
|
+
xt_array_push(result, xt_string_new(textBytes + p, (int64_t)(len - p)));
|
|
422
433
|
}
|
|
423
434
|
free(matches);
|
|
424
435
|
free(groups);
|
|
@@ -431,16 +442,20 @@ xt_value xt_regexp_get_property(xt_value target, xt_value key) {
|
|
|
431
442
|
if (!XT_IS_STRING(key)) return XT_UNDEFINED;
|
|
432
443
|
xt_string *k = xt_as_string(key);
|
|
433
444
|
xt_string *f = xt_as_string(regexp->flags);
|
|
445
|
+
const char *keyBytes = xt_string_bytes(k);
|
|
434
446
|
int flag = 0;
|
|
435
|
-
if (k->length == 6 && memcmp(
|
|
436
|
-
if (k->length == 5 && memcmp(
|
|
437
|
-
if (k->length == 9 && memcmp(
|
|
438
|
-
if (k->length == 6 && memcmp(
|
|
439
|
-
else if (k->length == 10 && memcmp(
|
|
440
|
-
else if (k->length == 9 && memcmp(
|
|
441
|
-
else if (k->length == 6 && memcmp(
|
|
447
|
+
if (k->length == 6 && memcmp(keyBytes, "source", 6) == 0) return regexp->source;
|
|
448
|
+
if (k->length == 5 && memcmp(keyBytes, "flags", 5) == 0) return regexp->flags;
|
|
449
|
+
if (k->length == 9 && memcmp(keyBytes, "lastIndex", 9) == 0) return xt_number(0);
|
|
450
|
+
if (k->length == 6 && memcmp(keyBytes, "global", 6) == 0) flag = 'g';
|
|
451
|
+
else if (k->length == 10 && memcmp(keyBytes, "ignoreCase", 10) == 0) flag = 'i';
|
|
452
|
+
else if (k->length == 9 && memcmp(keyBytes, "multiline", 9) == 0) flag = 'm';
|
|
453
|
+
else if (k->length == 6 && memcmp(keyBytes, "sticky", 6) == 0) flag = 'y';
|
|
442
454
|
if (flag) {
|
|
443
|
-
if (f)
|
|
455
|
+
if (f) {
|
|
456
|
+
const char *flags = xt_string_bytes(f);
|
|
457
|
+
for (uint32_t i = 0; i < f->length; i++) if (flags[i] == flag) return XT_TRUE;
|
|
458
|
+
}
|
|
444
459
|
return XT_FALSE;
|
|
445
460
|
}
|
|
446
461
|
return XT_UNDEFINED;
|
|
@@ -17,6 +17,8 @@ int32_t xt_set_size(xt_value value) {
|
|
|
17
17
|
xt_value xt_iter_length(xt_value value) {
|
|
18
18
|
if (xt_is_map(value)) return xt_number((double)xt_map_size(value));
|
|
19
19
|
if (xt_is_set(value)) return xt_number((double)xt_set_size(value));
|
|
20
|
+
/* Strings iterate by code point, not by UTF-16 unit. */
|
|
21
|
+
if (XT_IS_STRING(value)) return xt_number((double)xt_string_code_point_count(xt_as_string(value)));
|
|
20
22
|
return xt_array_length(value);
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -70,12 +72,16 @@ xt_value xt_iter_has(xt_value value, xt_value index) {
|
|
|
70
72
|
if (xt_is_generator(value)) return xt_bool(xt_generator_has_next(value));
|
|
71
73
|
if (xt_is_map(value)) return xt_bool(xt_to_int32(index) < xt_map_size(value));
|
|
72
74
|
if (xt_is_set(value)) return xt_bool(xt_to_int32(index) < xt_set_size(value));
|
|
75
|
+
if (XT_IS_STRING(value))
|
|
76
|
+
return xt_bool(xt_to_int32(index) <
|
|
77
|
+
(int32_t)xt_string_code_point_count(xt_as_string(value)));
|
|
73
78
|
return xt_bool(xt_to_int32(index) < xt_to_int32(xt_array_length(value)));
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
xt_value xt_iter_value(xt_value value, xt_value index) {
|
|
77
82
|
if (xt_is_iterator_state(value)) return ((xt_iterator_state *)XT_GET_PTR(value))->cached;
|
|
78
83
|
if (xt_is_generator(value)) return xt_generator_iter_value(value);
|
|
84
|
+
if (XT_IS_STRING(value)) return xt_string_code_point_value(xt_as_string(value), xt_to_int32(index));
|
|
79
85
|
if (xt_is_map(value)) {
|
|
80
86
|
xt_map *map = (xt_map *)XT_GET_PTR(value);
|
|
81
87
|
int64_t i = (int64_t)xt_to_int32(index);
|
|
@@ -1,42 +1,49 @@
|
|
|
1
1
|
/* -- string methods ------------------------------------------------------- */
|
|
2
2
|
|
|
3
|
+
/* `padStart`/`padEnd` measure the target length in UTF-16 code units and may
|
|
4
|
+
* cut the pad string through a surrogate pair. */
|
|
3
5
|
static xt_value xt_string_pad(xt_value value, xt_value lengthValue, xt_value padValue, int start) {
|
|
4
6
|
xt_string *s = xt_as_string(value);
|
|
5
7
|
if (!s) return value;
|
|
6
8
|
int32_t target = xt_to_int32(lengthValue);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
10
|
+
if (target <= units) return value;
|
|
11
|
+
xt_value padSymbol = padValue == XT_UNDEFINED ? xt_string_from_cstr(" ") : padValue;
|
|
12
|
+
xt_string *pad = xt_as_string(xt_to_string(padSymbol));
|
|
9
13
|
if (!pad || pad->length == 0) return value;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
int32_t padUnits = (int32_t)xt_string_utf16_length(pad);
|
|
15
|
+
if (padUnits <= 0) return value;
|
|
16
|
+
uint32_t needed = (uint32_t)(target - units);
|
|
17
|
+
xt_value padding = xt_string_from_cstr("");
|
|
18
|
+
uint32_t filled = 0;
|
|
19
|
+
while (filled < needed) {
|
|
20
|
+
uint32_t remaining = needed - filled;
|
|
21
|
+
int32_t chunk = remaining < (uint32_t)padUnits ? (int32_t)remaining : padUnits;
|
|
22
|
+
padding = xt_add(padding, xt_string_slice_units(pad, 0, chunk));
|
|
23
|
+
filled += (uint32_t)chunk;
|
|
19
24
|
}
|
|
20
|
-
return
|
|
25
|
+
return start ? xt_add(padding, value) : xt_add(value, padding);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
static xt_value xt_string_trim_side(xt_value value, int start) {
|
|
24
29
|
xt_string *s = xt_as_string(value);
|
|
25
30
|
if (!s) return value;
|
|
31
|
+
const char *bytes = xt_string_bytes(s);
|
|
26
32
|
uint32_t begin = 0;
|
|
27
33
|
uint32_t end = s->length;
|
|
28
|
-
if (start) { while (begin < end && isspace((unsigned char)
|
|
29
|
-
else { while (end > begin && isspace((unsigned char)
|
|
30
|
-
return xt_string_new(
|
|
34
|
+
if (start) { while (begin < end && isspace((unsigned char)bytes[begin])) begin++; }
|
|
35
|
+
else { while (end > begin && isspace((unsigned char)bytes[end - 1])) end--; }
|
|
36
|
+
return xt_string_new(bytes + begin, end - begin);
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
static xt_value xt_string_at(xt_value value, xt_value indexValue) {
|
|
34
40
|
xt_string *s = xt_as_string(value);
|
|
35
41
|
if (!s) return XT_UNDEFINED;
|
|
42
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
36
43
|
int32_t index = indexValue == XT_UNDEFINED ? 0 : xt_to_int32(indexValue);
|
|
37
|
-
if (index < 0) index +=
|
|
38
|
-
if (index < 0 ||
|
|
39
|
-
return
|
|
44
|
+
if (index < 0) index += units;
|
|
45
|
+
if (index < 0 || index >= units) return XT_UNDEFINED;
|
|
46
|
+
return xt_string_unit_value(s, index);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
static xt_value xt_string_replace_all(xt_value value, xt_value searchValue, xt_value replacementValue) {
|
|
@@ -45,21 +52,35 @@ static xt_value xt_string_replace_all(xt_value value, xt_value searchValue, xt_v
|
|
|
45
52
|
if (!s) return value;
|
|
46
53
|
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
47
54
|
xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
|
|
48
|
-
if (!search || search->length == 0)
|
|
55
|
+
if (!search || search->length == 0) {
|
|
56
|
+
/* An empty search inserts the replacement before every UTF-16 code unit
|
|
57
|
+
* and once at the end. */
|
|
58
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
59
|
+
xt_value replacementText = xt_to_string(replacementValue);
|
|
60
|
+
xt_value result = replacementText;
|
|
61
|
+
for (int32_t i = 0; i < units; i++) {
|
|
62
|
+
result = xt_add(result, xt_string_unit_value(s, i));
|
|
63
|
+
result = xt_add(result, replacementText);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
const char *text = xt_string_bytes(s);
|
|
68
|
+
const char *needle = xt_string_bytes(search);
|
|
69
|
+
const char *repl = xt_string_bytes(replacement);
|
|
49
70
|
size_t capacity = s->length + replacement->length * 4 + 16;
|
|
50
71
|
char *out = (char *)malloc(capacity);
|
|
51
72
|
if (!out) abort();
|
|
52
73
|
size_t length = 0;
|
|
53
74
|
uint32_t i = 0;
|
|
54
75
|
while (i < s->length) {
|
|
55
|
-
if (i + search->length <= s->length && memcmp(
|
|
76
|
+
if (i + search->length <= s->length && memcmp(text + i, needle, search->length) == 0) {
|
|
56
77
|
if (length + replacement->length + 2 > capacity) { capacity = (length + replacement->length + 2) * 2; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
57
|
-
memcpy(out + length,
|
|
78
|
+
memcpy(out + length, repl, replacement->length);
|
|
58
79
|
length += replacement->length;
|
|
59
80
|
i += search->length;
|
|
60
81
|
} else {
|
|
61
82
|
if (length + 2 > capacity) { capacity *= 2; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
62
|
-
out[length++] =
|
|
83
|
+
out[length++] = text[i++];
|
|
63
84
|
}
|
|
64
85
|
}
|
|
65
86
|
xt_value result = xt_string_new(out, length);
|
|
@@ -67,6 +88,33 @@ static xt_value xt_string_replace_all(xt_value value, xt_value searchValue, xt_v
|
|
|
67
88
|
return result;
|
|
68
89
|
}
|
|
69
90
|
|
|
91
|
+
/* `limit` is a UTF-16 index; the last possible match starts at or before it. */
|
|
92
|
+
static xt_value xt_string_last_index_of(xt_string *s, xt_string *needle, int32_t limit) {
|
|
93
|
+
if (!s || !needle) return xt_number(-1);
|
|
94
|
+
const char *text = xt_string_bytes(s);
|
|
95
|
+
const char *need = xt_string_bytes(needle);
|
|
96
|
+
int32_t units = (int32_t)xt_string_utf16_length(s);
|
|
97
|
+
if (limit < 0) limit = 0;
|
|
98
|
+
if (limit > units) limit = units;
|
|
99
|
+
if (needle->length == 0) return xt_number((double)limit);
|
|
100
|
+
/* The low half of a surrogate pair is a match candidate of its own, but a
|
|
101
|
+
* byte search from the pair start cannot see it. */
|
|
102
|
+
if (!xt_string_is_boundary(s, limit)) {
|
|
103
|
+
xt_string *tail = xt_as_string(xt_string_slice_units(s, limit, units));
|
|
104
|
+
if (needle->length <= tail->length &&
|
|
105
|
+
memcmp(xt_string_bytes(tail), need, needle->length) == 0)
|
|
106
|
+
return xt_number((double)limit);
|
|
107
|
+
}
|
|
108
|
+
if (needle->length > s->length) return xt_number(-1);
|
|
109
|
+
int32_t i = xt_string_byte_offset(s, limit);
|
|
110
|
+
if (i > (int32_t)s->length - (int32_t)needle->length) i = (int32_t)s->length - (int32_t)needle->length;
|
|
111
|
+
for (; i >= 0; i--) {
|
|
112
|
+
if (((unsigned char)text[i] & 0xC0) == 0x80) continue; /* not a code-point boundary */
|
|
113
|
+
if (memcmp(text + i, need, needle->length) == 0) return xt_number((double)xt_string_unit_offset(s, i));
|
|
114
|
+
}
|
|
115
|
+
return xt_number(-1);
|
|
116
|
+
}
|
|
117
|
+
|
|
70
118
|
xt_value xt_ext_string_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
|
|
71
119
|
*handled = 1;
|
|
72
120
|
if (strcmp(method, "at") == 0) return xt_string_at(target, xt_arg_at(argc, argv, 0));
|
|
@@ -86,45 +134,36 @@ xt_value xt_ext_string_method(xt_value target, const char *method, int32_t argc,
|
|
|
86
134
|
xt_value regexp = xt_arg_at(argc, argv, 0);
|
|
87
135
|
if (xt_is_regexp(regexp)) {
|
|
88
136
|
int32_t start = 0;
|
|
89
|
-
if (xt_regexp_find(regexp, target, 0, &start, NULL))
|
|
137
|
+
if (xt_regexp_find(regexp, target, 0, &start, NULL)) {
|
|
138
|
+
xt_string *text = xt_as_string(xt_to_string(target));
|
|
139
|
+
return xt_number((double)xt_string_unit_offset(text, start));
|
|
140
|
+
}
|
|
90
141
|
return xt_number(-1);
|
|
91
142
|
}
|
|
92
143
|
*handled = 0;
|
|
93
144
|
return XT_UNDEFINED;
|
|
94
145
|
}
|
|
95
|
-
if (strcmp(method, "replaceAll") == 0) {
|
|
96
|
-
xt_value searchValue = xt_arg_at(argc, argv, 0);
|
|
97
|
-
if (xt_is_regexp(searchValue)) return xt_regexp_replace(target, searchValue, xt_arg_at(argc, argv, 1));
|
|
98
|
-
*handled = 0;
|
|
99
|
-
return XT_UNDEFINED;
|
|
100
|
-
}
|
|
101
146
|
if (strcmp(method, "lastIndexOf") == 0) {
|
|
102
147
|
xt_string *s = xt_as_string(target);
|
|
103
148
|
xt_string *needle = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
104
149
|
if (!s || !needle) return xt_number(-1);
|
|
105
|
-
int32_t limit = (argc >= 2 && argv[1] != XT_UNDEFINED) ? xt_to_int32(argv[1])
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (nlen == 0) return xt_number((double)(limit < 0 ? 0 : limit));
|
|
109
|
-
int32_t i = limit;
|
|
110
|
-
if (i > (int32_t)s->length - nlen) i = (int32_t)s->length - nlen;
|
|
111
|
-
for (; i >= 0; i--) {
|
|
112
|
-
if (memcmp(s->data + i, needle->data, (size_t)nlen) == 0) return xt_number((double)i);
|
|
113
|
-
}
|
|
114
|
-
return xt_number(-1);
|
|
150
|
+
int32_t limit = (argc >= 2 && argv[1] != XT_UNDEFINED) ? xt_to_int32(argv[1])
|
|
151
|
+
: (int32_t)xt_string_utf16_length(s);
|
|
152
|
+
return xt_string_last_index_of(s, needle, limit);
|
|
115
153
|
}
|
|
116
154
|
if (strcmp(method, "localeCompare") == 0) {
|
|
117
155
|
xt_string *a = xt_as_string(target);
|
|
118
156
|
xt_string *b = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
119
|
-
int cmp = strcmp(a ? a
|
|
157
|
+
int cmp = strcmp(a ? xt_string_bytes(a) : "", b ? xt_string_bytes(b) : "");
|
|
120
158
|
return xt_number(cmp < 0 ? -1 : cmp > 0 ? 1 : 0);
|
|
121
159
|
}
|
|
122
160
|
if (strcmp(method, "codePointAt") == 0) {
|
|
123
161
|
xt_string *s = xt_as_string(target);
|
|
124
162
|
if (!s) return XT_UNDEFINED;
|
|
125
163
|
int32_t index = xt_to_int32(xt_arg_at(argc, argv, 0));
|
|
126
|
-
|
|
127
|
-
|
|
164
|
+
int32_t code = xt_string_code_point_at(s, index);
|
|
165
|
+
if (code < 0) return XT_UNDEFINED;
|
|
166
|
+
return xt_number((double)code);
|
|
128
167
|
}
|
|
129
168
|
if (strcmp(method, "valueOf") == 0 || strcmp(method, "toString") == 0) return target;
|
|
130
169
|
*handled = 0;
|
package/runtime/xt_symbol.c
CHANGED
|
@@ -98,7 +98,7 @@ xt_value xt_symbol_to_string(xt_value value) {
|
|
|
98
98
|
char *buffer = (char *)malloc(8 + length);
|
|
99
99
|
if (!buffer) abort();
|
|
100
100
|
memcpy(buffer, "Symbol(", 7);
|
|
101
|
-
if (description) memcpy(buffer + 7, description
|
|
101
|
+
if (description) memcpy(buffer + 7, xt_string_bytes(description), length);
|
|
102
102
|
buffer[7 + length] = ')';
|
|
103
103
|
xt_value result = xt_string_new(buffer, 8 + length);
|
|
104
104
|
free(buffer);
|