xbintsc 0.3.14 → 0.3.16
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/README.md +22 -22
- package/README.zh-CN.md +20 -19
- package/bin/xbintsc.js +1 -2
- package/dist/src/cli/main.js +8 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/state.js +8 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +5 -6
- package/dist/src/codegen/generator/tables.js +13 -6
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/graph.d.ts +8 -0
- package/dist/src/driver/bundler/graph.js +85 -0
- package/dist/src/driver/bundler/graph.js.map +1 -0
- package/dist/src/driver/bundler/merge.d.ts +11 -0
- package/dist/src/driver/bundler/merge.js +288 -0
- package/dist/src/driver/bundler/merge.js.map +1 -0
- package/dist/src/driver/bundler/resolve.d.ts +12 -0
- package/dist/src/driver/bundler/resolve.js +246 -0
- package/dist/src/driver/bundler/resolve.js.map +1 -0
- package/dist/src/driver/bundler/symbols.d.ts +15 -0
- package/dist/src/driver/bundler/symbols.js +51 -0
- package/dist/src/driver/bundler/symbols.js.map +1 -0
- package/dist/src/driver/bundler/types.d.ts +36 -0
- package/dist/src/driver/bundler/types.js +5 -0
- package/dist/src/driver/bundler/types.js.map +1 -0
- package/dist/src/driver/index.d.ts +1 -2
- package/dist/src/driver/index.js +1 -2
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/modules.d.ts +9 -16
- package/dist/src/driver/modules.js +8 -644
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/paths.d.ts +0 -9
- package/dist/src/driver/paths.js +0 -31
- package/dist/src/driver/paths.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +10 -10
- package/dist/src/driver/toolchain-provider.js +9 -38
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.js +8 -2
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/catalog.js +2 -1
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/gui/index.d.ts +24 -0
- package/dist/src/extensions/gui/index.js +101 -0
- package/dist/src/extensions/gui/index.js.map +1 -0
- package/dist/src/extensions/native.d.ts +2 -3
- package/dist/src/extensions/native.js +2 -3
- package/dist/src/extensions/native.js.map +1 -1
- package/dist/tests/driver/paths.test.js +1 -9
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/toolchain-provider.test.js +12 -79
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/e2e/compile-basics.test.d.ts +4 -0
- package/dist/tests/e2e/compile-basics.test.js +134 -0
- package/dist/tests/e2e/compile-basics.test.js.map +1 -0
- package/dist/tests/e2e/compile-builtins.test.d.ts +4 -0
- package/dist/tests/e2e/compile-builtins.test.js +191 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -0
- package/dist/tests/e2e/compile-control-flow.test.d.ts +4 -0
- package/dist/tests/e2e/compile-control-flow.test.js +190 -0
- package/dist/tests/e2e/compile-control-flow.test.js.map +1 -0
- package/dist/tests/e2e/compile-modules.test.d.ts +4 -0
- package/dist/tests/e2e/compile-modules.test.js +48 -0
- package/dist/tests/e2e/compile-modules.test.js.map +1 -0
- package/dist/tests/e2e/compile-objects-classes.test.d.ts +4 -0
- package/dist/tests/e2e/compile-objects-classes.test.js +362 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -0
- package/dist/tests/e2e/differential-helpers.d.ts +12 -0
- package/dist/tests/e2e/differential-helpers.js +16 -0
- package/dist/tests/e2e/differential-helpers.js.map +1 -0
- package/dist/tests/e2e/differential-language.test.d.ts +4 -0
- package/dist/tests/e2e/{differential.test.js → differential-language.test.js} +5 -287
- package/dist/tests/e2e/differential-language.test.js.map +1 -0
- package/dist/tests/e2e/differential-symbols.test.d.ts +4 -0
- package/dist/tests/e2e/differential-symbols.test.js +92 -0
- package/dist/tests/e2e/differential-symbols.test.js.map +1 -0
- package/dist/tests/e2e/differential-values.test.d.ts +4 -0
- package/dist/tests/e2e/differential-values.test.js +197 -0
- package/dist/tests/e2e/differential-values.test.js.map +1 -0
- package/dist/tests/e2e/gui.test.d.ts +12 -0
- package/dist/tests/e2e/gui.test.js +493 -0
- package/dist/tests/e2e/gui.test.js.map +1 -0
- package/dist/tests/e2e/native-extensions.test.d.ts +4 -5
- package/dist/tests/e2e/native-extensions.test.js +6 -7
- package/dist/tests/e2e/native-extensions.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-buffer.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js +97 -0
- package/dist/tests/e2e/runtime-coverage-buffer.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js +149 -0
- package/dist/tests/e2e/runtime-coverage-globals.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js +177 -0
- package/dist/tests/e2e/runtime-coverage-node-modules.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js +161 -0
- package/dist/tests/e2e/runtime-coverage-streams.test.js.map +1 -0
- package/dist/tests/e2e/runtime-coverage-system.test.d.ts +4 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js +91 -0
- package/dist/tests/e2e/runtime-coverage-system.test.js.map +1 -0
- package/dist/tests/e2e/timers.test.d.ts +7 -0
- package/dist/tests/e2e/timers.test.js +63 -0
- package/dist/tests/e2e/timers.test.js.map +1 -0
- package/package.json +2 -2
- package/runtime/ext_gui/css.cpp +743 -0
- package/runtime/ext_gui/css.h +98 -0
- package/runtime/ext_gui/document.cpp +264 -0
- package/runtime/ext_gui/document.h +89 -0
- package/runtime/ext_gui/dom.cpp +472 -0
- package/runtime/ext_gui/dom.h +71 -0
- package/runtime/ext_gui/gui.cpp +457 -0
- package/runtime/ext_gui/gui_engine.h +81 -0
- package/runtime/ext_gui/image.cpp +125 -0
- package/runtime/ext_gui/image.h +42 -0
- package/runtime/ext_gui/layout.cpp +748 -0
- package/runtime/ext_gui/layout.h +93 -0
- package/runtime/ext_gui/paint.cpp +137 -0
- package/runtime/ext_gui/paint.h +75 -0
- package/runtime/ext_gui/renderer.cpp +959 -0
- package/runtime/ext_gui/renderer.h +68 -0
- package/runtime/ext_gui/stb_image.h +7988 -0
- package/runtime/ext_gui/style.cpp +771 -0
- package/runtime/ext_gui/style.h +117 -0
- package/runtime/ext_gui/text.cpp +378 -0
- package/runtime/ext_gui/text.h +78 -0
- package/runtime/ext_gui/window.cpp +405 -0
- package/runtime/ext_node/fs/fs_common.h +13 -527
- package/runtime/ext_node/fs/meta_ops.c +3 -3
- package/runtime/ext_node/fs/parts/buffer-bridge.inc +6 -0
- package/runtime/ext_node/fs/parts/builtins.inc +69 -0
- package/runtime/ext_node/fs/parts/dir.inc +47 -0
- package/runtime/ext_node/fs/parts/encoding.inc +186 -0
- package/runtime/ext_node/fs/parts/errors.inc +66 -0
- package/runtime/ext_node/fs/parts/flags.inc +39 -0
- package/runtime/ext_node/fs/parts/platform.inc +86 -0
- package/runtime/ext_node/fs/parts/promisify.inc +15 -0
- package/runtime/ext_node/fs/parts/time.inc +9 -0
- package/runtime/rt.h +36 -4
- package/runtime/rt_internal.h +10 -23
- package/runtime/xt_builtins.c +4 -0
- package/runtime/xt_containers/access.inc +168 -0
- package/runtime/xt_containers/arrays.inc +108 -0
- package/runtime/xt_containers/boxes.inc +11 -0
- package/runtime/xt_containers/objects.inc +326 -0
- package/runtime/xt_containers.c +8 -618
- package/runtime/xt_ext.h +0 -1
- package/runtime/xt_io/base64.inc +87 -0
- package/runtime/xt_io/console.inc +119 -0
- package/runtime/xt_io/exception.inc +142 -0
- package/runtime/xt_io/inspect.inc +212 -0
- package/runtime/xt_io.c +11 -556
- package/runtime/xt_loop.c +262 -24
- package/runtime/xt_regex/api.inc +58 -0
- package/runtime/xt_regex/helpers.inc +68 -0
- package/runtime/xt_regex/matcher.inc +248 -0
- package/runtime/xt_regex/parser.inc +236 -0
- package/runtime/xt_regex/types.inc +58 -0
- package/runtime/xt_regex.h +9 -672
- package/runtime/xt_stdlib2/helpers.inc +2 -4
- package/runtime/xt_values/arithmetic.inc +150 -0
- package/runtime/xt_values/comparison.inc +112 -0
- package/runtime/xt_values/constructors.inc +41 -0
- package/runtime/xt_values/conversions.inc +115 -0
- package/runtime/xt_values/number-format.inc +180 -0
- package/runtime/xt_values/strings.inc +22 -0
- package/runtime/xt_values.c +10 -626
- package/scripts/build-gui.ts +317 -0
- package/scripts/build-runtime.ts +7 -8
- package/scripts/coverage-runtime.ts +4 -4
- package/scripts/package-release.ts +12 -84
- package/src/cli/main.ts +8 -4
- package/src/codegen/generator/state.ts +8 -0
- package/src/codegen/generator/tables.ts +13 -6
- package/src/driver/bundler/graph.ts +95 -0
- package/src/driver/bundler/merge.ts +312 -0
- package/src/driver/bundler/resolve.ts +240 -0
- package/src/driver/bundler/symbols.ts +55 -0
- package/src/driver/bundler/types.ts +37 -0
- package/src/driver/index.ts +1 -9
- package/src/driver/modules.ts +9 -693
- package/src/driver/paths.ts +0 -31
- package/src/driver/toolchain-provider.ts +12 -45
- package/src/driver/toolchain.ts +7 -2
- package/src/extensions/catalog.ts +2 -1
- package/src/extensions/gui/index.ts +107 -0
- package/src/extensions/native.ts +2 -3
- package/dist/src/driver/toolchain-download.d.ts +0 -50
- package/dist/src/driver/toolchain-download.js +0 -102
- package/dist/src/driver/toolchain-download.js.map +0 -1
- package/dist/tests/driver/toolchain-download.test.d.ts +0 -1
- package/dist/tests/driver/toolchain-download.test.js +0 -53
- package/dist/tests/driver/toolchain-download.test.js.map +0 -1
- package/dist/tests/e2e/compile.test.d.ts +0 -8
- package/dist/tests/e2e/compile.test.js +0 -893
- package/dist/tests/e2e/compile.test.js.map +0 -1
- package/dist/tests/e2e/differential.test.d.ts +0 -7
- package/dist/tests/e2e/differential.test.js.map +0 -1
- package/dist/tests/e2e/runtime-coverage.test.d.ts +0 -11
- package/dist/tests/e2e/runtime-coverage.test.js +0 -646
- package/dist/tests/e2e/runtime-coverage.test.js.map +0 -1
- package/scripts/fetch-toolchain.ts +0 -101
- package/src/driver/toolchain-download.ts +0 -135
package/runtime/xt_loop.c
CHANGED
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
* `dgram`, `http`, ...) deliver callbacks without baking an OS abstraction
|
|
6
6
|
* into the compiler. Generated `main` calls `xt_run_event_loop()` once the
|
|
7
7
|
* program body (and its microtasks) have finished; the loop returns as soon as
|
|
8
|
-
* no file descriptors remain registered, so programs that never touch
|
|
9
|
-
* unaffected.
|
|
8
|
+
* no file descriptors or timers remain registered, so programs that never touch
|
|
9
|
+
* I/O or timers are unaffected.
|
|
10
10
|
*
|
|
11
11
|
* Extensions register descriptors with `xt_loop_add` and receive readiness
|
|
12
|
-
* callbacks. The
|
|
13
|
-
*
|
|
12
|
+
* callbacks. The timer globals (`setTimeout` / `setInterval`) share the same
|
|
13
|
+
* wait: the nearest deadline bounds `select(2)`, so a timer-only program sleeps
|
|
14
|
+
* instead of spinning. The loop drains the promise microtask queue after every
|
|
15
|
+
* batch of callbacks so `await`/`.then` chains keep working inside handlers.
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
#include "rt_internal.h"
|
|
@@ -18,9 +20,11 @@
|
|
|
18
20
|
#include <stdio.h>
|
|
19
21
|
#include <stdlib.h>
|
|
20
22
|
#include <string.h>
|
|
23
|
+
#include <time.h>
|
|
21
24
|
|
|
22
25
|
#if defined(_WIN32)
|
|
23
26
|
#include <winsock2.h>
|
|
27
|
+
#include <windows.h>
|
|
24
28
|
#else
|
|
25
29
|
#include <sys/select.h>
|
|
26
30
|
#include <sys/time.h>
|
|
@@ -48,6 +52,184 @@ static xt_loop_watcher *xt_loop_find(int fd) {
|
|
|
48
52
|
return NULL;
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
/* -- timers --------------------------------------------------------------- */
|
|
56
|
+
/*
|
|
57
|
+
* `setTimeout` / `setInterval` and their `clear*` counterparts are implemented
|
|
58
|
+
* here, next to the reactor they piggyback on. The pending timers live in a
|
|
59
|
+
* flat array (the same shape as the watcher table above); before every wait the
|
|
60
|
+
* reactor looks at the nearest deadline and uses it to bound `select(2)`, so a
|
|
61
|
+
* program whose only pending work is a timer still sleeps instead of spinning.
|
|
62
|
+
*
|
|
63
|
+
* Callbacks are ordinary closure values invoked through `xt_closure_call`, so
|
|
64
|
+
* they capture their lexical environment like any other JS function.
|
|
65
|
+
*/
|
|
66
|
+
typedef struct {
|
|
67
|
+
uint64_t id;
|
|
68
|
+
xt_value callback;
|
|
69
|
+
xt_value *args;
|
|
70
|
+
int32_t argc;
|
|
71
|
+
int64_t due_ms;
|
|
72
|
+
int64_t interval_ms;
|
|
73
|
+
int active;
|
|
74
|
+
} xt_timer;
|
|
75
|
+
|
|
76
|
+
static xt_timer *xt_timers = NULL;
|
|
77
|
+
static uint32_t xt_timer_count = 0;
|
|
78
|
+
static uint32_t xt_timer_capacity = 0;
|
|
79
|
+
static uint64_t xt_timer_next_id = 1;
|
|
80
|
+
|
|
81
|
+
static int64_t xt_loop_now_ms(void) {
|
|
82
|
+
#if defined(_WIN32)
|
|
83
|
+
return (int64_t)GetTickCount64();
|
|
84
|
+
#else
|
|
85
|
+
struct timespec ts;
|
|
86
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
87
|
+
return (int64_t)ts.tv_sec * 1000 + (int64_t)(ts.tv_nsec / 1000000);
|
|
88
|
+
#endif
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static void xt_loop_sleep_ms(int ms) {
|
|
92
|
+
if (ms <= 0) return;
|
|
93
|
+
#if defined(_WIN32)
|
|
94
|
+
Sleep((DWORD)ms);
|
|
95
|
+
#else
|
|
96
|
+
struct timespec ts;
|
|
97
|
+
ts.tv_sec = ms / 1000;
|
|
98
|
+
ts.tv_nsec = (long)(ms % 1000) * 1000000L;
|
|
99
|
+
nanosleep(&ts, NULL);
|
|
100
|
+
#endif
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Coerce a delay argument to a non-negative whole number of milliseconds. */
|
|
104
|
+
static int64_t xt_timer_delay_arg(xt_value value) {
|
|
105
|
+
double d = xt_to_number(value);
|
|
106
|
+
if (d != d || d < 0) return 0;
|
|
107
|
+
if (d > 2147483647.0) return 2147483647;
|
|
108
|
+
return (int64_t)d;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static uint32_t xt_timer_active_count(void) {
|
|
112
|
+
uint32_t count = 0;
|
|
113
|
+
for (uint32_t i = 0; i < xt_timer_count; i++) {
|
|
114
|
+
if (xt_timers[i].active) count++;
|
|
115
|
+
}
|
|
116
|
+
return count;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Earliest deadline among the live timers, or -1 when none is pending. */
|
|
120
|
+
static int64_t xt_timer_next_due(void) {
|
|
121
|
+
int64_t next = -1;
|
|
122
|
+
for (uint32_t i = 0; i < xt_timer_count; i++) {
|
|
123
|
+
if (!xt_timers[i].active) continue;
|
|
124
|
+
if (next < 0 || xt_timers[i].due_ms < next) next = xt_timers[i].due_ms;
|
|
125
|
+
}
|
|
126
|
+
return next;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Reclaim slots left by cleared/fired one-shot timers. */
|
|
130
|
+
static void xt_timer_compact(void) {
|
|
131
|
+
uint32_t write = 0;
|
|
132
|
+
for (uint32_t read = 0; read < xt_timer_count; read++) {
|
|
133
|
+
if (!xt_timers[read].active) continue;
|
|
134
|
+
if (write != read) xt_timers[write] = xt_timers[read];
|
|
135
|
+
write++;
|
|
136
|
+
}
|
|
137
|
+
xt_timer_count = write;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static uint64_t xt_timer_add(xt_value callback, int64_t delay_ms, int64_t interval_ms, int32_t argc, xt_value *argv) {
|
|
141
|
+
if (xt_timer_count >= xt_timer_capacity) {
|
|
142
|
+
xt_timer_capacity = xt_timer_capacity == 0 ? 16 : xt_timer_capacity * 2;
|
|
143
|
+
xt_timer *grown = (xt_timer *)realloc(xt_timers, sizeof(xt_timer) * xt_timer_capacity);
|
|
144
|
+
if (!grown) abort();
|
|
145
|
+
xt_timers = grown;
|
|
146
|
+
}
|
|
147
|
+
xt_timer *timer = &xt_timers[xt_timer_count++];
|
|
148
|
+
timer->id = xt_timer_next_id++;
|
|
149
|
+
timer->callback = callback;
|
|
150
|
+
timer->argc = argc;
|
|
151
|
+
timer->args = NULL;
|
|
152
|
+
if (argc > 0) {
|
|
153
|
+
timer->args = (xt_value *)malloc(sizeof(xt_value) * (size_t)argc);
|
|
154
|
+
if (!timer->args) abort();
|
|
155
|
+
for (int32_t i = 0; i < argc; i++) timer->args[i] = argv[i];
|
|
156
|
+
}
|
|
157
|
+
timer->due_ms = xt_loop_now_ms() + delay_ms;
|
|
158
|
+
timer->interval_ms = interval_ms;
|
|
159
|
+
timer->active = 1;
|
|
160
|
+
return timer->id;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static int xt_timer_cancel(uint64_t id) {
|
|
164
|
+
for (uint32_t i = 0; i < xt_timer_count; i++) {
|
|
165
|
+
if (xt_timers[i].active && xt_timers[i].id == id) {
|
|
166
|
+
xt_timers[i].active = 0;
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Invoke every timer whose deadline has passed. Timers scheduled by a callback
|
|
174
|
+
* (or by a microtask) are left for the next reactor iteration, so a recursive
|
|
175
|
+
* `setTimeout(fn, 0)` cannot spin forever inside a single call. */
|
|
176
|
+
static void xt_timer_run_due(void) {
|
|
177
|
+
xt_timer_compact();
|
|
178
|
+
int64_t now = xt_loop_now_ms();
|
|
179
|
+
uint32_t limit = xt_timer_count;
|
|
180
|
+
for (uint32_t i = 0; i < limit; i++) {
|
|
181
|
+
xt_timer *timer = &xt_timers[i];
|
|
182
|
+
if (!timer->active || timer->due_ms > now) continue;
|
|
183
|
+
xt_value callback = timer->callback;
|
|
184
|
+
int32_t argc = timer->argc;
|
|
185
|
+
xt_value *args = timer->args;
|
|
186
|
+
if (timer->interval_ms > 0) {
|
|
187
|
+
timer->due_ms = now + timer->interval_ms;
|
|
188
|
+
} else {
|
|
189
|
+
timer->active = 0;
|
|
190
|
+
}
|
|
191
|
+
if (XT_IS_FUNCTION(callback)) xt_closure_call(callback, argc, args);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
xt_value xt_set_timeout(int32_t argc, xt_value *argv) {
|
|
196
|
+
xt_value callback = xt_arg_at(argc, argv, 0);
|
|
197
|
+
if (!XT_IS_FUNCTION(callback)) {
|
|
198
|
+
xt_throw(xt_string_from_cstr("TypeError: The \"callback\" argument must be of type function"));
|
|
199
|
+
return XT_UNDEFINED;
|
|
200
|
+
}
|
|
201
|
+
int64_t delay = xt_timer_delay_arg(xt_arg_at(argc, argv, 1));
|
|
202
|
+
int32_t extra = argc > 2 ? argc - 2 : 0;
|
|
203
|
+
uint64_t id = xt_timer_add(callback, delay, 0, extra, extra > 0 ? argv + 2 : NULL);
|
|
204
|
+
return xt_number((double)id);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
xt_value xt_set_interval(int32_t argc, xt_value *argv) {
|
|
208
|
+
xt_value callback = xt_arg_at(argc, argv, 0);
|
|
209
|
+
if (!XT_IS_FUNCTION(callback)) {
|
|
210
|
+
xt_throw(xt_string_from_cstr("TypeError: The \"callback\" argument must be of type function"));
|
|
211
|
+
return XT_UNDEFINED;
|
|
212
|
+
}
|
|
213
|
+
int64_t delay = xt_timer_delay_arg(xt_arg_at(argc, argv, 1));
|
|
214
|
+
/* Intervals are clamped to at least 1ms so an interval can never busy-spin. */
|
|
215
|
+
int64_t interval = delay > 0 ? delay : 1;
|
|
216
|
+
int32_t extra = argc > 2 ? argc - 2 : 0;
|
|
217
|
+
uint64_t id = xt_timer_add(callback, delay, interval, extra, extra > 0 ? argv + 2 : NULL);
|
|
218
|
+
return xt_number((double)id);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
xt_value xt_clear_timeout(int32_t argc, xt_value *argv) {
|
|
222
|
+
double id = xt_to_number(xt_arg_at(argc, argv, 0));
|
|
223
|
+
if (id >= 1) xt_timer_cancel((uint64_t)id);
|
|
224
|
+
return XT_UNDEFINED;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
xt_value xt_clear_interval(int32_t argc, xt_value *argv) {
|
|
228
|
+
double id = xt_to_number(xt_arg_at(argc, argv, 0));
|
|
229
|
+
if (id >= 1) xt_timer_cancel((uint64_t)id);
|
|
230
|
+
return XT_UNDEFINED;
|
|
231
|
+
}
|
|
232
|
+
|
|
51
233
|
int xt_loop_add(int fd, int events, xt_io_handler handler, void *userdata) {
|
|
52
234
|
if (fd < 0 || !handler) return -1;
|
|
53
235
|
xt_loop_watcher *existing = xt_loop_find(fd);
|
|
@@ -81,26 +263,67 @@ void xt_loop_remove(int fd) {
|
|
|
81
263
|
xt_loop_active_count--;
|
|
82
264
|
}
|
|
83
265
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
266
|
+
/* A host (GUI toolkit, embedder) can take over the main loop; see
|
|
267
|
+
* `xt_loop_set_main`. `NULL` keeps the default blocking behaviour. */
|
|
268
|
+
static xt_main_loop_fn xt_loop_main = NULL;
|
|
269
|
+
|
|
270
|
+
void xt_loop_set_main(xt_main_loop_fn fn) { xt_loop_main = fn; }
|
|
271
|
+
|
|
272
|
+
int xt_loop_poll(int timeout_ms) {
|
|
273
|
+
int has_fds = xt_loop_active_count > 0;
|
|
274
|
+
int64_t next_due = xt_timer_next_due();
|
|
275
|
+
if (!has_fds && next_due < 0) {
|
|
276
|
+
xt_drain_microtasks();
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Bound the wait by the nearest timer deadline when one is pending. */
|
|
281
|
+
int wait_ms = timeout_ms;
|
|
282
|
+
if (next_due >= 0) {
|
|
283
|
+
int64_t delta = next_due - xt_loop_now_ms();
|
|
284
|
+
if (delta < 0) delta = 0;
|
|
285
|
+
if (delta > 100000000) delta = 100000000;
|
|
286
|
+
if (wait_ms < 0 || delta < (int64_t)wait_ms) wait_ms = (int)delta;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* Timers only: sleep, then run whatever is due. */
|
|
290
|
+
if (!has_fds) {
|
|
291
|
+
xt_loop_sleep_ms(wait_ms);
|
|
292
|
+
xt_timer_run_due();
|
|
293
|
+
xt_drain_microtasks();
|
|
294
|
+
return (int)(xt_loop_active_count + xt_timer_active_count());
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
fd_set readSet;
|
|
298
|
+
fd_set writeSet;
|
|
299
|
+
FD_ZERO(&readSet);
|
|
300
|
+
FD_ZERO(&writeSet);
|
|
301
|
+
int maxFd = -1;
|
|
302
|
+
for (int i = 0; i < xt_loop_watcher_count; i++) {
|
|
303
|
+
xt_loop_watcher *watcher = &xt_loop_watchers[i];
|
|
304
|
+
if (!watcher->active) continue;
|
|
305
|
+
if (watcher->events & XT_IO_READ) FD_SET(watcher->fd, &readSet);
|
|
306
|
+
if (watcher->events & XT_IO_WRITE) FD_SET(watcher->fd, &writeSet);
|
|
307
|
+
if (watcher->fd > maxFd) maxFd = watcher->fd;
|
|
308
|
+
}
|
|
309
|
+
if (maxFd < 0) {
|
|
310
|
+
xt_timer_run_due();
|
|
311
|
+
xt_drain_microtasks();
|
|
312
|
+
return (int)(xt_loop_active_count + xt_timer_active_count());
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
struct timeval timeout;
|
|
316
|
+
struct timeval *timeoutPtr = NULL;
|
|
317
|
+
if (wait_ms >= 0) {
|
|
318
|
+
timeout.tv_sec = wait_ms / 1000;
|
|
319
|
+
timeout.tv_usec = (wait_ms % 1000) * 1000;
|
|
320
|
+
timeoutPtr = &timeout;
|
|
321
|
+
}
|
|
99
322
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (ready == 0) continue;
|
|
323
|
+
int ready = select(maxFd + 1, &readSet, &writeSet, NULL, timeoutPtr);
|
|
324
|
+
if (ready < 0) return -1;
|
|
103
325
|
|
|
326
|
+
if (ready > 0) {
|
|
104
327
|
/* Collect the ready watchers first: handlers may add/remove descriptors. */
|
|
105
328
|
for (int i = 0; i < xt_loop_watcher_count; i++) {
|
|
106
329
|
xt_loop_watcher *watcher = &xt_loop_watchers[i];
|
|
@@ -110,8 +333,23 @@ void xt_run_event_loop(void) {
|
|
|
110
333
|
if ((watcher->events & XT_IO_WRITE) && FD_ISSET(watcher->fd, &writeSet)) events |= XT_IO_WRITE;
|
|
111
334
|
if (events) watcher->handler(watcher->userdata, events);
|
|
112
335
|
}
|
|
113
|
-
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
xt_timer_run_due();
|
|
339
|
+
xt_drain_microtasks();
|
|
340
|
+
return (int)(xt_loop_active_count + xt_timer_active_count());
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
void xt_run_event_loop(void) {
|
|
344
|
+
/* A host that owns the main loop (GUI toolkit) takes precedence: it is
|
|
345
|
+
responsible for calling `xt_loop_poll` while it runs. */
|
|
346
|
+
if (xt_loop_main != NULL) {
|
|
347
|
+
xt_loop_main();
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
while (xt_loop_has_work()) {
|
|
351
|
+
if (xt_loop_poll(-1) < 0) break;
|
|
114
352
|
}
|
|
115
353
|
}
|
|
116
354
|
|
|
117
|
-
int xt_loop_has_work(void) { return xt_loop_active_count > 0; }
|
|
355
|
+
int xt_loop_has_work(void) { return xt_loop_active_count > 0 || xt_timer_active_count() > 0; }
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* -- public API ----------------------------------------------------------- */
|
|
2
|
+
|
|
3
|
+
static int regcomp(regex_t *preg, const char *pattern, int cflags) {
|
|
4
|
+
xt_re_parser p;
|
|
5
|
+
xt_re_alt *root;
|
|
6
|
+
if (!preg || !pattern) return 1;
|
|
7
|
+
memset(preg, 0, sizeof(*preg));
|
|
8
|
+
p.s = pattern;
|
|
9
|
+
p.i = 0;
|
|
10
|
+
p.error = 0;
|
|
11
|
+
p.groupCount = 0;
|
|
12
|
+
root = xt_re_parse_alt(&p);
|
|
13
|
+
if (!root || p.error || p.s[p.i] != '\0') {
|
|
14
|
+
if (root) xt_re_free_alt(root);
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
preg->root = root;
|
|
18
|
+
preg->icase = (cflags & REG_ICASE) ? 1 : 0;
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static int regexec(const regex_t *preg, const char *string, size_t nmatch,
|
|
23
|
+
regmatch_t pmatch[], int eflags) {
|
|
24
|
+
xt_re_input in;
|
|
25
|
+
size_t start;
|
|
26
|
+
size_t i;
|
|
27
|
+
(void)eflags;
|
|
28
|
+
if (!preg || !preg->root || !string) return 1;
|
|
29
|
+
if (pmatch) {
|
|
30
|
+
for (i = 0; i < nmatch; i++) {
|
|
31
|
+
pmatch[i].rm_so = -1;
|
|
32
|
+
pmatch[i].rm_eo = -1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
in.text = string;
|
|
36
|
+
in.len = strlen(string);
|
|
37
|
+
in.icase = preg->icase;
|
|
38
|
+
for (start = 0; start <= in.len; start++) {
|
|
39
|
+
xt_re_match_result result;
|
|
40
|
+
result.end = 0;
|
|
41
|
+
result.pmatch = pmatch;
|
|
42
|
+
result.nmatch = nmatch;
|
|
43
|
+
if (xt_re_match_alt(preg->root, &in, start, xt_re_record_match, &result, NULL)) {
|
|
44
|
+
if (nmatch > 0 && pmatch) {
|
|
45
|
+
pmatch[0].rm_so = (int)start;
|
|
46
|
+
pmatch[0].rm_eo = (int)result.end;
|
|
47
|
+
}
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static void regfree(regex_t *preg) {
|
|
55
|
+
if (!preg) return;
|
|
56
|
+
xt_re_free_alt(preg->root);
|
|
57
|
+
preg->root = NULL;
|
|
58
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* -- small helpers -------------------------------------------------------- */
|
|
2
|
+
|
|
3
|
+
static void xt_re_set_bit(unsigned char *bits, unsigned char c) {
|
|
4
|
+
bits[c >> 3] |= (unsigned char)(1u << (c & 7));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
static int xt_re_bit_test(const unsigned char *bits, unsigned char c) {
|
|
8
|
+
return (bits[c >> 3] >> (c & 7)) & 1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static unsigned char xt_re_lower(unsigned char c) {
|
|
12
|
+
if (c >= 'A' && c <= 'Z') return (unsigned char)(c - 'A' + 'a');
|
|
13
|
+
return c;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static void xt_re_add_range(unsigned char *bits, unsigned char lo, unsigned char hi) {
|
|
17
|
+
unsigned int c;
|
|
18
|
+
if (hi < lo) {
|
|
19
|
+
unsigned char t = lo;
|
|
20
|
+
lo = hi;
|
|
21
|
+
hi = t;
|
|
22
|
+
}
|
|
23
|
+
for (c = lo; c <= hi; c++) xt_re_set_bit(bits, (unsigned char)c);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Adds a predefined class such as \d or \w. Returns 1 when handled. */
|
|
27
|
+
static int xt_re_add_predefined(unsigned char *bits, char e) {
|
|
28
|
+
int c;
|
|
29
|
+
switch (e) {
|
|
30
|
+
case 'd':
|
|
31
|
+
xt_re_add_range(bits, '0', '9');
|
|
32
|
+
return 1;
|
|
33
|
+
case 'D':
|
|
34
|
+
for (c = 0; c < 256; c++)
|
|
35
|
+
if (!(c >= '0' && c <= '9')) xt_re_set_bit(bits, (unsigned char)c);
|
|
36
|
+
return 1;
|
|
37
|
+
case 'w':
|
|
38
|
+
xt_re_add_range(bits, 'a', 'z');
|
|
39
|
+
xt_re_add_range(bits, 'A', 'Z');
|
|
40
|
+
xt_re_add_range(bits, '0', '9');
|
|
41
|
+
xt_re_set_bit(bits, '_');
|
|
42
|
+
return 1;
|
|
43
|
+
case 'W':
|
|
44
|
+
for (c = 0; c < 256; c++) {
|
|
45
|
+
int is_word = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
46
|
+
(c >= '0' && c <= '9') || c == '_';
|
|
47
|
+
if (!is_word) xt_re_set_bit(bits, (unsigned char)c);
|
|
48
|
+
}
|
|
49
|
+
return 1;
|
|
50
|
+
case 's':
|
|
51
|
+
xt_re_set_bit(bits, ' ');
|
|
52
|
+
xt_re_set_bit(bits, '\t');
|
|
53
|
+
xt_re_set_bit(bits, '\n');
|
|
54
|
+
xt_re_set_bit(bits, '\r');
|
|
55
|
+
xt_re_set_bit(bits, '\f');
|
|
56
|
+
xt_re_set_bit(bits, '\v');
|
|
57
|
+
return 1;
|
|
58
|
+
case 'S':
|
|
59
|
+
for (c = 0; c < 256; c++) {
|
|
60
|
+
int is_space = c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
|
|
61
|
+
c == '\f' || c == '\v';
|
|
62
|
+
if (!is_space) xt_re_set_bit(bits, (unsigned char)c);
|
|
63
|
+
}
|
|
64
|
+
return 1;
|
|
65
|
+
default:
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/* -- matching ------------------------------------------------------------- */
|
|
2
|
+
|
|
3
|
+
typedef struct {
|
|
4
|
+
const char *text;
|
|
5
|
+
size_t len;
|
|
6
|
+
int icase;
|
|
7
|
+
} xt_re_input;
|
|
8
|
+
|
|
9
|
+
/* One capture on the immutable chain of the current search path. `parent`
|
|
10
|
+
* points to the captures that were live when the group was entered, so an
|
|
11
|
+
* abandoned backtrack simply drops the frame instead of restoring it. */
|
|
12
|
+
typedef struct xt_re_cap {
|
|
13
|
+
const struct xt_re_cap *parent;
|
|
14
|
+
int index; /* 1-based capture group number */
|
|
15
|
+
int start;
|
|
16
|
+
int end;
|
|
17
|
+
} xt_re_cap;
|
|
18
|
+
|
|
19
|
+
typedef int (*xt_re_cont)(void *ctx, size_t pos, const xt_re_cap *caps);
|
|
20
|
+
|
|
21
|
+
static void xt_re_free_alt(xt_re_alt *alt);
|
|
22
|
+
|
|
23
|
+
static void xt_re_free_node(xt_re_node *n) {
|
|
24
|
+
if (!n) return;
|
|
25
|
+
if (n->type == XT_RE_GROUP) xt_re_free_alt(n->group);
|
|
26
|
+
free(n->classbits);
|
|
27
|
+
free(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static void xt_re_free_alt(xt_re_alt *alt) {
|
|
31
|
+
int i;
|
|
32
|
+
if (!alt) return;
|
|
33
|
+
for (i = 0; i < alt->count; i++) {
|
|
34
|
+
int j;
|
|
35
|
+
for (j = 0; j < alt->branches[i].count; j++)
|
|
36
|
+
xt_re_free_node(alt->branches[i].pieces[j].node);
|
|
37
|
+
free(alt->branches[i].pieces);
|
|
38
|
+
}
|
|
39
|
+
free(alt->branches);
|
|
40
|
+
free(alt);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static int xt_re_char_eq(const xt_re_input *in, unsigned char a, unsigned char b) {
|
|
44
|
+
if (a == b) return 1;
|
|
45
|
+
if (in->icase && xt_re_lower(a) == xt_re_lower(b)) return 1;
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static int xt_re_class_match(const unsigned char *bits, unsigned char c, int icase) {
|
|
50
|
+
if (xt_re_bit_test(bits, c)) return 1;
|
|
51
|
+
if (icase) {
|
|
52
|
+
unsigned char l = xt_re_lower(c);
|
|
53
|
+
if (xt_re_bit_test(bits, l)) return 1;
|
|
54
|
+
if (l >= 'a' && l <= 'z' && xt_re_bit_test(bits, (unsigned char)(l - 'a' + 'A'))) return 1;
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
|
|
60
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps);
|
|
61
|
+
|
|
62
|
+
/* Resumes the outer continuation once a group's body has matched, recording
|
|
63
|
+
* where the group ended on the frame it pushed. */
|
|
64
|
+
typedef struct {
|
|
65
|
+
xt_re_cap *frame;
|
|
66
|
+
xt_re_cont cont;
|
|
67
|
+
void *ctx;
|
|
68
|
+
} xt_re_group_state;
|
|
69
|
+
|
|
70
|
+
static int xt_re_group_next(void *v, size_t pos, const xt_re_cap *caps) {
|
|
71
|
+
xt_re_group_state *s = (xt_re_group_state *)v;
|
|
72
|
+
/* `caps` already heads the chain that includes this group's own frame (and
|
|
73
|
+
* any groups nested inside it), so forward it as-is. */
|
|
74
|
+
s->frame->end = (int)pos;
|
|
75
|
+
return s->cont(s->ctx, pos, caps);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static int xt_re_match_atom(xt_re_node *node, const xt_re_input *in, size_t pos,
|
|
79
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps) {
|
|
80
|
+
switch (node->type) {
|
|
81
|
+
case XT_RE_CHAR:
|
|
82
|
+
if (pos < in->len && xt_re_char_eq(in, (unsigned char)in->text[pos], node->ch))
|
|
83
|
+
return cont(ctx, pos + 1, caps);
|
|
84
|
+
return 0;
|
|
85
|
+
case XT_RE_ANY:
|
|
86
|
+
if (pos < in->len && in->text[pos] != '\n') return cont(ctx, pos + 1, caps);
|
|
87
|
+
return 0;
|
|
88
|
+
case XT_RE_CLASS:
|
|
89
|
+
if (pos < in->len && xt_re_class_match(node->classbits, (unsigned char)in->text[pos], in->icase))
|
|
90
|
+
return cont(ctx, pos + 1, caps);
|
|
91
|
+
return 0;
|
|
92
|
+
case XT_RE_BOL:
|
|
93
|
+
return pos == 0 ? cont(ctx, pos, caps) : 0;
|
|
94
|
+
case XT_RE_EOL:
|
|
95
|
+
return pos == in->len ? cont(ctx, pos, caps) : 0;
|
|
96
|
+
case XT_RE_GROUP: {
|
|
97
|
+
xt_re_cap frame;
|
|
98
|
+
xt_re_group_state state;
|
|
99
|
+
frame.parent = caps;
|
|
100
|
+
frame.index = node->groupIndex;
|
|
101
|
+
frame.start = (int)pos;
|
|
102
|
+
frame.end = -1;
|
|
103
|
+
state.frame = &frame;
|
|
104
|
+
state.cont = cont;
|
|
105
|
+
state.ctx = ctx;
|
|
106
|
+
return xt_re_match_alt(node->group, in, pos, xt_re_group_next, &state, &frame);
|
|
107
|
+
}
|
|
108
|
+
default:
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
typedef struct {
|
|
114
|
+
xt_re_node *atom;
|
|
115
|
+
const xt_re_input *in;
|
|
116
|
+
int min;
|
|
117
|
+
int max; /* -1 == unbounded */
|
|
118
|
+
xt_re_cont cont;
|
|
119
|
+
void *ctx;
|
|
120
|
+
} xt_re_repeat;
|
|
121
|
+
|
|
122
|
+
typedef struct {
|
|
123
|
+
xt_re_repeat *rep;
|
|
124
|
+
size_t pos;
|
|
125
|
+
int count;
|
|
126
|
+
} xt_re_repeat_step;
|
|
127
|
+
|
|
128
|
+
static int xt_re_repeat_try(xt_re_repeat *rep, size_t pos, int count, const xt_re_cap *caps);
|
|
129
|
+
|
|
130
|
+
static int xt_re_repeat_next(void *v, size_t newpos, const xt_re_cap *caps) {
|
|
131
|
+
xt_re_repeat_step *s = (xt_re_repeat_step *)v;
|
|
132
|
+
if (newpos == s->pos) return 0; /* no progress -> stop to avoid loops */
|
|
133
|
+
return xt_re_repeat_try(s->rep, newpos, s->count + 1, caps);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static int xt_re_repeat_try(xt_re_repeat *rep, size_t pos, int count, const xt_re_cap *caps) {
|
|
137
|
+
if (rep->max < 0 || count < rep->max) {
|
|
138
|
+
xt_re_repeat_step step;
|
|
139
|
+
step.rep = rep;
|
|
140
|
+
step.pos = pos;
|
|
141
|
+
step.count = count;
|
|
142
|
+
if (xt_re_match_atom(rep->atom, rep->in, pos, xt_re_repeat_next, &step, caps)) return 1;
|
|
143
|
+
}
|
|
144
|
+
if (count >= rep->min) return rep->cont(rep->ctx, pos, caps);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static int xt_re_match_piece(xt_re_piece *piece, const xt_re_input *in, size_t pos,
|
|
149
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps) {
|
|
150
|
+
xt_re_repeat rep;
|
|
151
|
+
switch (piece->quant) {
|
|
152
|
+
case XT_RE_Q_STAR:
|
|
153
|
+
rep.atom = piece->node;
|
|
154
|
+
rep.in = in;
|
|
155
|
+
rep.min = 0;
|
|
156
|
+
rep.max = -1;
|
|
157
|
+
rep.cont = cont;
|
|
158
|
+
rep.ctx = ctx;
|
|
159
|
+
return xt_re_repeat_try(&rep, pos, 0, caps);
|
|
160
|
+
case XT_RE_Q_PLUS:
|
|
161
|
+
rep.atom = piece->node;
|
|
162
|
+
rep.in = in;
|
|
163
|
+
rep.min = 1;
|
|
164
|
+
rep.max = -1;
|
|
165
|
+
rep.cont = cont;
|
|
166
|
+
rep.ctx = ctx;
|
|
167
|
+
return xt_re_repeat_try(&rep, pos, 0, caps);
|
|
168
|
+
case XT_RE_Q_QUEST:
|
|
169
|
+
rep.atom = piece->node;
|
|
170
|
+
rep.in = in;
|
|
171
|
+
rep.min = 0;
|
|
172
|
+
rep.max = 1;
|
|
173
|
+
rep.cont = cont;
|
|
174
|
+
rep.ctx = ctx;
|
|
175
|
+
return xt_re_repeat_try(&rep, pos, 0, caps);
|
|
176
|
+
default:
|
|
177
|
+
return xt_re_match_atom(piece->node, in, pos, cont, ctx, caps);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
typedef struct {
|
|
182
|
+
xt_re_concat *concat;
|
|
183
|
+
const xt_re_input *in;
|
|
184
|
+
xt_re_cont cont;
|
|
185
|
+
void *ctx;
|
|
186
|
+
} xt_re_concat_state;
|
|
187
|
+
|
|
188
|
+
typedef struct {
|
|
189
|
+
xt_re_concat_state *state;
|
|
190
|
+
int index;
|
|
191
|
+
} xt_re_concat_step;
|
|
192
|
+
|
|
193
|
+
static int xt_re_match_concat(xt_re_concat_state *state, int index, size_t pos, const xt_re_cap *caps);
|
|
194
|
+
|
|
195
|
+
/* The continuation for a concat piece resumes matching at the next piece. */
|
|
196
|
+
static int xt_re_concat_next(void *v, size_t pos, const xt_re_cap *caps) {
|
|
197
|
+
xt_re_concat_step *s = (xt_re_concat_step *)v;
|
|
198
|
+
return xt_re_match_concat(s->state, s->index + 1, pos, caps);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static int xt_re_match_concat(xt_re_concat_state *state, int index, size_t pos, const xt_re_cap *caps) {
|
|
202
|
+
xt_re_concat_step step;
|
|
203
|
+
if (index >= state->concat->count) return state->cont(state->ctx, pos, caps);
|
|
204
|
+
step.state = state;
|
|
205
|
+
step.index = index;
|
|
206
|
+
return xt_re_match_piece(&state->concat->pieces[index], state->in, pos,
|
|
207
|
+
xt_re_concat_next, &step, caps);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static int xt_re_match_alt(xt_re_alt *alt, const xt_re_input *in, size_t pos,
|
|
211
|
+
xt_re_cont cont, void *ctx, const xt_re_cap *caps) {
|
|
212
|
+
int i;
|
|
213
|
+
for (i = 0; i < alt->count; i++) {
|
|
214
|
+
xt_re_concat_state state;
|
|
215
|
+
state.concat = &alt->branches[i];
|
|
216
|
+
state.in = in;
|
|
217
|
+
state.cont = cont;
|
|
218
|
+
state.ctx = ctx;
|
|
219
|
+
if (xt_re_match_concat(&state, 0, pos, caps)) return 1;
|
|
220
|
+
}
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
typedef struct {
|
|
225
|
+
size_t end;
|
|
226
|
+
regmatch_t *pmatch;
|
|
227
|
+
size_t nmatch;
|
|
228
|
+
} xt_re_match_result;
|
|
229
|
+
|
|
230
|
+
/* Final continuation: records the whole-match end offset and copies every
|
|
231
|
+
* capture on the successful path into `pmatch`. Walking from the head means
|
|
232
|
+
* the most recent frame wins, which is what a quantified group such as
|
|
233
|
+
* `(a)+` should report (its last iteration). */
|
|
234
|
+
static int xt_re_record_match(void *ctx, size_t pos, const xt_re_cap *caps) {
|
|
235
|
+
xt_re_match_result *result = (xt_re_match_result *)ctx;
|
|
236
|
+
const xt_re_cap *cap;
|
|
237
|
+
result->end = pos;
|
|
238
|
+
for (cap = caps; cap; cap = cap->parent) {
|
|
239
|
+
if (cap->index >= 1 && (size_t)cap->index < result->nmatch) {
|
|
240
|
+
regmatch_t *slot = &result->pmatch[cap->index];
|
|
241
|
+
if (slot->rm_so < 0) {
|
|
242
|
+
slot->rm_so = cap->start;
|
|
243
|
+
slot->rm_eo = cap->end;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return 1;
|
|
248
|
+
}
|