xbintsc 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/ast/expressions.d.ts +8 -0
- package/dist/src/binder/binder.js +1 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/references.d.ts +1 -1
- package/dist/src/binder/references.js +34 -5
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/binder/types.d.ts +4 -0
- package/dist/src/binder/types.js.map +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +9 -1
- package/dist/src/codegen/generator/calls/access.js +146 -13
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
- package/dist/src/codegen/generator/calls/closures.js +25 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +12 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/calls/literals.js +12 -4
- package/dist/src/codegen/generator/calls/literals.js.map +1 -1
- package/dist/src/codegen/generator/calls/super.js +4 -7
- package/dist/src/codegen/generator/calls/super.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +21 -2
- package/dist/src/codegen/generator/context.js +42 -5
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.js +1 -3
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
- package/dist/src/codegen/generator/expressions/primary.js +99 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.js +25 -15
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +15 -0
- package/dist/src/codegen/generator/state.js +28 -1
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
- package/dist/src/codegen/generator/statements/control-flow.js +85 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +15 -6
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
- package/dist/src/codegen/generator/statements/loops.js +25 -11
- package/dist/src/codegen/generator/statements/loops.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +41 -1
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +1 -1
- package/dist/src/driver/compiler.js +33 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +4 -3
- package/dist/src/driver/modules.js +61 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/toolchain.js +1 -1
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.js +4 -0
- package/dist/src/parser/declarations/classes.js.map +1 -1
- package/dist/src/parser/declarations/type-members.js.map +1 -1
- package/dist/src/parser/expressions.d.ts +2 -1
- package/dist/src/parser/expressions.js +10 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -2
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +79 -6
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/parser.js +0 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/statements.d.ts +1 -0
- package/dist/src/parser/statements.js +13 -1
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +2 -2
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/ast/factory.test.d.ts +1 -0
- package/dist/tests/ast/factory.test.js +35 -0
- package/dist/tests/ast/factory.test.js.map +1 -0
- package/dist/tests/ast/visitor.test.d.ts +1 -0
- package/dist/tests/ast/visitor.test.js +53 -0
- package/dist/tests/ast/visitor.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +109 -9
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.d.ts +1 -0
- package/dist/tests/codegen/closures.test.js +32 -0
- package/dist/tests/codegen/closures.test.js.map +1 -0
- package/dist/tests/codegen/expressions.test.d.ts +1 -0
- package/dist/tests/codegen/expressions.test.js +149 -0
- package/dist/tests/codegen/expressions.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.js +14 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/codegen/values.test.d.ts +1 -0
- package/dist/tests/codegen/values.test.js +48 -0
- package/dist/tests/codegen/values.test.js.map +1 -0
- package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
- package/dist/tests/diagnostics/diagnostic.test.js +117 -0
- package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
- package/dist/tests/diagnostics/source.test.d.ts +1 -0
- package/dist/tests/diagnostics/source.test.js +58 -0
- package/dist/tests/diagnostics/source.test.js.map +1 -0
- package/dist/tests/driver/modules.test.d.ts +1 -0
- package/dist/tests/driver/modules.test.js +140 -0
- package/dist/tests/driver/modules.test.js.map +1 -0
- package/dist/tests/driver/paths.test.js +17 -1
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
- package/dist/tests/driver/runtime-lib.test.js +19 -0
- package/dist/tests/driver/runtime-lib.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +91 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +11 -1
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +14 -1
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +465 -2
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/differential.test.d.ts +7 -0
- package/dist/tests/e2e/differential.test.js +581 -0
- package/dist/tests/e2e/differential.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +21 -0
- package/dist/tests/e2e/harness.js +26 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/extensions/node.test.d.ts +1 -0
- package/dist/tests/extensions/node.test.js +32 -0
- package/dist/tests/extensions/node.test.js.map +1 -0
- package/dist/tests/lexer/identifiers.test.d.ts +1 -0
- package/dist/tests/lexer/identifiers.test.js +13 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -0
- package/dist/tests/lexer/numbers.test.d.ts +1 -0
- package/dist/tests/lexer/numbers.test.js +23 -0
- package/dist/tests/lexer/numbers.test.js.map +1 -0
- package/dist/tests/lexer/strings.test.d.ts +1 -0
- package/dist/tests/lexer/strings.test.js +72 -0
- package/dist/tests/lexer/strings.test.js.map +1 -0
- package/dist/tests/parser/declarations.test.d.ts +1 -0
- package/dist/tests/parser/declarations.test.js +70 -0
- package/dist/tests/parser/declarations.test.js.map +1 -0
- package/dist/tests/parser/expressions.test.d.ts +1 -0
- package/dist/tests/parser/expressions.test.js +47 -0
- package/dist/tests/parser/expressions.test.js.map +1 -0
- package/dist/tests/parser/helpers.test.d.ts +1 -0
- package/dist/tests/parser/helpers.test.js +58 -0
- package/dist/tests/parser/helpers.test.js.map +1 -0
- package/dist/tests/parser/literals.test.d.ts +1 -0
- package/dist/tests/parser/literals.test.js +70 -0
- package/dist/tests/parser/literals.test.js.map +1 -0
- package/dist/tests/parser/module-syntax.test.d.ts +1 -0
- package/dist/tests/parser/module-syntax.test.js +91 -0
- package/dist/tests/parser/module-syntax.test.js.map +1 -0
- package/dist/tests/parser/statements.test.d.ts +1 -0
- package/dist/tests/parser/statements.test.js +85 -0
- package/dist/tests/parser/statements.test.js.map +1 -0
- package/dist/tests/parser/type-members.test.d.ts +1 -0
- package/dist/tests/parser/type-members.test.js +86 -0
- package/dist/tests/parser/type-members.test.js.map +1 -0
- package/dist/tests/parser/types.test.d.ts +1 -0
- package/dist/tests/parser/types.test.js +143 -0
- package/dist/tests/parser/types.test.js.map +1 -0
- package/package.json +6 -1
- package/runtime/ext_node/fs/fs_ops.c +36 -1
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +88 -2
- package/runtime/xt_builtins.c +174 -6
- package/runtime/xt_containers.c +234 -42
- package/runtime/xt_generator.c +304 -0
- package/runtime/xt_io.c +170 -30
- package/runtime/xt_promise.c +6 -1
- package/runtime/xt_regex.h +105 -35
- package/runtime/xt_stdlib/dispatch.inc +255 -4
- package/runtime/xt_stdlib/strings.inc +17 -13
- package/runtime/xt_stdlib2/arrays.inc +126 -19
- package/runtime/xt_stdlib2/date.inc +9 -2
- package/runtime/xt_stdlib2/error.inc +162 -12
- package/runtime/xt_stdlib2/helpers.inc +4 -0
- package/runtime/xt_stdlib2/json.inc +19 -6
- package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
- package/runtime/xt_stdlib2/numbers.inc +4 -5
- package/runtime/xt_stdlib2/objects.inc +29 -0
- package/runtime/xt_stdlib2/regexp-match.inc +293 -51
- package/runtime/xt_stdlib2/set.inc +58 -2
- package/runtime/xt_stdlib2/strings.inc +38 -0
- package/runtime/xt_symbol.c +149 -0
- package/runtime/xt_values.c +221 -15
- package/scripts/build-runtime.ts +1 -1
- package/src/ast/expressions.ts +8 -0
- package/src/binder/binder.ts +1 -1
- package/src/binder/references.ts +47 -4
- package/src/binder/types.ts +4 -0
- package/src/cli/main.ts +1 -2
- package/src/codegen/generator/calls/access.ts +158 -13
- package/src/codegen/generator/calls/closures.ts +24 -0
- package/src/codegen/generator/calls/invocation.ts +11 -1
- package/src/codegen/generator/calls/literals.ts +10 -4
- package/src/codegen/generator/calls/super.ts +6 -9
- package/src/codegen/generator/context.ts +44 -7
- package/src/codegen/generator/expressions/assignment.ts +1 -3
- package/src/codegen/generator/expressions/primary.ts +108 -2
- package/src/codegen/generator/generator.ts +0 -1
- package/src/codegen/generator/module.ts +24 -18
- package/src/codegen/generator/state.ts +44 -1
- package/src/codegen/generator/statements/control-flow.ts +86 -0
- package/src/codegen/generator/statements/dispatch.ts +18 -6
- package/src/codegen/generator/statements/loops.ts +29 -11
- package/src/codegen/generator/tables.ts +44 -1
- package/src/driver/compiler.ts +32 -2
- package/src/driver/modules.ts +64 -4
- package/src/driver/toolchain.ts +1 -1
- package/src/lexer/scanner.ts +0 -1
- package/src/parser/declarations/classes.ts +4 -0
- package/src/parser/declarations/type-members.ts +0 -2
- package/src/parser/expressions.ts +13 -1
- package/src/parser/helpers.ts +2 -2
- package/src/parser/literals.ts +82 -7
- package/src/parser/modules.ts +0 -1
- package/src/parser/parser.ts +0 -1
- package/src/parser/statements.ts +14 -0
- package/src/parser/types.ts +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/* RegExp support built on POSIX ERE. JavaScript patterns are translated to
|
|
2
|
+
* their POSIX equivalents, capturing groups are tracked so the JS capture
|
|
3
|
+
* indices stay correct even after `(?:` groups are lowered, and the resulting
|
|
4
|
+
* `regmatch_t` offsets are converted back to offsets into the input string. */
|
|
4
5
|
|
|
5
6
|
static int xt_regex_hex_digit(char c) {
|
|
6
7
|
if (c >= '0' && c <= '9') return c - '0';
|
|
@@ -11,14 +12,25 @@ static int xt_regex_hex_digit(char c) {
|
|
|
11
12
|
|
|
12
13
|
/* JavaScript regexes use escapes POSIX ERE does not understand (`\r`, `\n`,
|
|
13
14
|
* `\d`, `\w`, `\s`, `(?:...)`, ...). Rewrite the common ones into their POSIX
|
|
14
|
-
* equivalents before handing the pattern to the engine.
|
|
15
|
-
*
|
|
16
|
-
|
|
15
|
+
* equivalents before handing the pattern to the engine.
|
|
16
|
+
*
|
|
17
|
+
* When `jsGroups` is non-NULL it receives, for each POSIX capturing group
|
|
18
|
+
* (1-based, stored at `jsGroups[i - 1]`), the JavaScript capture index, or -1
|
|
19
|
+
* for a group that was non-capturing in the source (POSIX has no
|
|
20
|
+
* non-capturing groups, so `(?:...)` is emitted as a capturing group).
|
|
21
|
+
* `*groupCount` receives the number of groups seen. `jsGroups` must have room
|
|
22
|
+
* for at least `strlen(pattern) + 1` entries.
|
|
23
|
+
*
|
|
24
|
+
* Returns a malloc'd, NUL-terminated string the caller must free. */
|
|
25
|
+
static char *xt_regexp_translate_js(const char *pattern, int *jsGroups, int *groupCount) {
|
|
17
26
|
size_t length = strlen(pattern);
|
|
18
27
|
size_t capacity = length * 16 + 16;
|
|
19
28
|
char *out = (char *)malloc(capacity);
|
|
20
29
|
if (!out) abort();
|
|
21
30
|
size_t written = 0;
|
|
31
|
+
int posixGroup = 0;
|
|
32
|
+
int jsCapture = 1;
|
|
33
|
+
int inClass = 0;
|
|
22
34
|
for (size_t i = 0; i < length; i++) {
|
|
23
35
|
char c = pattern[i];
|
|
24
36
|
const char *replacement = NULL;
|
|
@@ -46,12 +58,21 @@ static char *xt_regexp_translate_js(const char *pattern) {
|
|
|
46
58
|
}
|
|
47
59
|
default: break;
|
|
48
60
|
}
|
|
49
|
-
} else if (c == '
|
|
61
|
+
} else if (c == '[') {
|
|
62
|
+
inClass = 1;
|
|
63
|
+
} else if (c == ']') {
|
|
64
|
+
inClass = 0;
|
|
65
|
+
} else if (!inClass && c == '(' && i + 2 < length && pattern[i + 1] == '?' && pattern[i + 2] == ':') {
|
|
50
66
|
/* POSIX ERE has no non-capturing groups; a capturing group matches the
|
|
51
67
|
* same text, only the capture bookkeeping differs. */
|
|
52
68
|
literal = '(';
|
|
53
69
|
consume = 2;
|
|
54
70
|
}
|
|
71
|
+
if (!inClass && c == '(') {
|
|
72
|
+
if (jsGroups) jsGroups[posixGroup] = (consume == 2) ? -1 : jsCapture;
|
|
73
|
+
if (consume != 2) jsCapture++;
|
|
74
|
+
posixGroup++;
|
|
75
|
+
}
|
|
55
76
|
if (replacement) {
|
|
56
77
|
size_t addition = strlen(replacement);
|
|
57
78
|
if (written + addition + 1 > capacity) { capacity = (written + addition) * 2 + 16; out = (char *)realloc(out, capacity); if (!out) abort(); }
|
|
@@ -67,20 +88,29 @@ static char *xt_regexp_translate_js(const char *pattern) {
|
|
|
67
88
|
i += (size_t)consume;
|
|
68
89
|
}
|
|
69
90
|
out[written] = '\0';
|
|
91
|
+
if (groupCount) *groupCount = posixGroup;
|
|
70
92
|
return out;
|
|
71
93
|
}
|
|
72
94
|
|
|
73
|
-
|
|
95
|
+
/* Run `regexpValue` against `input` starting at byte offset `start`. On a
|
|
96
|
+
* match, fills `matches` (up to `nmatch` entries, offsets made absolute) and
|
|
97
|
+
* returns 1; otherwise returns 0 and leaves `matches` untouched. When
|
|
98
|
+
* `jsGroups` is non-NULL it receives the JS capture index for each POSIX
|
|
99
|
+
* group, and `*groupCount` the number of POSIX groups. */
|
|
100
|
+
static int xt_regexp_run(xt_value regexpValue, xt_value input, int32_t start,
|
|
101
|
+
regmatch_t *matches, size_t nmatch,
|
|
102
|
+
int *jsGroups, int *groupCount) {
|
|
74
103
|
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
75
104
|
xt_string *pattern = xt_as_string(regexp->source);
|
|
76
|
-
|
|
105
|
+
xt_value textValue = xt_to_string(input);
|
|
106
|
+
xt_string *text = xt_as_string(textValue);
|
|
77
107
|
if (!pattern || !text) return 0;
|
|
78
108
|
if (start < 0) start = 0;
|
|
79
109
|
if ((uint32_t)start > text->length) return 0;
|
|
80
110
|
int flags = REG_EXTENDED;
|
|
81
111
|
xt_string *f = xt_as_string(regexp->flags);
|
|
82
112
|
if (f) for (uint32_t i = 0; i < f->length; i++) if (f->data[i] == 'i') flags |= REG_ICASE;
|
|
83
|
-
char *translated = xt_regexp_translate_js(pattern->data);
|
|
113
|
+
char *translated = xt_regexp_translate_js(pattern->data, jsGroups, groupCount);
|
|
84
114
|
if (!translated) return 0;
|
|
85
115
|
regex_t compiled;
|
|
86
116
|
if (regcomp(&compiled, translated, flags) != 0) {
|
|
@@ -88,14 +118,23 @@ int xt_regexp_find(xt_value regexpValue, xt_value input, int32_t start, int32_t
|
|
|
88
118
|
return 0;
|
|
89
119
|
}
|
|
90
120
|
free(translated);
|
|
91
|
-
|
|
92
|
-
match.rm_so = 0;
|
|
93
|
-
match.rm_eo = 0;
|
|
94
|
-
int ok = regexec(&compiled, text->data + start, 1, &match, 0) == 0;
|
|
121
|
+
int ok = regexec(&compiled, text->data + start, nmatch, matches, 0) == 0;
|
|
95
122
|
regfree(&compiled);
|
|
96
123
|
if (!ok) return 0;
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
for (size_t i = 0; i < nmatch; i++) {
|
|
125
|
+
if (matches[i].rm_so >= 0) {
|
|
126
|
+
matches[i].rm_so += start;
|
|
127
|
+
matches[i].rm_eo += start;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
int xt_regexp_find(xt_value regexpValue, xt_value input, int32_t start, int32_t *matchStart, int32_t *matchEnd) {
|
|
134
|
+
regmatch_t match;
|
|
135
|
+
if (!xt_regexp_run(regexpValue, input, start, &match, 1, NULL, NULL)) return 0;
|
|
136
|
+
if (matchStart) *matchStart = (int32_t)match.rm_so;
|
|
137
|
+
if (matchEnd) *matchEnd = (int32_t)match.rm_eo;
|
|
99
138
|
return 1;
|
|
100
139
|
}
|
|
101
140
|
|
|
@@ -107,78 +146,281 @@ static int xt_regexp_is_global(xt_value regexpValue) {
|
|
|
107
146
|
return 0;
|
|
108
147
|
}
|
|
109
148
|
|
|
149
|
+
/* Number of JavaScript capturing groups for the pattern. */
|
|
150
|
+
static int xt_regexp_js_group_count(xt_value regexpValue) {
|
|
151
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
152
|
+
xt_string *pattern = xt_as_string(regexp->source);
|
|
153
|
+
if (!pattern) return 0;
|
|
154
|
+
int count = (int)pattern->length + 1;
|
|
155
|
+
int *groups = (int *)malloc(sizeof(int) * (size_t)count);
|
|
156
|
+
int groupCount = 0;
|
|
157
|
+
char *translated = xt_regexp_translate_js(pattern->data, groups, &groupCount);
|
|
158
|
+
free(translated);
|
|
159
|
+
int total = 0;
|
|
160
|
+
for (int i = 0; i < groupCount; i++) if (groups[i] > total) total = groups[i];
|
|
161
|
+
free(groups);
|
|
162
|
+
return total;
|
|
163
|
+
}
|
|
164
|
+
|
|
110
165
|
static xt_value xt_regexp_test(xt_value target, xt_value input) {
|
|
111
166
|
return xt_bool(xt_regexp_find(target, input, 0, NULL, NULL));
|
|
112
167
|
}
|
|
113
168
|
|
|
114
|
-
/* JavaScript-style match object: `[0]` is the matched text, `
|
|
115
|
-
* match offset and `input`
|
|
169
|
+
/* JavaScript-style match object: `[0]` is the matched text, `[1..n]` the
|
|
170
|
+
* capture groups, `index` is the match offset and `input` the searched string. */
|
|
116
171
|
xt_value xt_regexp_exec(xt_value target, xt_value input) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
172
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(target);
|
|
173
|
+
xt_string *pattern = xt_as_string(regexp->source);
|
|
174
|
+
size_t cap = (pattern ? pattern->length : 0) + 2;
|
|
175
|
+
regmatch_t *matches = (regmatch_t *)malloc(sizeof(regmatch_t) * cap);
|
|
176
|
+
int *groups = (int *)malloc(sizeof(int) * cap);
|
|
177
|
+
if (!matches || !groups) { free(matches); free(groups); return XT_NULL; }
|
|
120
178
|
xt_value textValue = xt_to_string(input);
|
|
121
179
|
xt_string *text = xt_as_string(textValue);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
180
|
+
int groupCount = 0;
|
|
181
|
+
if (!xt_regexp_run(target, input, 0, matches, cap, groups, &groupCount)) {
|
|
182
|
+
free(matches);
|
|
183
|
+
free(groups);
|
|
184
|
+
return XT_NULL;
|
|
185
|
+
}
|
|
186
|
+
int jsTotal = 0;
|
|
187
|
+
for (int i = 0; i < groupCount; i++) if (groups[i] > jsTotal) jsTotal = groups[i];
|
|
188
|
+
xt_value out = xt_array_new(0, NULL);
|
|
189
|
+
xt_array_push(out, xt_string_new(text->data + matches[0].rm_so, (int64_t)(matches[0].rm_eo - matches[0].rm_so)));
|
|
190
|
+
for (int g = 1; g <= jsTotal; g++) {
|
|
191
|
+
xt_value groupValue = XT_UNDEFINED;
|
|
192
|
+
for (int i = 0; i < groupCount; i++) {
|
|
193
|
+
if (groups[i] != g) continue;
|
|
194
|
+
if (matches[i + 1].rm_so >= 0) {
|
|
195
|
+
groupValue = xt_string_new(text->data + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
xt_array_push(out, groupValue);
|
|
200
|
+
}
|
|
201
|
+
xt_set(out, xt_string_from_cstr("index"), xt_number((double)matches[0].rm_so));
|
|
202
|
+
xt_set(out, xt_string_from_cstr("input"), textValue);
|
|
203
|
+
xt_set(out, xt_string_from_cstr("groups"), XT_UNDEFINED);
|
|
204
|
+
free(matches);
|
|
205
|
+
free(groups);
|
|
127
206
|
return out;
|
|
128
207
|
}
|
|
129
208
|
|
|
209
|
+
/* `String.prototype.match`: a non-global regexp yields the exec result, a
|
|
210
|
+
* global one yields the array of matched substrings (or null). */
|
|
211
|
+
xt_value xt_regexp_match(xt_value input, xt_value regexp) {
|
|
212
|
+
if (!xt_regexp_is_global(regexp)) return xt_regexp_exec(regexp, input);
|
|
213
|
+
xt_value textValue = xt_to_string(input);
|
|
214
|
+
xt_string *text = xt_as_string(textValue);
|
|
215
|
+
xt_value results = xt_array_new(0, NULL);
|
|
216
|
+
int32_t cursor = 0;
|
|
217
|
+
int found = 0;
|
|
218
|
+
while (cursor <= (int32_t)text->length) {
|
|
219
|
+
int32_t start = 0;
|
|
220
|
+
int32_t end = 0;
|
|
221
|
+
if (!xt_regexp_find(regexp, input, cursor, &start, &end)) break;
|
|
222
|
+
found = 1;
|
|
223
|
+
xt_array_push(results, xt_string_new(text->data + start, (int64_t)(end - start)));
|
|
224
|
+
cursor = end > start ? end : end + 1;
|
|
225
|
+
}
|
|
226
|
+
return found ? results : XT_NULL;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* -- replacement expansion ------------------------------------------------- */
|
|
230
|
+
|
|
231
|
+
static void xt_regexp_append(char **out, size_t *length, size_t *capacity, const char *data, size_t count) {
|
|
232
|
+
if (*length + count + 1 > *capacity) {
|
|
233
|
+
*capacity = (*length + count + 1) * 2 + 16;
|
|
234
|
+
*out = (char *)realloc(*out, *capacity);
|
|
235
|
+
if (!*out) abort();
|
|
236
|
+
}
|
|
237
|
+
memcpy(*out + *length, data, count);
|
|
238
|
+
*length += count;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static void xt_regexp_append_group(char **out, size_t *length, size_t *capacity,
|
|
242
|
+
xt_string *text, regmatch_t *matches, int *groups,
|
|
243
|
+
int groupCount, int group) {
|
|
244
|
+
for (int i = 0; i < groupCount; i++) {
|
|
245
|
+
if (groups[i] != group) continue;
|
|
246
|
+
if (matches[i + 1].rm_so >= 0) {
|
|
247
|
+
xt_regexp_append(out, length, capacity, text->data + matches[i + 1].rm_so,
|
|
248
|
+
(size_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static void xt_regexp_expand_replacement(char **out, size_t *length, size_t *capacity,
|
|
255
|
+
xt_string *replacement, xt_string *text,
|
|
256
|
+
regmatch_t *matches, int *groups, int groupCount,
|
|
257
|
+
int jsTotal) {
|
|
258
|
+
size_t start = (size_t)matches[0].rm_so;
|
|
259
|
+
size_t end = (size_t)matches[0].rm_eo;
|
|
260
|
+
for (size_t i = 0; i < replacement->length; i++) {
|
|
261
|
+
char c = replacement->data[i];
|
|
262
|
+
if (c != '$' || i + 1 >= replacement->length) {
|
|
263
|
+
xt_regexp_append(out, length, capacity, &c, 1);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
char next = replacement->data[i + 1];
|
|
267
|
+
if (next == '$') {
|
|
268
|
+
xt_regexp_append(out, length, capacity, "$", 1);
|
|
269
|
+
i++;
|
|
270
|
+
} else if (next == '&') {
|
|
271
|
+
xt_regexp_append(out, length, capacity, text->data + start, end - start);
|
|
272
|
+
i++;
|
|
273
|
+
} else if (next == '`') {
|
|
274
|
+
xt_regexp_append(out, length, capacity, text->data, start);
|
|
275
|
+
i++;
|
|
276
|
+
} else if (next == '\'') {
|
|
277
|
+
xt_regexp_append(out, length, capacity, text->data + end, text->length - end);
|
|
278
|
+
i++;
|
|
279
|
+
} else if (next >= '0' && next <= '9') {
|
|
280
|
+
int value = next - '0';
|
|
281
|
+
int digits = 1;
|
|
282
|
+
if (i + 2 < replacement->length && replacement->data[i + 2] >= '0' && replacement->data[i + 2] <= '9') {
|
|
283
|
+
int two = value * 10 + (replacement->data[i + 2] - '0');
|
|
284
|
+
if (two <= jsTotal) { value = two; digits = 2; }
|
|
285
|
+
}
|
|
286
|
+
if (value >= 1 && value <= jsTotal) {
|
|
287
|
+
xt_regexp_append_group(out, length, capacity, text, matches, groups, groupCount, value);
|
|
288
|
+
i += (size_t)digits;
|
|
289
|
+
} else {
|
|
290
|
+
xt_regexp_append(out, length, capacity, "$", 1);
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
xt_regexp_append(out, length, capacity, "$", 1);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
130
298
|
/* `String.prototype.replace`/`replaceAll` with a RegExp search value. */
|
|
131
299
|
xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replacementValue) {
|
|
132
300
|
xt_string *text = xt_as_string(xt_to_string(value));
|
|
133
301
|
if (!text) return value;
|
|
134
|
-
xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
|
|
135
|
-
if (!replacement) return value;
|
|
136
302
|
int global = xt_regexp_is_global(regexpValue);
|
|
137
|
-
|
|
303
|
+
int useFunction = XT_IS_FUNCTION(replacementValue);
|
|
304
|
+
xt_string *replacement = NULL;
|
|
305
|
+
if (!useFunction) {
|
|
306
|
+
replacement = xt_as_string(xt_to_string(replacementValue));
|
|
307
|
+
if (!replacement) return value;
|
|
308
|
+
}
|
|
309
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
310
|
+
xt_string *pattern = xt_as_string(regexp->source);
|
|
311
|
+
size_t cap = (pattern ? pattern->length : 0) + 2;
|
|
312
|
+
regmatch_t *matches = (regmatch_t *)malloc(sizeof(regmatch_t) * cap);
|
|
313
|
+
int *groups = (int *)malloc(sizeof(int) * cap);
|
|
314
|
+
if (!matches || !groups) { free(matches); free(groups); return value; }
|
|
315
|
+
size_t capacity = text->length + 64;
|
|
138
316
|
char *out = (char *)malloc(capacity);
|
|
139
|
-
if (!out)
|
|
317
|
+
if (!out) { free(matches); free(groups); return value; }
|
|
140
318
|
size_t length = 0;
|
|
141
319
|
int32_t cursor = 0;
|
|
142
320
|
int replaced = 0;
|
|
321
|
+
int jsTotal = xt_regexp_js_group_count(regexpValue);
|
|
143
322
|
for (;;) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
323
|
+
int groupCount = 0;
|
|
324
|
+
if (!xt_regexp_run(regexpValue, value, cursor, matches, cap, groups, &groupCount)) break;
|
|
325
|
+
int32_t start = (int32_t)matches[0].rm_so;
|
|
326
|
+
int32_t end = (int32_t)matches[0].rm_eo;
|
|
327
|
+
xt_regexp_append(&out, &length, &capacity, text->data + cursor, (size_t)(start - cursor));
|
|
328
|
+
if (useFunction) {
|
|
329
|
+
xt_value *args = (xt_value *)malloc(sizeof(xt_value) * (size_t)(jsTotal + 3));
|
|
330
|
+
args[0] = xt_string_new(text->data + start, (int64_t)(end - start));
|
|
331
|
+
for (int g = 1; g <= jsTotal; g++) {
|
|
332
|
+
xt_value groupValue = XT_UNDEFINED;
|
|
333
|
+
for (int i = 0; i < groupCount; i++) {
|
|
334
|
+
if (groups[i] != g) continue;
|
|
335
|
+
if (matches[i + 1].rm_so >= 0) {
|
|
336
|
+
groupValue = xt_string_new(text->data + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
args[g] = groupValue;
|
|
341
|
+
}
|
|
342
|
+
args[jsTotal + 1] = xt_number((double)start);
|
|
343
|
+
args[jsTotal + 2] = xt_to_string(value);
|
|
344
|
+
xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, args);
|
|
345
|
+
free(args);
|
|
346
|
+
xt_string *producedText = xt_as_string(xt_to_string(produced));
|
|
347
|
+
if (producedText) xt_regexp_append(&out, &length, &capacity, producedText->data, producedText->length);
|
|
348
|
+
} else {
|
|
349
|
+
xt_regexp_expand_replacement(&out, &length, &capacity, replacement, text, matches, groups, groupCount, jsTotal);
|
|
153
350
|
}
|
|
154
|
-
memcpy(out + length, text->data + cursor, prefix);
|
|
155
|
-
length += prefix;
|
|
156
|
-
memcpy(out + length, replacement->data, replacement->length);
|
|
157
|
-
length += replacement->length;
|
|
158
351
|
replaced = 1;
|
|
159
352
|
if (end > start) {
|
|
160
353
|
cursor = end;
|
|
161
354
|
} else {
|
|
162
355
|
/* Zero-width match: copy one byte so the scan always progresses. */
|
|
163
356
|
if (cursor >= (int32_t)text->length) break;
|
|
164
|
-
out
|
|
357
|
+
xt_regexp_append(&out, &length, &capacity, text->data + cursor, 1);
|
|
358
|
+
cursor++;
|
|
165
359
|
}
|
|
166
360
|
if (!global) break;
|
|
167
361
|
}
|
|
168
362
|
if (!replaced) {
|
|
169
363
|
free(out);
|
|
364
|
+
free(matches);
|
|
365
|
+
free(groups);
|
|
170
366
|
return value;
|
|
171
367
|
}
|
|
172
368
|
size_t tail = text->length - (size_t)cursor;
|
|
173
|
-
|
|
174
|
-
capacity = length + tail + 1;
|
|
175
|
-
out = (char *)realloc(out, capacity);
|
|
176
|
-
if (!out) abort();
|
|
177
|
-
}
|
|
178
|
-
memcpy(out + length, text->data + cursor, tail);
|
|
179
|
-
length += tail;
|
|
369
|
+
xt_regexp_append(&out, &length, &capacity, text->data + cursor, tail);
|
|
180
370
|
xt_value result = xt_string_new(out, (int64_t)length);
|
|
181
371
|
free(out);
|
|
372
|
+
free(matches);
|
|
373
|
+
free(groups);
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* `String.prototype.split` with a RegExp separator. Capture groups are
|
|
378
|
+
* inserted into the result, matching the specification. */
|
|
379
|
+
xt_value xt_regexp_split(xt_value value, xt_value regexpValue, xt_value limitValue) {
|
|
380
|
+
xt_value result = xt_array_new(0, NULL);
|
|
381
|
+
xt_string *text = xt_as_string(xt_to_string(value));
|
|
382
|
+
if (!text) return result;
|
|
383
|
+
int64_t limit = limitValue == XT_UNDEFINED ? 0x7fffffff : (int64_t)xt_to_int32(limitValue);
|
|
384
|
+
if (limit <= 0) return result;
|
|
385
|
+
xt_regexp *regexp = (xt_regexp *)XT_GET_PTR(regexpValue);
|
|
386
|
+
xt_string *pattern = xt_as_string(regexp->source);
|
|
387
|
+
size_t cap = (pattern ? pattern->length : 0) + 2;
|
|
388
|
+
regmatch_t *matches = (regmatch_t *)malloc(sizeof(regmatch_t) * cap);
|
|
389
|
+
int *groups = (int *)malloc(sizeof(int) * cap);
|
|
390
|
+
if (!matches || !groups) { free(matches); free(groups); return result; }
|
|
391
|
+
int jsTotal = xt_regexp_js_group_count(regexpValue);
|
|
392
|
+
int32_t len = (int32_t)text->length;
|
|
393
|
+
int32_t p = 0;
|
|
394
|
+
int32_t q = 0;
|
|
395
|
+
while (q < len) {
|
|
396
|
+
int groupCount = 0;
|
|
397
|
+
if (!xt_regexp_run(regexpValue, value, q, matches, cap, groups, &groupCount)) break;
|
|
398
|
+
int32_t start = (int32_t)matches[0].rm_so;
|
|
399
|
+
int32_t end = (int32_t)matches[0].rm_eo;
|
|
400
|
+
if (start != q) { q++; continue; }
|
|
401
|
+
if (end == p) { q++; continue; }
|
|
402
|
+
xt_array_push(result, xt_string_new(text->data + p, (int64_t)(q - p)));
|
|
403
|
+
if ((int64_t)xt_as_array(result)->length >= limit) { free(matches); free(groups); return result; }
|
|
404
|
+
for (int g = 1; g <= jsTotal; g++) {
|
|
405
|
+
xt_value groupValue = XT_UNDEFINED;
|
|
406
|
+
for (int i = 0; i < groupCount; i++) {
|
|
407
|
+
if (groups[i] != g) continue;
|
|
408
|
+
if (matches[i + 1].rm_so >= 0) {
|
|
409
|
+
groupValue = xt_string_new(text->data + matches[i + 1].rm_so, (int64_t)(matches[i + 1].rm_eo - matches[i + 1].rm_so));
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
xt_array_push(result, groupValue);
|
|
414
|
+
if ((int64_t)xt_as_array(result)->length >= limit) { free(matches); free(groups); return result; }
|
|
415
|
+
}
|
|
416
|
+
p = end;
|
|
417
|
+
q = p;
|
|
418
|
+
}
|
|
419
|
+
if ((int64_t)xt_as_array(result)->length < limit) {
|
|
420
|
+
xt_array_push(result, xt_string_new(text->data + p, (int64_t)(len - p)));
|
|
421
|
+
}
|
|
422
|
+
free(matches);
|
|
423
|
+
free(groups);
|
|
182
424
|
return result;
|
|
183
425
|
}
|
|
184
426
|
|
|
@@ -11,15 +11,71 @@ int32_t xt_set_size(xt_value value) {
|
|
|
11
11
|
|
|
12
12
|
/* -- for...of iteration --------------------------------------------------- */
|
|
13
13
|
|
|
14
|
-
/* `for...of` lowers to a
|
|
15
|
-
index directly; Maps yield `[key, value]` pairs and Sets yield their
|
|
14
|
+
/* `for...of` lowers to a per-step check plus an index lookup. Arrays and
|
|
15
|
+
strings index directly; Maps yield `[key, value]` pairs and Sets yield their
|
|
16
|
+
values. Generators pull their next value during the check. */
|
|
16
17
|
xt_value xt_iter_length(xt_value value) {
|
|
17
18
|
if (xt_is_map(value)) return xt_number((double)xt_map_size(value));
|
|
18
19
|
if (xt_is_set(value)) return xt_number((double)xt_set_size(value));
|
|
19
20
|
return xt_array_length(value);
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
/* Resolved `[Symbol.iterator]()` result: primes one `next()` per step and
|
|
24
|
+
* caches its `value` until `xt_iter_value` consumes it. */
|
|
25
|
+
typedef struct {
|
|
26
|
+
xt_header header;
|
|
27
|
+
xt_value iterator;
|
|
28
|
+
xt_value cached;
|
|
29
|
+
int32_t done;
|
|
30
|
+
} xt_iterator_state;
|
|
31
|
+
|
|
32
|
+
static int xt_is_iterator_state(xt_value value) {
|
|
33
|
+
if (!XT_IS_OBJECT(value)) return 0;
|
|
34
|
+
return ((xt_object *)XT_GET_PTR(value))->header.kind == XT_OBJECT_KIND_ITERATOR;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Resolve the iteration protocol once, before the loop starts. Arrays,
|
|
38
|
+
* strings, Maps, Sets and generators keep their built-in index protocol;
|
|
39
|
+
* any other object with a `Symbol.iterator` method is wrapped. */
|
|
40
|
+
xt_value xt_iter_open(xt_value value) {
|
|
41
|
+
if (XT_IS_ARRAY(value) || XT_IS_STRING(value) || xt_is_map(value) || xt_is_set(value) ||
|
|
42
|
+
xt_is_generator(value))
|
|
43
|
+
return value;
|
|
44
|
+
if (XT_IS_OBJECT(value)) {
|
|
45
|
+
xt_value method = xt_object_get(value, xt_symbol_well_known("iterator"));
|
|
46
|
+
if (XT_IS_FUNCTION(method)) {
|
|
47
|
+
xt_value iterator = xt_call_with_this(method, value, 0, NULL);
|
|
48
|
+
xt_iterator_state *state =
|
|
49
|
+
(xt_iterator_state *)xt_alloc(sizeof(xt_iterator_state), XT_OBJECT_KIND_ITERATOR);
|
|
50
|
+
state->iterator = iterator;
|
|
51
|
+
state->cached = XT_UNDEFINED;
|
|
52
|
+
state->done = 0;
|
|
53
|
+
return XT_FROM_PTR(XT_TAG_OBJECT, state);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
xt_throw(xt_string_from_cstr("TypeError: value is not iterable"));
|
|
57
|
+
return XT_UNDEFINED;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
xt_value xt_iter_has(xt_value value, xt_value index) {
|
|
61
|
+
if (xt_is_iterator_state(value)) {
|
|
62
|
+
xt_iterator_state *state = (xt_iterator_state *)XT_GET_PTR(value);
|
|
63
|
+
if (state->done) return XT_FALSE;
|
|
64
|
+
xt_value next = xt_get(state->iterator, xt_string_from_cstr("next"));
|
|
65
|
+
xt_value result = xt_call_with_this(next, state->iterator, 0, NULL);
|
|
66
|
+
state->cached = xt_get(result, xt_string_from_cstr("value"));
|
|
67
|
+
state->done = xt_truthy(xt_get(result, xt_string_from_cstr("done")));
|
|
68
|
+
return xt_bool(!state->done);
|
|
69
|
+
}
|
|
70
|
+
if (xt_is_generator(value)) return xt_bool(xt_generator_has_next(value));
|
|
71
|
+
if (xt_is_map(value)) return xt_bool(xt_to_int32(index) < xt_map_size(value));
|
|
72
|
+
if (xt_is_set(value)) return xt_bool(xt_to_int32(index) < xt_set_size(value));
|
|
73
|
+
return xt_bool(xt_to_int32(index) < xt_to_int32(xt_array_length(value)));
|
|
74
|
+
}
|
|
75
|
+
|
|
22
76
|
xt_value xt_iter_value(xt_value value, xt_value index) {
|
|
77
|
+
if (xt_is_iterator_state(value)) return ((xt_iterator_state *)XT_GET_PTR(value))->cached;
|
|
78
|
+
if (xt_is_generator(value)) return xt_generator_iter_value(value);
|
|
23
79
|
if (xt_is_map(value)) {
|
|
24
80
|
xt_map *map = (xt_map *)XT_GET_PTR(value);
|
|
25
81
|
int64_t i = (int64_t)xt_to_int32(index);
|
|
@@ -75,6 +75,44 @@ xt_value xt_ext_string_method(xt_value target, const char *method, int32_t argc,
|
|
|
75
75
|
if (strcmp(method, "trimStart") == 0) return xt_string_trim_side(target, 1);
|
|
76
76
|
if (strcmp(method, "trimEnd") == 0) return xt_string_trim_side(target, 0);
|
|
77
77
|
if (strcmp(method, "replaceAll") == 0) return xt_string_replace_all(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
78
|
+
if (strcmp(method, "match") == 0) return xt_regexp_match(target, xt_arg_at(argc, argv, 0));
|
|
79
|
+
if (strcmp(method, "split") == 0) {
|
|
80
|
+
xt_value separator = xt_arg_at(argc, argv, 0);
|
|
81
|
+
if (xt_is_regexp(separator)) return xt_regexp_split(target, separator, xt_arg_at(argc, argv, 1));
|
|
82
|
+
*handled = 0;
|
|
83
|
+
return XT_UNDEFINED;
|
|
84
|
+
}
|
|
85
|
+
if (strcmp(method, "search") == 0) {
|
|
86
|
+
xt_value regexp = xt_arg_at(argc, argv, 0);
|
|
87
|
+
if (xt_is_regexp(regexp)) {
|
|
88
|
+
int32_t start = 0;
|
|
89
|
+
if (xt_regexp_find(regexp, target, 0, &start, NULL)) return xt_number((double)start);
|
|
90
|
+
return xt_number(-1);
|
|
91
|
+
}
|
|
92
|
+
*handled = 0;
|
|
93
|
+
return XT_UNDEFINED;
|
|
94
|
+
}
|
|
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
|
+
if (strcmp(method, "lastIndexOf") == 0) {
|
|
102
|
+
xt_string *s = xt_as_string(target);
|
|
103
|
+
xt_string *needle = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
104
|
+
if (!s || !needle) return xt_number(-1);
|
|
105
|
+
int32_t limit = (argc >= 2 && argv[1] != XT_UNDEFINED) ? xt_to_int32(argv[1]) : (int32_t)s->length;
|
|
106
|
+
if (limit > (int32_t)s->length) limit = (int32_t)s->length;
|
|
107
|
+
int32_t nlen = (int32_t)needle->length;
|
|
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);
|
|
115
|
+
}
|
|
78
116
|
if (strcmp(method, "localeCompare") == 0) {
|
|
79
117
|
xt_string *a = xt_as_string(target);
|
|
80
118
|
xt_string *b = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|