xbintsc 0.1.6 → 0.1.9
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 +7 -4
- package/dist/src/ast/common.d.ts +33 -0
- package/dist/src/ast/common.js +7 -0
- package/dist/src/ast/common.js.map +1 -0
- package/dist/src/ast/declarations.d.ts +110 -0
- package/dist/src/ast/declarations.js +5 -0
- package/dist/src/ast/declarations.js.map +1 -0
- package/dist/src/ast/expressions.d.ts +219 -0
- package/dist/src/ast/expressions.js +6 -0
- package/dist/src/ast/expressions.js.map +1 -0
- package/dist/src/ast/kinds.d.ts +160 -0
- package/dist/src/ast/kinds.js +171 -0
- package/dist/src/ast/kinds.js.map +1 -0
- package/dist/src/ast/modules.d.ts +61 -0
- package/dist/src/ast/modules.js +5 -0
- package/dist/src/ast/modules.js.map +1 -0
- package/dist/src/ast/nodes.d.ts +11 -839
- package/dist/src/ast/nodes.js +11 -237
- package/dist/src/ast/nodes.js.map +1 -1
- package/dist/src/ast/operators.d.ts +71 -0
- package/dist/src/ast/operators.js +77 -0
- package/dist/src/ast/operators.js.map +1 -0
- package/dist/src/ast/statements.d.ts +120 -0
- package/dist/src/ast/statements.js +5 -0
- package/dist/src/ast/statements.js.map +1 -0
- package/dist/src/ast/types.d.ts +123 -0
- package/dist/src/ast/types.js +6 -0
- package/dist/src/ast/types.js.map +1 -0
- package/dist/src/binder/binder.d.ts +35 -1
- package/dist/src/binder/binder.js +138 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/codegen/generator/calls.d.ts +26 -0
- package/dist/src/codegen/generator/calls.js +373 -0
- package/dist/src/codegen/generator/calls.js.map +1 -0
- package/dist/src/codegen/generator/context.d.ts +47 -0
- package/dist/src/codegen/generator/context.js +164 -0
- package/dist/src/codegen/generator/context.js.map +1 -0
- package/dist/src/codegen/generator/expressions.d.ts +25 -0
- package/dist/src/codegen/generator/expressions.js +454 -0
- package/dist/src/codegen/generator/expressions.js.map +1 -0
- package/dist/src/codegen/generator/generator.d.ts +22 -0
- package/dist/src/codegen/generator/generator.js +21 -0
- package/dist/src/codegen/generator/generator.js.map +1 -0
- package/dist/src/codegen/generator/module.d.ts +19 -0
- package/dist/src/codegen/generator/module.js +214 -0
- package/dist/src/codegen/generator/module.js.map +1 -0
- package/dist/src/codegen/generator/state.d.ts +42 -0
- package/dist/src/codegen/generator/state.js +125 -0
- package/dist/src/codegen/generator/state.js.map +1 -0
- package/dist/src/codegen/generator/statements.d.ts +22 -0
- package/dist/src/codegen/generator/statements.js +433 -0
- package/dist/src/codegen/generator/statements.js.map +1 -0
- package/dist/src/codegen/generator/tables.d.ts +23 -0
- package/dist/src/codegen/generator/tables.js +186 -0
- package/dist/src/codegen/generator/tables.js.map +1 -0
- package/dist/src/codegen/llvm.d.ts +4 -13
- package/dist/src/codegen/llvm.js +4 -1537
- package/dist/src/codegen/llvm.js.map +1 -1
- package/dist/src/driver/compiler.js +36 -11
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +24 -0
- package/dist/src/driver/modules.js +347 -0
- package/dist/src/driver/modules.js.map +1 -0
- package/dist/src/lexer/char.d.ts +78 -0
- package/dist/src/lexer/char.js +106 -0
- package/dist/src/lexer/char.js.map +1 -0
- package/dist/src/lexer/scanner.js +1 -98
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/context.d.ts +58 -0
- package/dist/src/parser/context.js +174 -0
- package/dist/src/parser/context.js.map +1 -0
- package/dist/src/parser/declarations.d.ts +26 -0
- package/dist/src/parser/declarations.js +461 -0
- package/dist/src/parser/declarations.js.map +1 -0
- package/dist/src/parser/expressions.d.ts +27 -0
- package/dist/src/parser/expressions.js +440 -0
- package/dist/src/parser/expressions.js.map +1 -0
- package/dist/src/parser/helpers.d.ts +7 -0
- package/dist/src/parser/helpers.js +36 -0
- package/dist/src/parser/helpers.js.map +1 -0
- package/dist/src/parser/literals.d.ts +16 -0
- package/dist/src/parser/literals.js +193 -0
- package/dist/src/parser/literals.js.map +1 -0
- package/dist/src/parser/modules.d.ts +12 -0
- package/dist/src/parser/modules.js +175 -0
- package/dist/src/parser/modules.js.map +1 -0
- package/dist/src/parser/operators.d.ts +12 -0
- package/dist/src/parser/operators.js +172 -0
- package/dist/src/parser/operators.js.map +1 -0
- package/dist/src/parser/parser.d.ts +17 -122
- package/dist/src/parser/parser.js +18 -2311
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/speculation.d.ts +6 -0
- package/dist/src/parser/speculation.js +10 -0
- package/dist/src/parser/speculation.js.map +1 -0
- package/dist/src/parser/statements.d.ts +28 -0
- package/dist/src/parser/statements.js +402 -0
- package/dist/src/parser/statements.js.map +1 -0
- package/dist/src/parser/types.d.ts +21 -0
- package/dist/src/parser/types.js +338 -0
- package/dist/src/parser/types.js.map +1 -0
- package/dist/tests/codegen/llvm.test.js +1 -1
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +86 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/rt.h +55 -4
- package/runtime/rt_internal.h +169 -0
- package/runtime/xt_alloc.c +38 -0
- package/runtime/xt_builtins.c +238 -0
- package/runtime/xt_containers.c +312 -0
- package/runtime/xt_io.c +246 -0
- package/runtime/xt_promise.c +292 -0
- package/runtime/xt_stdlib.c +556 -0
- package/runtime/xt_stdlib2.c +1043 -0
- package/runtime/xt_values.c +291 -0
- package/scripts/build-runtime.mjs +8 -1
- package/src/ast/common.ts +210 -0
- package/src/ast/declarations.ts +130 -0
- package/src/ast/expressions.ts +275 -0
- package/src/ast/kinds.ts +176 -0
- package/src/ast/modules.ts +73 -0
- package/src/ast/nodes.ts +11 -1101
- package/src/ast/operators.ts +76 -0
- package/src/ast/statements.ts +148 -0
- package/src/ast/types.ts +173 -0
- package/src/binder/binder.ts +196 -0
- package/src/codegen/generator/calls.ts +433 -0
- package/src/codegen/generator/context.ts +190 -0
- package/src/codegen/generator/expressions.ts +508 -0
- package/src/codegen/generator/generator.ts +35 -0
- package/src/codegen/generator/module.ts +244 -0
- package/src/codegen/generator/state.ts +169 -0
- package/src/codegen/generator/statements.ts +480 -0
- package/src/codegen/generator/tables.ts +206 -0
- package/src/codegen/llvm.ts +4 -1720
- package/src/driver/compiler.ts +45 -12
- package/src/driver/modules.ts +377 -0
- package/src/lexer/char.ts +116 -0
- package/src/lexer/scanner.ts +9 -109
- package/src/parser/context.ts +196 -0
- package/src/parser/declarations.ts +502 -0
- package/src/parser/expressions.ts +468 -0
- package/src/parser/helpers.ts +42 -0
- package/src/parser/literals.ts +207 -0
- package/src/parser/modules.ts +194 -0
- package/src/parser/operators.ts +181 -0
- package/src/parser/parser.ts +35 -2373
- package/src/parser/speculation.ts +9 -0
- package/src/parser/statements.ts +457 -0
- package/src/parser/types.ts +365 -0
- package/runtime/xt_runtime.c +0 -1372
package/runtime/xt_runtime.c
DELETED
|
@@ -1,1372 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* xbintsc runtime implementation.
|
|
3
|
-
*
|
|
4
|
-
* Design notes
|
|
5
|
-
* ------------
|
|
6
|
-
* * Values are 64-bit NaN-boxed words (`xt_value`). Numbers travel unboxed;
|
|
7
|
-
* everything else is a tagged pointer into and allocation arena.
|
|
8
|
-
* * The allocator is a bump arena. It never frees, which keeps the generated
|
|
9
|
-
* code free of write barriers and makes the first implementation small and
|
|
10
|
-
* predictable. Replacing it with a precise/conservative collector is an
|
|
11
|
-
* isolated change behind `xt_alloc` plus root registration.
|
|
12
|
-
* * Strings are UTF-8. JavaScript measures string length in UTF-16 code
|
|
13
|
-
* units; `xt_string_length` currently reports code points (see README).
|
|
14
|
-
* * Objects use a small linear property list. Fine for the language subset
|
|
15
|
-
* xbintsc targets today; swap for a hash map when the profile demands it.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
#include "rt.h"
|
|
19
|
-
|
|
20
|
-
#include <ctype.h>
|
|
21
|
-
#include <math.h>
|
|
22
|
-
#include <setjmp.h>
|
|
23
|
-
#include <stdio.h>
|
|
24
|
-
#include <stdlib.h>
|
|
25
|
-
#include <string.h>
|
|
26
|
-
#include <stdarg.h>
|
|
27
|
-
|
|
28
|
-
/* ------------------------------------------------------------------------- */
|
|
29
|
-
/* Allocation */
|
|
30
|
-
/* ------------------------------------------------------------------------- */
|
|
31
|
-
|
|
32
|
-
#define XT_OBJECT_KIND_STRING 1
|
|
33
|
-
#define XT_OBJECT_KIND_OBJECT 2
|
|
34
|
-
#define XT_OBJECT_KIND_ARRAY 3
|
|
35
|
-
#define XT_OBJECT_KIND_FUNCTION 4
|
|
36
|
-
|
|
37
|
-
typedef struct xt_header {
|
|
38
|
-
uint8_t kind;
|
|
39
|
-
uint8_t flags;
|
|
40
|
-
uint16_t reserved;
|
|
41
|
-
uint32_t size;
|
|
42
|
-
struct xt_header *gc_next;
|
|
43
|
-
} xt_header;
|
|
44
|
-
|
|
45
|
-
typedef struct {
|
|
46
|
-
xt_header header;
|
|
47
|
-
uint32_t length;
|
|
48
|
-
uint32_t capacity;
|
|
49
|
-
char *data;
|
|
50
|
-
} xt_string;
|
|
51
|
-
|
|
52
|
-
typedef struct {
|
|
53
|
-
xt_string *key;
|
|
54
|
-
xt_value value;
|
|
55
|
-
} xt_property;
|
|
56
|
-
|
|
57
|
-
typedef struct {
|
|
58
|
-
xt_header header;
|
|
59
|
-
uint32_t count;
|
|
60
|
-
uint32_t capacity;
|
|
61
|
-
xt_property *properties;
|
|
62
|
-
} xt_object;
|
|
63
|
-
|
|
64
|
-
typedef struct {
|
|
65
|
-
xt_header header;
|
|
66
|
-
uint32_t length;
|
|
67
|
-
uint32_t capacity;
|
|
68
|
-
xt_value *items;
|
|
69
|
-
} xt_array;
|
|
70
|
-
|
|
71
|
-
typedef struct {
|
|
72
|
-
xt_header header;
|
|
73
|
-
xt_code_fn code;
|
|
74
|
-
xt_value *environment;
|
|
75
|
-
int32_t environment_count;
|
|
76
|
-
int32_t arity;
|
|
77
|
-
xt_string *name;
|
|
78
|
-
xt_object *properties;
|
|
79
|
-
} xt_function;
|
|
80
|
-
|
|
81
|
-
static size_t g_heap_bytes = 0;
|
|
82
|
-
static size_t g_heap_allocations = 0;
|
|
83
|
-
static xt_header *g_heap_head = NULL;
|
|
84
|
-
|
|
85
|
-
static void *xt_alloc(size_t size, int kind) {
|
|
86
|
-
xt_header *header = (xt_header *)calloc(1, size);
|
|
87
|
-
if (!header) {
|
|
88
|
-
fprintf(stderr, "xbintsc: out of memory allocating %zu bytes\n", size);
|
|
89
|
-
abort();
|
|
90
|
-
}
|
|
91
|
-
header->kind = (uint8_t)kind;
|
|
92
|
-
header->size = (uint32_t)size;
|
|
93
|
-
header->gc_next = g_heap_head;
|
|
94
|
-
g_heap_head = header;
|
|
95
|
-
g_heap_bytes += size;
|
|
96
|
-
g_heap_allocations++;
|
|
97
|
-
return header;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
size_t xt_heap_bytes(void) { return g_heap_bytes; }
|
|
101
|
-
size_t xt_heap_allocations(void) { return g_heap_allocations; }
|
|
102
|
-
|
|
103
|
-
/* ------------------------------------------------------------------------- */
|
|
104
|
-
/* Constructors */
|
|
105
|
-
/* ------------------------------------------------------------------------- */
|
|
106
|
-
|
|
107
|
-
xt_value xt_undefined(void) { return XT_UNDEFINED; }
|
|
108
|
-
xt_value xt_null(void) { return XT_NULL; }
|
|
109
|
-
xt_value xt_bool(int b) { return b ? XT_TRUE : XT_FALSE; }
|
|
110
|
-
xt_value xt_number(double d) { return xt_from_double(d); }
|
|
111
|
-
|
|
112
|
-
static xt_string *xt_string_alloc(size_t length) {
|
|
113
|
-
xt_string *s = (xt_string *)xt_alloc(sizeof(xt_string), XT_OBJECT_KIND_STRING);
|
|
114
|
-
s->length = (uint32_t)length;
|
|
115
|
-
s->capacity = (uint32_t)length + 1;
|
|
116
|
-
s->data = (char *)malloc(s->capacity);
|
|
117
|
-
if (!s->data) {
|
|
118
|
-
fprintf(stderr, "xbintsc: out of memory allocating string\n");
|
|
119
|
-
abort();
|
|
120
|
-
}
|
|
121
|
-
s->data[length] = '\0';
|
|
122
|
-
return s;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
xt_value xt_string_new(const char *data, size_t len) {
|
|
126
|
-
xt_string *s = xt_string_alloc(len);
|
|
127
|
-
if (len > 0) memcpy(s->data, data, len);
|
|
128
|
-
return XT_FROM_PTR(XT_TAG_STRING, s);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
xt_value xt_string_from_cstr(const char *data) { return xt_string_new(data, strlen(data)); }
|
|
132
|
-
|
|
133
|
-
static xt_string *xt_as_string(xt_value v);
|
|
134
|
-
|
|
135
|
-
const char *xt_string_data(xt_value value) {
|
|
136
|
-
xt_string *s = xt_as_string(value);
|
|
137
|
-
return s ? s->data : NULL;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
int32_t xt_string_length_value(xt_value value) {
|
|
141
|
-
xt_string *s = xt_as_string(value);
|
|
142
|
-
return s ? (int32_t)s->length : -1;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/* ------------------------------------------------------------------------- */
|
|
146
|
-
/* Strings */
|
|
147
|
-
/* ------------------------------------------------------------------------- */
|
|
148
|
-
|
|
149
|
-
static xt_string *xt_as_string(xt_value v) {
|
|
150
|
-
if (XT_IS_STRING(v)) return (xt_string *)XT_GET_PTR(v);
|
|
151
|
-
return NULL;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
static int32_t xt_string_length(xt_string *s) { return (int32_t)s->length; }
|
|
155
|
-
|
|
156
|
-
static xt_value xt_string_concat(xt_string *a, xt_string *b) {
|
|
157
|
-
size_t total = (size_t)a->length + (size_t)b->length;
|
|
158
|
-
xt_string *out = xt_string_alloc(total);
|
|
159
|
-
memcpy(out->data, a->data, a->length);
|
|
160
|
-
memcpy(out->data + a->length, b->data, b->length);
|
|
161
|
-
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
static int xt_string_equals(xt_string *a, xt_string *b) {
|
|
165
|
-
return a->length == b->length && memcmp(a->data, b->data, a->length) == 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/*
|
|
169
|
-
* Format a double the way JavaScript's ToString does for the common cases:
|
|
170
|
-
* integers print without a decimal point, everything else uses the shortest
|
|
171
|
-
* representation that round-trips.
|
|
172
|
-
*/
|
|
173
|
-
static void xt_format_double(double d, char *buffer, size_t size) {
|
|
174
|
-
if (isnan(d)) {
|
|
175
|
-
snprintf(buffer, size, "NaN");
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (isinf(d)) {
|
|
179
|
-
snprintf(buffer, size, d < 0 ? "-Infinity" : "Infinity");
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (d == 0.0) {
|
|
183
|
-
snprintf(buffer, size, "0");
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (d == floor(d) && fabs(d) < 1e21) {
|
|
187
|
-
snprintf(buffer, size, "%.0f", d);
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
for (int precision = 1; precision <= 17; precision++) {
|
|
191
|
-
snprintf(buffer, size, "%.*g", precision, d);
|
|
192
|
-
if (strtod(buffer, NULL) == d) return;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/* ------------------------------------------------------------------------- */
|
|
197
|
-
/* Conversions */
|
|
198
|
-
/* ------------------------------------------------------------------------- */
|
|
199
|
-
|
|
200
|
-
int xt_truthy(xt_value v) {
|
|
201
|
-
if (XT_IS_NUMBER(v)) {
|
|
202
|
-
double d = xt_to_double(v);
|
|
203
|
-
return d != 0.0 && !isnan(d);
|
|
204
|
-
}
|
|
205
|
-
if (v == XT_UNDEFINED || v == XT_NULL) return 0;
|
|
206
|
-
if (v == XT_FALSE) return 0;
|
|
207
|
-
if (v == XT_TRUE) return 1;
|
|
208
|
-
return 1; /* objects, arrays, functions and non-empty strings are truthy */
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
double xt_to_number(xt_value v) {
|
|
212
|
-
if (XT_IS_NUMBER(v)) return xt_to_double(v);
|
|
213
|
-
if (v == XT_TRUE) return 1.0;
|
|
214
|
-
if (v == XT_FALSE) return 0.0;
|
|
215
|
-
if (v == XT_UNDEFINED) return NAN;
|
|
216
|
-
if (v == XT_NULL) return 0.0;
|
|
217
|
-
if (XT_IS_STRING(v)) {
|
|
218
|
-
xt_string *s = xt_as_string(v);
|
|
219
|
-
if (s->length == 0) return 0.0;
|
|
220
|
-
char *end = NULL;
|
|
221
|
-
double parsed = strtod(s->data, &end);
|
|
222
|
-
if (end != s->data + s->length) return NAN;
|
|
223
|
-
return parsed;
|
|
224
|
-
}
|
|
225
|
-
return NAN;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
static xt_value xt_number_to_string_value(double d) {
|
|
229
|
-
char buffer[64];
|
|
230
|
-
xt_format_double(d, buffer, sizeof(buffer));
|
|
231
|
-
return xt_string_from_cstr(buffer);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
xt_value xt_to_string(xt_value v) {
|
|
235
|
-
if (XT_IS_STRING(v)) return v;
|
|
236
|
-
if (XT_IS_NUMBER(v)) return xt_number_to_string_value(xt_to_double(v));
|
|
237
|
-
if (v == XT_UNDEFINED) return xt_string_from_cstr("undefined");
|
|
238
|
-
if (v == XT_NULL) return xt_string_from_cstr("null");
|
|
239
|
-
if (v == XT_TRUE) return xt_string_from_cstr("true");
|
|
240
|
-
if (v == XT_FALSE) return xt_string_from_cstr("false");
|
|
241
|
-
if (XT_IS_ARRAY(v)) {
|
|
242
|
-
xt_array *a = (xt_array *)XT_GET_PTR(v);
|
|
243
|
-
/* Join elements with commas, like Array.prototype.toString. */
|
|
244
|
-
size_t capacity = 32;
|
|
245
|
-
char *out = (char *)malloc(capacity);
|
|
246
|
-
size_t length = 0;
|
|
247
|
-
out[0] = '\0';
|
|
248
|
-
for (uint32_t i = 0; i < a->length; i++) {
|
|
249
|
-
xt_value s = xt_to_string(a->items[i]);
|
|
250
|
-
xt_string *str = xt_as_string(s);
|
|
251
|
-
if (length + str->length + 2 > capacity) {
|
|
252
|
-
capacity = (length + str->length + 2) * 2;
|
|
253
|
-
out = (char *)realloc(out, capacity);
|
|
254
|
-
}
|
|
255
|
-
if (i > 0) out[length++] = ',';
|
|
256
|
-
memcpy(out + length, str->data, str->length);
|
|
257
|
-
length += str->length;
|
|
258
|
-
out[length] = '\0';
|
|
259
|
-
}
|
|
260
|
-
xt_value result = xt_string_new(out, length);
|
|
261
|
-
free(out);
|
|
262
|
-
return result;
|
|
263
|
-
}
|
|
264
|
-
if (XT_IS_FUNCTION(v)) return xt_string_from_cstr("function () { [native code] }");
|
|
265
|
-
return xt_string_from_cstr("[object Object]");
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
xt_value xt_typeof(xt_value v) {
|
|
269
|
-
if (XT_IS_NUMBER(v)) return xt_string_from_cstr("number");
|
|
270
|
-
if (XT_IS_STRING(v)) return xt_string_from_cstr("string");
|
|
271
|
-
if (v == XT_TRUE || v == XT_FALSE) return xt_string_from_cstr("boolean");
|
|
272
|
-
if (v == XT_UNDEFINED) return xt_string_from_cstr("undefined");
|
|
273
|
-
if (XT_IS_FUNCTION(v)) return xt_string_from_cstr("function");
|
|
274
|
-
if (v == XT_NULL) return xt_string_from_cstr("object");
|
|
275
|
-
return xt_string_from_cstr("object");
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/* ------------------------------------------------------------------------- */
|
|
279
|
-
/* Arithmetic */
|
|
280
|
-
/* ------------------------------------------------------------------------- */
|
|
281
|
-
|
|
282
|
-
xt_value xt_add(xt_value a, xt_value b) {
|
|
283
|
-
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) {
|
|
284
|
-
return xt_number(xt_to_double(a) + xt_to_double(b));
|
|
285
|
-
}
|
|
286
|
-
/* JavaScript's `+` concatenates when either operand is a string. */
|
|
287
|
-
if (XT_IS_STRING(a) || XT_IS_STRING(b)) {
|
|
288
|
-
xt_value sa = xt_to_string(a);
|
|
289
|
-
xt_value sb = xt_to_string(b);
|
|
290
|
-
return xt_string_concat(xt_as_string(sa), xt_as_string(sb));
|
|
291
|
-
}
|
|
292
|
-
return xt_number(xt_to_number(a) + xt_to_number(b));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
xt_value xt_sub(xt_value a, xt_value b) { return xt_number(xt_to_number(a) - xt_to_number(b)); }
|
|
296
|
-
xt_value xt_mul(xt_value a, xt_value b) { return xt_number(xt_to_number(a) * xt_to_number(b)); }
|
|
297
|
-
xt_value xt_div(xt_value a, xt_value b) { return xt_number(xt_to_number(a) / xt_to_number(b)); }
|
|
298
|
-
|
|
299
|
-
xt_value xt_mod(xt_value a, xt_value b) {
|
|
300
|
-
double x = xt_to_number(a);
|
|
301
|
-
double y = xt_to_number(b);
|
|
302
|
-
return xt_number(fmod(x, y));
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
xt_value xt_pow(xt_value a, xt_value b) { return xt_number(pow(xt_to_number(a), xt_to_number(b))); }
|
|
306
|
-
xt_value xt_neg(xt_value a) { return xt_number(-xt_to_number(a)); }
|
|
307
|
-
xt_value xt_pos(xt_value a) { return xt_number(xt_to_number(a)); }
|
|
308
|
-
|
|
309
|
-
static int32_t xt_to_int32(xt_value v) {
|
|
310
|
-
double d = xt_to_number(v);
|
|
311
|
-
if (isnan(d) || isinf(d)) return 0;
|
|
312
|
-
return (int32_t)(uint32_t)(int64_t)d;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
xt_value xt_bit_and(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) & xt_to_int32(b))); }
|
|
316
|
-
xt_value xt_bit_or(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) | xt_to_int32(b))); }
|
|
317
|
-
xt_value xt_bit_xor(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) ^ xt_to_int32(b))); }
|
|
318
|
-
xt_value xt_bit_not(xt_value a) { return xt_number((double)(~xt_to_int32(a))); }
|
|
319
|
-
xt_value xt_shl(xt_value a, xt_value b) { return xt_number((double)(int32_t)((uint32_t)xt_to_int32(a) << (xt_to_int32(b) & 31))); }
|
|
320
|
-
xt_value xt_shr(xt_value a, xt_value b) { return xt_number((double)(xt_to_int32(a) >> (xt_to_int32(b) & 31))); }
|
|
321
|
-
xt_value xt_ushr(xt_value a, xt_value b) {
|
|
322
|
-
return xt_number((double)(uint32_t)((uint32_t)xt_to_int32(a) >> (xt_to_int32(b) & 31)));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/* ------------------------------------------------------------------------- */
|
|
326
|
-
/* Comparison */
|
|
327
|
-
/* ------------------------------------------------------------------------- */
|
|
328
|
-
|
|
329
|
-
xt_value xt_lt(xt_value a, xt_value b) {
|
|
330
|
-
if (XT_IS_STRING(a) && XT_IS_STRING(b)) {
|
|
331
|
-
xt_string *x = xt_as_string(a);
|
|
332
|
-
xt_string *y = xt_as_string(b);
|
|
333
|
-
size_t n = x->length < y->length ? x->length : y->length;
|
|
334
|
-
int cmp = memcmp(x->data, y->data, n);
|
|
335
|
-
if (cmp == 0) return xt_bool(x->length < y->length);
|
|
336
|
-
return xt_bool(cmp < 0);
|
|
337
|
-
}
|
|
338
|
-
return xt_bool(xt_to_number(a) < xt_to_number(b));
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
xt_value xt_le(xt_value a, xt_value b) {
|
|
342
|
-
if (XT_IS_STRING(a) && XT_IS_STRING(b)) {
|
|
343
|
-
xt_string *x = xt_as_string(a);
|
|
344
|
-
xt_string *y = xt_as_string(b);
|
|
345
|
-
size_t n = x->length < y->length ? x->length : y->length;
|
|
346
|
-
int cmp = memcmp(x->data, y->data, n);
|
|
347
|
-
if (cmp == 0) return xt_bool(x->length <= y->length);
|
|
348
|
-
return xt_bool(cmp < 0);
|
|
349
|
-
}
|
|
350
|
-
return xt_bool(xt_to_number(a) <= xt_to_number(b));
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
xt_value xt_gt(xt_value a, xt_value b) { return xt_lt(b, a); }
|
|
354
|
-
xt_value xt_ge(xt_value a, xt_value b) { return xt_le(b, a); }
|
|
355
|
-
|
|
356
|
-
static int xt_loose_equals(xt_value a, xt_value b) {
|
|
357
|
-
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) return xt_to_double(a) == xt_to_double(b);
|
|
358
|
-
if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_string_equals(xt_as_string(a), xt_as_string(b));
|
|
359
|
-
if (a == b) return 1;
|
|
360
|
-
if ((a == XT_NULL && b == XT_UNDEFINED) || (a == XT_UNDEFINED && b == XT_NULL)) return 1;
|
|
361
|
-
if (XT_IS_BOOL(a)) return xt_loose_equals(xt_number(xt_to_number(a)), b);
|
|
362
|
-
if (XT_IS_BOOL(b)) return xt_loose_equals(a, xt_number(xt_to_number(b)));
|
|
363
|
-
if (XT_IS_STRING(a) && XT_IS_NUMBER(b)) return xt_to_number(a) == xt_to_double(b);
|
|
364
|
-
if (XT_IS_NUMBER(a) && XT_IS_STRING(b)) return xt_to_double(a) == xt_to_number(b);
|
|
365
|
-
return 0;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
xt_value xt_eq(xt_value a, xt_value b) { return xt_bool(xt_loose_equals(a, b)); }
|
|
369
|
-
xt_value xt_ne(xt_value a, xt_value b) { return xt_bool(!xt_loose_equals(a, b)); }
|
|
370
|
-
|
|
371
|
-
xt_value xt_seq(xt_value a, xt_value b) {
|
|
372
|
-
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) return xt_bool(xt_to_double(a) == xt_to_double(b));
|
|
373
|
-
if (XT_IS_STRING(a) && XT_IS_STRING(b)) return xt_bool(xt_string_equals(xt_as_string(a), xt_as_string(b)));
|
|
374
|
-
return xt_bool(a == b);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
xt_value xt_sne(xt_value a, xt_value b) { return xt_bool(!xt_truthy(xt_seq(a, b))); }
|
|
378
|
-
xt_value xt_not(xt_value a) { return xt_bool(!xt_truthy(a)); }
|
|
379
|
-
|
|
380
|
-
/* ------------------------------------------------------------------------- */
|
|
381
|
-
/* Objects */
|
|
382
|
-
/* ------------------------------------------------------------------------- */
|
|
383
|
-
|
|
384
|
-
static void xt_object_reserve(xt_object *obj, uint32_t needed) {
|
|
385
|
-
if (needed <= obj->capacity) return;
|
|
386
|
-
uint32_t capacity = obj->capacity ? obj->capacity * 2 : 8;
|
|
387
|
-
while (capacity < needed) capacity *= 2;
|
|
388
|
-
obj->properties = (xt_property *)realloc(obj->properties, sizeof(xt_property) * capacity);
|
|
389
|
-
if (!obj->properties) {
|
|
390
|
-
fprintf(stderr, "xbintsc: out of memory growing object\n");
|
|
391
|
-
abort();
|
|
392
|
-
}
|
|
393
|
-
obj->capacity = capacity;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
xt_value xt_object_new(void) {
|
|
397
|
-
xt_object *obj = (xt_object *)xt_alloc(sizeof(xt_object), XT_OBJECT_KIND_OBJECT);
|
|
398
|
-
obj->count = 0;
|
|
399
|
-
obj->capacity = 0;
|
|
400
|
-
obj->properties = NULL;
|
|
401
|
-
return XT_FROM_PTR(XT_TAG_OBJECT, obj);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
|
|
405
|
-
for (uint32_t i = 0; i < obj->count; i++) {
|
|
406
|
-
if (xt_string_equals(obj->properties[i].key, key)) return &obj->properties[i];
|
|
407
|
-
}
|
|
408
|
-
return NULL;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
xt_value xt_object_get(xt_value value, xt_value key) {
|
|
412
|
-
if (!XT_IS_OBJECT(value)) return XT_UNDEFINED;
|
|
413
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
414
|
-
xt_value keyString = xt_to_string(key);
|
|
415
|
-
xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
|
|
416
|
-
return prop ? prop->value : XT_UNDEFINED;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
420
|
-
if (!XT_IS_OBJECT(value)) return newValue;
|
|
421
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
422
|
-
xt_value keyString = xt_to_string(key);
|
|
423
|
-
xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
|
|
424
|
-
if (prop) {
|
|
425
|
-
prop->value = newValue;
|
|
426
|
-
return newValue;
|
|
427
|
-
}
|
|
428
|
-
xt_object_reserve(obj, obj->count + 1);
|
|
429
|
-
obj->properties[obj->count].key = xt_as_string(keyString);
|
|
430
|
-
obj->properties[obj->count].value = newValue;
|
|
431
|
-
obj->count++;
|
|
432
|
-
return newValue;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
xt_value xt_object_has(xt_value value, xt_value key) {
|
|
436
|
-
if (!XT_IS_OBJECT(value)) return XT_FALSE;
|
|
437
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
438
|
-
xt_value keyString = xt_to_string(key);
|
|
439
|
-
return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
xt_value xt_object_keys(xt_value value) {
|
|
443
|
-
xt_value result = xt_array_new(0, NULL);
|
|
444
|
-
if (XT_IS_OBJECT(value)) {
|
|
445
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
446
|
-
for (uint32_t i = 0; i < obj->count; i++) {
|
|
447
|
-
xt_array_push(result, XT_FROM_PTR(XT_TAG_STRING, obj->properties[i].key));
|
|
448
|
-
}
|
|
449
|
-
return result;
|
|
450
|
-
}
|
|
451
|
-
if (XT_IS_ARRAY(value)) {
|
|
452
|
-
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
453
|
-
for (uint32_t i = 0; i < array->length; i++) {
|
|
454
|
-
char buffer[32];
|
|
455
|
-
snprintf(buffer, sizeof(buffer), "%u", i);
|
|
456
|
-
xt_array_push(result, xt_string_from_cstr(buffer));
|
|
457
|
-
}
|
|
458
|
-
return result;
|
|
459
|
-
}
|
|
460
|
-
if (XT_IS_STRING(value)) {
|
|
461
|
-
xt_string *s = xt_as_string(value);
|
|
462
|
-
for (uint32_t i = 0; i < s->length; i++) {
|
|
463
|
-
char buffer[32];
|
|
464
|
-
snprintf(buffer, sizeof(buffer), "%u", i);
|
|
465
|
-
xt_array_push(result, xt_string_from_cstr(buffer));
|
|
466
|
-
}
|
|
467
|
-
return result;
|
|
468
|
-
}
|
|
469
|
-
return result;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/* Convenience used by codegen for `obj.prop`. */
|
|
473
|
-
xt_value xt_object_get_cstr(xt_value value, const char *key) {
|
|
474
|
-
xt_value k = xt_string_from_cstr(key);
|
|
475
|
-
return xt_object_get(value, k);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/* ------------------------------------------------------------------------- */
|
|
479
|
-
/* Arrays */
|
|
480
|
-
/* ------------------------------------------------------------------------- */
|
|
481
|
-
|
|
482
|
-
static void xt_array_reserve(xt_array *array, uint32_t needed) {
|
|
483
|
-
if (needed <= array->capacity) return;
|
|
484
|
-
uint32_t capacity = array->capacity ? array->capacity * 2 : 8;
|
|
485
|
-
while (capacity < needed) capacity *= 2;
|
|
486
|
-
array->items = (xt_value *)realloc(array->items, sizeof(xt_value) * capacity);
|
|
487
|
-
if (!array->items) {
|
|
488
|
-
fprintf(stderr, "xbintsc: out of memory growing array\n");
|
|
489
|
-
abort();
|
|
490
|
-
}
|
|
491
|
-
for (uint32_t i = array->capacity; i < capacity; i++) array->items[i] = XT_UNDEFINED;
|
|
492
|
-
array->capacity = capacity;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
xt_value xt_array_new(int32_t count, xt_value *items) {
|
|
496
|
-
xt_array *array = (xt_array *)xt_alloc(sizeof(xt_array), XT_OBJECT_KIND_ARRAY);
|
|
497
|
-
array->length = 0;
|
|
498
|
-
array->capacity = 0;
|
|
499
|
-
array->items = NULL;
|
|
500
|
-
if (count > 0) {
|
|
501
|
-
xt_array_reserve(array, (uint32_t)count);
|
|
502
|
-
for (int32_t i = 0; i < count; i++) array->items[i] = items[i];
|
|
503
|
-
array->length = (uint32_t)count;
|
|
504
|
-
}
|
|
505
|
-
return XT_FROM_PTR(XT_TAG_ARRAY, array);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
xt_value xt_array_get(xt_value value, xt_value index) {
|
|
509
|
-
if (!XT_IS_ARRAY(value)) return XT_UNDEFINED;
|
|
510
|
-
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
511
|
-
int32_t i = xt_to_int32(index);
|
|
512
|
-
if (i < 0 || (uint32_t)i >= array->length) return XT_UNDEFINED;
|
|
513
|
-
return array->items[i];
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
xt_value xt_array_set(xt_value value, xt_value index, xt_value newValue) {
|
|
517
|
-
if (!XT_IS_ARRAY(value)) return newValue;
|
|
518
|
-
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
519
|
-
int32_t i = xt_to_int32(index);
|
|
520
|
-
if (i < 0) return newValue;
|
|
521
|
-
xt_array_reserve(array, (uint32_t)i + 1);
|
|
522
|
-
for (uint32_t j = array->length; j < (uint32_t)i; j++) array->items[j] = XT_UNDEFINED;
|
|
523
|
-
array->items[i] = newValue;
|
|
524
|
-
if ((uint32_t)i >= array->length) array->length = (uint32_t)i + 1;
|
|
525
|
-
return newValue;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
xt_value xt_array_push(xt_value value, xt_value newValue) {
|
|
529
|
-
if (!XT_IS_ARRAY(value)) return xt_number(0);
|
|
530
|
-
xt_array *array = (xt_array *)XT_GET_PTR(value);
|
|
531
|
-
xt_array_reserve(array, array->length + 1);
|
|
532
|
-
array->items[array->length++] = newValue;
|
|
533
|
-
return xt_number((double)array->length);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
xt_value xt_array_length(xt_value value) {
|
|
537
|
-
if (XT_IS_ARRAY(value)) return xt_number((double)((xt_array *)XT_GET_PTR(value))->length);
|
|
538
|
-
if (XT_IS_STRING(value)) return xt_number((double)xt_string_length(xt_as_string(value)));
|
|
539
|
-
return XT_UNDEFINED;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
xt_value xt_array_spread(xt_value target, xt_value source) {
|
|
543
|
-
if (!XT_IS_ARRAY(target) || !XT_IS_ARRAY(source)) return target;
|
|
544
|
-
xt_array *dst = (xt_array *)XT_GET_PTR(target);
|
|
545
|
-
xt_array *src = (xt_array *)XT_GET_PTR(source);
|
|
546
|
-
xt_array_reserve(dst, dst->length + src->length);
|
|
547
|
-
for (uint32_t i = 0; i < src->length; i++) dst->items[dst->length++] = src->items[i];
|
|
548
|
-
return target;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/* ------------------------------------------------------------------------- */
|
|
552
|
-
/* Standard library: Array / String methods */
|
|
553
|
-
/* ------------------------------------------------------------------------- */
|
|
554
|
-
|
|
555
|
-
static xt_value xt_arg_at(int32_t argc, xt_value *argv, int32_t index) {
|
|
556
|
-
if (!argv || index < 0 || index >= argc) return XT_UNDEFINED;
|
|
557
|
-
return argv[index];
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
static int xt_same_value_zero(xt_value a, xt_value b) {
|
|
561
|
-
if (XT_IS_NUMBER(a) && XT_IS_NUMBER(b)) {
|
|
562
|
-
double x = xt_to_double(a);
|
|
563
|
-
double y = xt_to_double(b);
|
|
564
|
-
if (isnan(x) && isnan(y)) return 1;
|
|
565
|
-
return x == y;
|
|
566
|
-
}
|
|
567
|
-
return xt_truthy(xt_seq(a, b));
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
static xt_array *xt_as_array(xt_value value) {
|
|
571
|
-
if (XT_IS_ARRAY(value)) return (xt_array *)XT_GET_PTR(value);
|
|
572
|
-
return NULL;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/* -- array methods -------------------------------------------------------- */
|
|
576
|
-
|
|
577
|
-
static xt_value xt_array_pop(xt_value value) {
|
|
578
|
-
xt_array *array = xt_as_array(value);
|
|
579
|
-
if (!array || array->length == 0) return XT_UNDEFINED;
|
|
580
|
-
return array->items[--array->length];
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
static xt_value xt_array_shift(xt_value value) {
|
|
584
|
-
xt_array *array = xt_as_array(value);
|
|
585
|
-
if (!array || array->length == 0) return XT_UNDEFINED;
|
|
586
|
-
xt_value first = array->items[0];
|
|
587
|
-
for (uint32_t i = 1; i < array->length; i++) array->items[i - 1] = array->items[i];
|
|
588
|
-
array->length--;
|
|
589
|
-
return first;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
static xt_value xt_array_unshift(xt_value value, int32_t argc, xt_value *argv) {
|
|
593
|
-
xt_array *array = xt_as_array(value);
|
|
594
|
-
if (!array) return XT_UNDEFINED;
|
|
595
|
-
if (argc <= 0) return xt_number((double)array->length);
|
|
596
|
-
xt_array_reserve(array, array->length + (uint32_t)argc);
|
|
597
|
-
for (uint32_t i = array->length; i-- > 0;) array->items[i + argc] = array->items[i];
|
|
598
|
-
for (int32_t i = 0; i < argc; i++) array->items[i] = argv[i];
|
|
599
|
-
array->length += (uint32_t)argc;
|
|
600
|
-
return xt_number((double)array->length);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
static void xt_buffer_append(char **out, size_t *length, size_t *capacity, const char *data, size_t size) {
|
|
604
|
-
if (*length + size + 1 > *capacity) {
|
|
605
|
-
while (*length + size + 1 > *capacity) *capacity = (*capacity) * 2;
|
|
606
|
-
*out = (char *)realloc(*out, *capacity);
|
|
607
|
-
if (!*out) abort();
|
|
608
|
-
}
|
|
609
|
-
memcpy(*out + *length, data, size);
|
|
610
|
-
*length += size;
|
|
611
|
-
(*out)[*length] = '\0';
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
static xt_value xt_array_join(xt_value value, xt_value separator) {
|
|
615
|
-
xt_array *array = xt_as_array(value);
|
|
616
|
-
if (!array) return xt_string_from_cstr("");
|
|
617
|
-
xt_string *sep = xt_as_string(xt_to_string(separator == XT_UNDEFINED ? xt_string_from_cstr(",") : separator));
|
|
618
|
-
size_t capacity = 32;
|
|
619
|
-
size_t length = 0;
|
|
620
|
-
char *out = (char *)malloc(capacity);
|
|
621
|
-
if (!out) abort();
|
|
622
|
-
out[0] = '\0';
|
|
623
|
-
for (uint32_t i = 0; i < array->length; i++) {
|
|
624
|
-
if (i > 0) xt_buffer_append(&out, &length, &capacity, sep->data, sep->length);
|
|
625
|
-
xt_value item = array->items[i];
|
|
626
|
-
if (item == XT_UNDEFINED || item == XT_NULL) continue;
|
|
627
|
-
xt_string *str = xt_as_string(xt_to_string(item));
|
|
628
|
-
xt_buffer_append(&out, &length, &capacity, str->data, str->length);
|
|
629
|
-
}
|
|
630
|
-
xt_value result = xt_string_new(out, length);
|
|
631
|
-
free(out);
|
|
632
|
-
return result;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
static void xt_normalize_slice(int32_t len, int32_t *start, int32_t *end) {
|
|
636
|
-
if (*start < 0) *start += len;
|
|
637
|
-
if (*start < 0) *start = 0;
|
|
638
|
-
if (*start > len) *start = len;
|
|
639
|
-
if (*end < 0) *end += len;
|
|
640
|
-
if (*end < 0) *end = 0;
|
|
641
|
-
if (*end > len) *end = len;
|
|
642
|
-
if (*end < *start) *end = *start;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
static xt_value xt_array_slice(xt_value value, xt_value startValue, xt_value endValue) {
|
|
646
|
-
xt_array *array = xt_as_array(value);
|
|
647
|
-
if (!array) return XT_UNDEFINED;
|
|
648
|
-
int32_t len = (int32_t)array->length;
|
|
649
|
-
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
650
|
-
int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
|
|
651
|
-
xt_normalize_slice(len, &start, &end);
|
|
652
|
-
return xt_array_new(end - start, array->items + start);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
static xt_value xt_array_index_of(xt_value value, xt_value needle, xt_value fromValue, int sameValueZero) {
|
|
656
|
-
xt_array *array = xt_as_array(value);
|
|
657
|
-
if (!array) return xt_number(-1);
|
|
658
|
-
int32_t from = fromValue == XT_UNDEFINED ? 0 : xt_to_int32(fromValue);
|
|
659
|
-
if (from < 0) from += (int32_t)array->length;
|
|
660
|
-
if (from < 0) from = 0;
|
|
661
|
-
for (uint32_t i = (uint32_t)from; i < array->length; i++) {
|
|
662
|
-
int equal = sameValueZero ? xt_same_value_zero(array->items[i], needle) : xt_truthy(xt_seq(array->items[i], needle));
|
|
663
|
-
if (equal) return xt_number((double)i);
|
|
664
|
-
}
|
|
665
|
-
return xt_number(-1);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
static xt_value xt_array_concat(xt_value value, int32_t argc, xt_value *argv) {
|
|
669
|
-
xt_array *array = xt_as_array(value);
|
|
670
|
-
xt_value result = xt_array_new(0, NULL);
|
|
671
|
-
if (!array) return result;
|
|
672
|
-
for (uint32_t i = 0; i < array->length; i++) xt_array_push(result, array->items[i]);
|
|
673
|
-
for (int32_t i = 0; i < argc; i++) {
|
|
674
|
-
if (XT_IS_ARRAY(argv[i])) {
|
|
675
|
-
xt_array *other = xt_as_array(argv[i]);
|
|
676
|
-
for (uint32_t j = 0; j < other->length; j++) xt_array_push(result, other->items[j]);
|
|
677
|
-
} else {
|
|
678
|
-
xt_array_push(result, argv[i]);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return result;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
static xt_value xt_array_for_each(xt_value value, xt_value fn) {
|
|
685
|
-
xt_array *array = xt_as_array(value);
|
|
686
|
-
if (!array) return XT_UNDEFINED;
|
|
687
|
-
for (uint32_t i = 0; i < array->length; i++) {
|
|
688
|
-
xt_value args[3] = {array->items[i], xt_number((double)i), value};
|
|
689
|
-
xt_closure_call(fn, 3, args);
|
|
690
|
-
}
|
|
691
|
-
return XT_UNDEFINED;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
static xt_value xt_array_map(xt_value value, xt_value fn) {
|
|
695
|
-
xt_array *array = xt_as_array(value);
|
|
696
|
-
xt_value result = xt_array_new(0, NULL);
|
|
697
|
-
if (!array) return result;
|
|
698
|
-
for (uint32_t i = 0; i < array->length; i++) {
|
|
699
|
-
xt_value args[3] = {array->items[i], xt_number((double)i), value};
|
|
700
|
-
xt_array_push(result, xt_closure_call(fn, 3, args));
|
|
701
|
-
}
|
|
702
|
-
return result;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
static xt_value xt_array_filter(xt_value value, xt_value fn) {
|
|
706
|
-
xt_array *array = xt_as_array(value);
|
|
707
|
-
xt_value result = xt_array_new(0, NULL);
|
|
708
|
-
if (!array) return result;
|
|
709
|
-
for (uint32_t i = 0; i < array->length; i++) {
|
|
710
|
-
xt_value args[3] = {array->items[i], xt_number((double)i), value};
|
|
711
|
-
if (xt_truthy(xt_closure_call(fn, 3, args))) xt_array_push(result, array->items[i]);
|
|
712
|
-
}
|
|
713
|
-
return result;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
static xt_value xt_array_reduce(xt_value value, xt_value fn, int32_t hasInitial, xt_value initial) {
|
|
717
|
-
xt_array *array = xt_as_array(value);
|
|
718
|
-
if (!array) return XT_UNDEFINED;
|
|
719
|
-
uint32_t i = 0;
|
|
720
|
-
xt_value accumulator;
|
|
721
|
-
if (hasInitial) {
|
|
722
|
-
accumulator = initial;
|
|
723
|
-
} else if (array->length > 0) {
|
|
724
|
-
accumulator = array->items[0];
|
|
725
|
-
i = 1;
|
|
726
|
-
} else {
|
|
727
|
-
xt_throw(xt_string_from_cstr("TypeError: Reduce of empty array with no initial value"));
|
|
728
|
-
return XT_UNDEFINED;
|
|
729
|
-
}
|
|
730
|
-
for (; i < array->length; i++) {
|
|
731
|
-
xt_value args[4] = {accumulator, array->items[i], xt_number((double)i), value};
|
|
732
|
-
accumulator = xt_closure_call(fn, 4, args);
|
|
733
|
-
}
|
|
734
|
-
return accumulator;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
/* -- string methods ------------------------------------------------------- */
|
|
738
|
-
|
|
739
|
-
static int32_t xt_string_find(xt_string *haystack, xt_string *needle, int32_t from) {
|
|
740
|
-
if (from < 0) from = 0;
|
|
741
|
-
if ((uint32_t)from > haystack->length) return -1;
|
|
742
|
-
if (needle->length == 0) return from;
|
|
743
|
-
for (uint32_t i = (uint32_t)from; i + needle->length <= haystack->length; i++) {
|
|
744
|
-
if (memcmp(haystack->data + i, needle->data, needle->length) == 0) return (int32_t)i;
|
|
745
|
-
}
|
|
746
|
-
return -1;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
static xt_value xt_string_char_at(xt_value value, xt_value indexValue) {
|
|
750
|
-
xt_string *s = xt_as_string(value);
|
|
751
|
-
if (!s) return xt_string_from_cstr("");
|
|
752
|
-
int32_t index = xt_to_int32(indexValue);
|
|
753
|
-
if (index < 0 || (uint32_t)index >= s->length) return xt_string_from_cstr("");
|
|
754
|
-
return xt_string_new(s->data + index, 1);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
static xt_value xt_string_char_code_at(xt_value value, xt_value indexValue) {
|
|
758
|
-
xt_string *s = xt_as_string(value);
|
|
759
|
-
if (!s) return xt_number(NAN);
|
|
760
|
-
int32_t index = xt_to_int32(indexValue);
|
|
761
|
-
if (index < 0 || (uint32_t)index >= s->length) return xt_number(NAN);
|
|
762
|
-
return xt_number((double)(unsigned char)s->data[index]);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
static xt_value xt_string_index_of(xt_value value, xt_value needleValue, xt_value fromValue) {
|
|
766
|
-
xt_string *s = xt_as_string(value);
|
|
767
|
-
if (!s) return xt_number(-1);
|
|
768
|
-
xt_string *needle = xt_as_string(xt_to_string(needleValue));
|
|
769
|
-
int32_t from = fromValue == XT_UNDEFINED ? 0 : xt_to_int32(fromValue);
|
|
770
|
-
return xt_number((double)xt_string_find(s, needle, from));
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
static xt_value xt_string_slice(xt_value value, xt_value startValue, xt_value endValue) {
|
|
774
|
-
xt_string *s = xt_as_string(value);
|
|
775
|
-
if (!s) return xt_string_from_cstr("");
|
|
776
|
-
int32_t len = (int32_t)s->length;
|
|
777
|
-
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
778
|
-
int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
|
|
779
|
-
xt_normalize_slice(len, &start, &end);
|
|
780
|
-
return xt_string_new(s->data + start, (size_t)(end - start));
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
static xt_value xt_string_substring(xt_value value, xt_value startValue, xt_value endValue) {
|
|
784
|
-
xt_string *s = xt_as_string(value);
|
|
785
|
-
if (!s) return xt_string_from_cstr("");
|
|
786
|
-
int32_t len = (int32_t)s->length;
|
|
787
|
-
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
788
|
-
int32_t end = endValue == XT_UNDEFINED ? len : xt_to_int32(endValue);
|
|
789
|
-
if (start < 0) start = 0;
|
|
790
|
-
if (start > len) start = len;
|
|
791
|
-
if (end < 0) end = 0;
|
|
792
|
-
if (end > len) end = len;
|
|
793
|
-
if (start > end) {
|
|
794
|
-
int32_t swap = start;
|
|
795
|
-
start = end;
|
|
796
|
-
end = swap;
|
|
797
|
-
}
|
|
798
|
-
return xt_string_new(s->data + start, (size_t)(end - start));
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
static xt_value xt_string_substr(xt_value value, xt_value startValue, xt_value lengthValue) {
|
|
802
|
-
xt_string *s = xt_as_string(value);
|
|
803
|
-
if (!s) return xt_string_from_cstr("");
|
|
804
|
-
int32_t len = (int32_t)s->length;
|
|
805
|
-
int32_t start = startValue == XT_UNDEFINED ? 0 : xt_to_int32(startValue);
|
|
806
|
-
if (start < 0) start += len;
|
|
807
|
-
if (start < 0) start = 0;
|
|
808
|
-
if (start > len) start = len;
|
|
809
|
-
int32_t length = lengthValue == XT_UNDEFINED ? len - start : xt_to_int32(lengthValue);
|
|
810
|
-
if (length < 0) length = 0;
|
|
811
|
-
if (start + length > len) length = len - start;
|
|
812
|
-
return xt_string_new(s->data + start, (size_t)length);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
static xt_value xt_string_split(xt_value value, xt_value separatorValue) {
|
|
816
|
-
xt_value result = xt_array_new(0, NULL);
|
|
817
|
-
xt_string *s = xt_as_string(value);
|
|
818
|
-
if (!s) return result;
|
|
819
|
-
if (separatorValue == XT_UNDEFINED) {
|
|
820
|
-
xt_array_push(result, value);
|
|
821
|
-
return result;
|
|
822
|
-
}
|
|
823
|
-
xt_string *sep = xt_as_string(xt_to_string(separatorValue));
|
|
824
|
-
if (sep->length == 0) {
|
|
825
|
-
for (uint32_t i = 0; i < s->length; i++) xt_array_push(result, xt_string_new(s->data + i, 1));
|
|
826
|
-
return result;
|
|
827
|
-
}
|
|
828
|
-
uint32_t start = 0;
|
|
829
|
-
uint32_t i = 0;
|
|
830
|
-
while (i + sep->length <= s->length) {
|
|
831
|
-
if (memcmp(s->data + i, sep->data, sep->length) == 0) {
|
|
832
|
-
xt_array_push(result, xt_string_new(s->data + start, i - start));
|
|
833
|
-
i += sep->length;
|
|
834
|
-
start = i;
|
|
835
|
-
} else {
|
|
836
|
-
i++;
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
xt_array_push(result, xt_string_new(s->data + start, s->length - start));
|
|
840
|
-
return result;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
static xt_value xt_string_case(xt_value value, int upper) {
|
|
844
|
-
xt_string *s = xt_as_string(value);
|
|
845
|
-
if (!s) return value;
|
|
846
|
-
xt_string *out = xt_string_alloc(s->length);
|
|
847
|
-
for (uint32_t i = 0; i < s->length; i++) {
|
|
848
|
-
unsigned char c = (unsigned char)s->data[i];
|
|
849
|
-
out->data[i] = (char)(upper ? toupper((int)c) : tolower((int)c));
|
|
850
|
-
}
|
|
851
|
-
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
static xt_value xt_string_trim(xt_value value) {
|
|
855
|
-
xt_string *s = xt_as_string(value);
|
|
856
|
-
if (!s) return value;
|
|
857
|
-
uint32_t start = 0;
|
|
858
|
-
uint32_t end = s->length;
|
|
859
|
-
while (start < end && isspace((unsigned char)s->data[start])) start++;
|
|
860
|
-
while (end > start && isspace((unsigned char)s->data[end - 1])) end--;
|
|
861
|
-
return xt_string_new(s->data + start, end - start);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
static xt_value xt_string_replace(xt_value value, xt_value searchValue, xt_value replacementValue) {
|
|
865
|
-
xt_string *s = xt_as_string(value);
|
|
866
|
-
if (!s) return value;
|
|
867
|
-
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
868
|
-
xt_string *replacement = xt_as_string(xt_to_string(replacementValue));
|
|
869
|
-
int32_t at = xt_string_find(s, search, 0);
|
|
870
|
-
if (at < 0) return value;
|
|
871
|
-
size_t total = s->length - search->length + replacement->length;
|
|
872
|
-
xt_string *out = xt_string_alloc(total);
|
|
873
|
-
memcpy(out->data, s->data, (size_t)at);
|
|
874
|
-
memcpy(out->data + at, replacement->data, replacement->length);
|
|
875
|
-
memcpy(out->data + at + replacement->length, s->data + at + search->length,
|
|
876
|
-
s->length - (size_t)at - search->length);
|
|
877
|
-
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
static xt_value xt_string_repeat(xt_value value, xt_value countValue) {
|
|
881
|
-
xt_string *s = xt_as_string(value);
|
|
882
|
-
if (!s) return value;
|
|
883
|
-
int32_t count = xt_to_int32(countValue);
|
|
884
|
-
if (count < 0) {
|
|
885
|
-
xt_throw(xt_string_from_cstr("RangeError: Invalid count value"));
|
|
886
|
-
return XT_UNDEFINED;
|
|
887
|
-
}
|
|
888
|
-
xt_string *out = xt_string_alloc((size_t)s->length * (size_t)count);
|
|
889
|
-
for (int32_t i = 0; i < count; i++) memcpy(out->data + (size_t)i * s->length, s->data, s->length);
|
|
890
|
-
return XT_FROM_PTR(XT_TAG_STRING, out);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
static xt_value xt_string_starts_with(xt_value value, xt_value searchValue) {
|
|
894
|
-
xt_string *s = xt_as_string(value);
|
|
895
|
-
if (!s) return XT_FALSE;
|
|
896
|
-
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
897
|
-
if (search->length > s->length) return XT_FALSE;
|
|
898
|
-
return xt_bool(memcmp(s->data, search->data, search->length) == 0);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
static xt_value xt_string_ends_with(xt_value value, xt_value searchValue) {
|
|
902
|
-
xt_string *s = xt_as_string(value);
|
|
903
|
-
if (!s) return XT_FALSE;
|
|
904
|
-
xt_string *search = xt_as_string(xt_to_string(searchValue));
|
|
905
|
-
if (search->length > s->length) return XT_FALSE;
|
|
906
|
-
return xt_bool(memcmp(s->data + s->length - search->length, search->data, search->length) == 0);
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
static xt_value xt_string_concat_args(xt_value value, int32_t argc, xt_value *argv) {
|
|
910
|
-
xt_value result = XT_IS_STRING(value) ? value : xt_to_string(value);
|
|
911
|
-
for (int32_t i = 0; i < argc; i++) result = xt_add(result, xt_to_string(argv[i]));
|
|
912
|
-
return result;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
/* -- method dispatch ------------------------------------------------------ */
|
|
916
|
-
|
|
917
|
-
static xt_value xt_array_method(xt_value target, const char *method, int32_t argc, xt_value *argv) {
|
|
918
|
-
if (strcmp(method, "push") == 0) {
|
|
919
|
-
xt_value result = xt_number(0);
|
|
920
|
-
for (int32_t i = 0; i < argc; i++) result = xt_array_push(target, argv[i]);
|
|
921
|
-
if (argc == 0) result = xt_number((double)xt_as_array(target)->length);
|
|
922
|
-
return result;
|
|
923
|
-
}
|
|
924
|
-
if (strcmp(method, "pop") == 0) return xt_array_pop(target);
|
|
925
|
-
if (strcmp(method, "shift") == 0) return xt_array_shift(target);
|
|
926
|
-
if (strcmp(method, "unshift") == 0) return xt_array_unshift(target, argc, argv);
|
|
927
|
-
if (strcmp(method, "join") == 0) return xt_array_join(target, xt_arg_at(argc, argv, 0));
|
|
928
|
-
if (strcmp(method, "slice") == 0) return xt_array_slice(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
929
|
-
if (strcmp(method, "indexOf") == 0) return xt_array_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1), 0);
|
|
930
|
-
if (strcmp(method, "includes") == 0) return xt_bool(xt_truthy(xt_ne(xt_array_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1), 1), xt_number(-1))));
|
|
931
|
-
if (strcmp(method, "concat") == 0) return xt_array_concat(target, argc, argv);
|
|
932
|
-
if (strcmp(method, "forEach") == 0) return xt_array_for_each(target, xt_arg_at(argc, argv, 0));
|
|
933
|
-
if (strcmp(method, "map") == 0) return xt_array_map(target, xt_arg_at(argc, argv, 0));
|
|
934
|
-
if (strcmp(method, "filter") == 0) return xt_array_filter(target, xt_arg_at(argc, argv, 0));
|
|
935
|
-
if (strcmp(method, "reduce") == 0) {
|
|
936
|
-
int32_t hasInitial = argc >= 2 ? 1 : 0;
|
|
937
|
-
return xt_array_reduce(target, xt_arg_at(argc, argv, 0), hasInitial, xt_arg_at(argc, argv, 1));
|
|
938
|
-
}
|
|
939
|
-
if (strcmp(method, "reverse") == 0) {
|
|
940
|
-
xt_array *array = xt_as_array(target);
|
|
941
|
-
for (uint32_t i = 0; i + 1 < array->length; i++, array->length--) {
|
|
942
|
-
xt_value tmp = array->items[i];
|
|
943
|
-
array->items[i] = array->items[array->length - 1];
|
|
944
|
-
array->items[array->length - 1] = tmp;
|
|
945
|
-
}
|
|
946
|
-
return target;
|
|
947
|
-
}
|
|
948
|
-
return XT_UNDEFINED;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
static xt_value xt_string_method(xt_value target, const char *method, int32_t argc, xt_value *argv) {
|
|
952
|
-
if (strcmp(method, "charAt") == 0) return xt_string_char_at(target, xt_arg_at(argc, argv, 0));
|
|
953
|
-
if (strcmp(method, "charCodeAt") == 0) return xt_string_char_code_at(target, xt_arg_at(argc, argv, 0));
|
|
954
|
-
if (strcmp(method, "indexOf") == 0) return xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
955
|
-
if (strcmp(method, "includes") == 0) return xt_bool(xt_truthy(xt_ne(xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1)), xt_number(-1))));
|
|
956
|
-
if (strcmp(method, "slice") == 0) return xt_string_slice(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
957
|
-
if (strcmp(method, "substring") == 0) return xt_string_substring(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
958
|
-
if (strcmp(method, "substr") == 0) return xt_string_substr(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
959
|
-
if (strcmp(method, "split") == 0) return xt_string_split(target, xt_arg_at(argc, argv, 0));
|
|
960
|
-
if (strcmp(method, "toUpperCase") == 0) return xt_string_case(target, 1);
|
|
961
|
-
if (strcmp(method, "toLowerCase") == 0) return xt_string_case(target, 0);
|
|
962
|
-
if (strcmp(method, "trim") == 0) return xt_string_trim(target);
|
|
963
|
-
if (strcmp(method, "replace") == 0) return xt_string_replace(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
964
|
-
if (strcmp(method, "repeat") == 0) return xt_string_repeat(target, xt_arg_at(argc, argv, 0));
|
|
965
|
-
if (strcmp(method, "startsWith") == 0) return xt_string_starts_with(target, xt_arg_at(argc, argv, 0));
|
|
966
|
-
if (strcmp(method, "endsWith") == 0) return xt_string_ends_with(target, xt_arg_at(argc, argv, 0));
|
|
967
|
-
if (strcmp(method, "concat") == 0) return xt_string_concat_args(target, argc, argv);
|
|
968
|
-
return XT_UNDEFINED;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *argv) {
|
|
972
|
-
const char *method = xt_string_data(name);
|
|
973
|
-
if (!method) return XT_UNDEFINED;
|
|
974
|
-
if (XT_IS_OBJECT(target)) {
|
|
975
|
-
xt_value fn = xt_object_get(target, name);
|
|
976
|
-
if (XT_IS_FUNCTION(fn)) return xt_closure_call(fn, argc, argv);
|
|
977
|
-
}
|
|
978
|
-
if (XT_IS_ARRAY(target)) return xt_array_method(target, method, argc, argv);
|
|
979
|
-
if (XT_IS_STRING(target)) return xt_string_method(target, method, argc, argv);
|
|
980
|
-
return XT_UNDEFINED;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
/* ------------------------------------------------------------------------- */
|
|
984
|
-
/* Math */
|
|
985
|
-
/* ------------------------------------------------------------------------- */
|
|
986
|
-
|
|
987
|
-
xt_value xt_math_call(xt_value name, int32_t argc, xt_value *argv) {
|
|
988
|
-
const char *fn = xt_string_data(name);
|
|
989
|
-
double a = xt_to_number(xt_arg_at(argc, argv, 0));
|
|
990
|
-
double b = xt_to_number(xt_arg_at(argc, argv, 1));
|
|
991
|
-
if (!fn) return xt_number(NAN);
|
|
992
|
-
if (strcmp(fn, "abs") == 0) return xt_number(fabs(a));
|
|
993
|
-
if (strcmp(fn, "floor") == 0) return xt_number(floor(a));
|
|
994
|
-
if (strcmp(fn, "ceil") == 0) return xt_number(ceil(a));
|
|
995
|
-
if (strcmp(fn, "round") == 0) return xt_number(floor(a + 0.5));
|
|
996
|
-
if (strcmp(fn, "trunc") == 0) return xt_number(trunc(a));
|
|
997
|
-
if (strcmp(fn, "sqrt") == 0) return xt_number(sqrt(a));
|
|
998
|
-
if (strcmp(fn, "cbrt") == 0) return xt_number(cbrt(a));
|
|
999
|
-
if (strcmp(fn, "pow") == 0) return xt_number(pow(a, b));
|
|
1000
|
-
if (strcmp(fn, "exp") == 0) return xt_number(exp(a));
|
|
1001
|
-
if (strcmp(fn, "log") == 0) return xt_number(log(a));
|
|
1002
|
-
if (strcmp(fn, "log2") == 0) return xt_number(log2(a));
|
|
1003
|
-
if (strcmp(fn, "log10") == 0) return xt_number(log10(a));
|
|
1004
|
-
if (strcmp(fn, "sin") == 0) return xt_number(sin(a));
|
|
1005
|
-
if (strcmp(fn, "cos") == 0) return xt_number(cos(a));
|
|
1006
|
-
if (strcmp(fn, "tan") == 0) return xt_number(tan(a));
|
|
1007
|
-
if (strcmp(fn, "asin") == 0) return xt_number(asin(a));
|
|
1008
|
-
if (strcmp(fn, "acos") == 0) return xt_number(acos(a));
|
|
1009
|
-
if (strcmp(fn, "atan") == 0) return xt_number(atan(a));
|
|
1010
|
-
if (strcmp(fn, "atan2") == 0) return xt_number(atan2(a, b));
|
|
1011
|
-
if (strcmp(fn, "hypot") == 0) return xt_number(hypot(a, b));
|
|
1012
|
-
if (strcmp(fn, "sign") == 0) {
|
|
1013
|
-
if (isnan(a)) return xt_number(NAN);
|
|
1014
|
-
return xt_number(a > 0 ? 1.0 : a < 0 ? -1.0 : a);
|
|
1015
|
-
}
|
|
1016
|
-
if (strcmp(fn, "random") == 0) return xt_number((double)rand() / ((double)RAND_MAX + 1.0));
|
|
1017
|
-
if (strcmp(fn, "min") == 0) {
|
|
1018
|
-
double result = INFINITY;
|
|
1019
|
-
for (int32_t i = 0; i < argc; i++) {
|
|
1020
|
-
double v = xt_to_number(argv[i]);
|
|
1021
|
-
if (isnan(v)) return xt_number(NAN);
|
|
1022
|
-
if (v < result) result = v;
|
|
1023
|
-
}
|
|
1024
|
-
return xt_number(result);
|
|
1025
|
-
}
|
|
1026
|
-
if (strcmp(fn, "max") == 0) {
|
|
1027
|
-
double result = -INFINITY;
|
|
1028
|
-
for (int32_t i = 0; i < argc; i++) {
|
|
1029
|
-
double v = xt_to_number(argv[i]);
|
|
1030
|
-
if (isnan(v)) return xt_number(NAN);
|
|
1031
|
-
if (v > result) result = v;
|
|
1032
|
-
}
|
|
1033
|
-
return xt_number(result);
|
|
1034
|
-
}
|
|
1035
|
-
return xt_number(NAN);
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
/* ------------------------------------------------------------------------- */
|
|
1039
|
-
/* Object methods and global functions */
|
|
1040
|
-
/* ------------------------------------------------------------------------- */
|
|
1041
|
-
|
|
1042
|
-
xt_value xt_object_values(xt_value value) {
|
|
1043
|
-
xt_value result = xt_array_new(0, NULL);
|
|
1044
|
-
if (!XT_IS_OBJECT(value)) return result;
|
|
1045
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
1046
|
-
for (uint32_t i = 0; i < obj->count; i++) xt_array_push(result, obj->properties[i].value);
|
|
1047
|
-
return result;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
xt_value xt_object_entries(xt_value value) {
|
|
1051
|
-
xt_value result = xt_array_new(0, NULL);
|
|
1052
|
-
if (!XT_IS_OBJECT(value)) return result;
|
|
1053
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
1054
|
-
for (uint32_t i = 0; i < obj->count; i++) {
|
|
1055
|
-
xt_value pair = xt_array_new(0, NULL);
|
|
1056
|
-
xt_array_push(pair, XT_FROM_PTR(XT_TAG_STRING, obj->properties[i].key));
|
|
1057
|
-
xt_array_push(pair, obj->properties[i].value);
|
|
1058
|
-
xt_array_push(result, pair);
|
|
1059
|
-
}
|
|
1060
|
-
return result;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
xt_value xt_object_assign(int32_t argc, xt_value *argv) {
|
|
1064
|
-
if (argc <= 0) return xt_undefined();
|
|
1065
|
-
xt_value target = argv[0];
|
|
1066
|
-
if (!XT_IS_OBJECT(target)) return target;
|
|
1067
|
-
for (int32_t i = 1; i < argc; i++) {
|
|
1068
|
-
if (!XT_IS_OBJECT(argv[i])) continue;
|
|
1069
|
-
xt_object *source = (xt_object *)XT_GET_PTR(argv[i]);
|
|
1070
|
-
for (uint32_t j = 0; j < source->count; j++) {
|
|
1071
|
-
xt_object_set(target, XT_FROM_PTR(XT_TAG_STRING, source->properties[j].key), source->properties[j].value);
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
return target;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
/* Copy every own enumerable property of `source` onto `target` (`{...source}`). */
|
|
1078
|
-
xt_value xt_object_spread(xt_value target, xt_value source) {
|
|
1079
|
-
xt_value args[2];
|
|
1080
|
-
args[0] = target;
|
|
1081
|
-
args[1] = source;
|
|
1082
|
-
return xt_object_assign(2, args);
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
xt_value xt_parse_int(int32_t argc, xt_value *argv) {
|
|
1086
|
-
xt_string *s = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
1087
|
-
int32_t radix = argc >= 2 ? xt_to_int32(argv[1]) : 0;
|
|
1088
|
-
char *end = NULL;
|
|
1089
|
-
long value = strtol(s->data, &end, radix);
|
|
1090
|
-
if (end == s->data) return xt_number(NAN);
|
|
1091
|
-
return xt_number((double)value);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
xt_value xt_parse_float(int32_t argc, xt_value *argv) {
|
|
1095
|
-
xt_string *s = xt_as_string(xt_to_string(xt_arg_at(argc, argv, 0)));
|
|
1096
|
-
char *end = NULL;
|
|
1097
|
-
double value = strtod(s->data, &end);
|
|
1098
|
-
if (end == s->data) return xt_number(NAN);
|
|
1099
|
-
return xt_number(value);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
xt_value xt_is_nan(int32_t argc, xt_value *argv) {
|
|
1103
|
-
double value = xt_to_number(xt_arg_at(argc, argv, 0));
|
|
1104
|
-
return xt_bool(value != value);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
xt_value xt_is_finite(int32_t argc, xt_value *argv) {
|
|
1108
|
-
double value = xt_to_number(xt_arg_at(argc, argv, 0));
|
|
1109
|
-
return xt_bool(!isnan(value) && !isinf(value));
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
xt_value xt_number_ctor(int32_t argc, xt_value *argv) {
|
|
1113
|
-
if (argc == 0) return xt_number(0);
|
|
1114
|
-
return xt_number(xt_to_number(argv[0]));
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
xt_value xt_string_ctor(int32_t argc, xt_value *argv) {
|
|
1118
|
-
if (argc == 0) return xt_string_from_cstr("");
|
|
1119
|
-
return xt_to_string(argv[0]);
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
xt_value xt_boolean_ctor(int32_t argc, xt_value *argv) {
|
|
1123
|
-
if (argc == 0) return XT_FALSE;
|
|
1124
|
-
return xt_bool(xt_truthy(argv[0]));
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
xt_value xt_in(xt_value key, xt_value value) {
|
|
1128
|
-
if (XT_IS_OBJECT(value)) return xt_object_has(value, key);
|
|
1129
|
-
if (XT_IS_ARRAY(value)) {
|
|
1130
|
-
int32_t index = xt_to_int32(key);
|
|
1131
|
-
return xt_bool(index >= 0 && (uint32_t)index < xt_as_array(value)->length);
|
|
1132
|
-
}
|
|
1133
|
-
if (XT_IS_STRING(value)) {
|
|
1134
|
-
xt_string *s = xt_as_string(value);
|
|
1135
|
-
xt_string *k = xt_as_string(xt_to_string(key));
|
|
1136
|
-
if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return XT_TRUE;
|
|
1137
|
-
int32_t index = xt_to_int32(key);
|
|
1138
|
-
return xt_bool(index >= 0 && (uint32_t)index < s->length);
|
|
1139
|
-
}
|
|
1140
|
-
return XT_FALSE;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
xt_value xt_delete(xt_value value, xt_value key) {
|
|
1144
|
-
if (XT_IS_OBJECT(value)) {
|
|
1145
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
1146
|
-
xt_string *k = xt_as_string(xt_to_string(key));
|
|
1147
|
-
for (uint32_t i = 0; i < obj->count; i++) {
|
|
1148
|
-
if (xt_string_equals(obj->properties[i].key, k)) {
|
|
1149
|
-
for (uint32_t j = i + 1; j < obj->count; j++) obj->properties[j - 1] = obj->properties[j];
|
|
1150
|
-
obj->count--;
|
|
1151
|
-
break;
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
return XT_TRUE;
|
|
1155
|
-
}
|
|
1156
|
-
if (XT_IS_ARRAY(value)) {
|
|
1157
|
-
xt_array_set(value, key, XT_UNDEFINED);
|
|
1158
|
-
return XT_TRUE;
|
|
1159
|
-
}
|
|
1160
|
-
return XT_TRUE;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
xt_value xt_rest_args(int32_t argc, xt_value *argv, int32_t start) {
|
|
1164
|
-
if (start < 0) start = 0;
|
|
1165
|
-
int32_t count = argc - start;
|
|
1166
|
-
if (count < 0) count = 0;
|
|
1167
|
-
return xt_array_new(count, argv ? argv + start : NULL);
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
/* ------------------------------------------------------------------------- */
|
|
1171
|
-
/* Generic member access */
|
|
1172
|
-
/* ------------------------------------------------------------------------- */
|
|
1173
|
-
|
|
1174
|
-
xt_value xt_get(xt_value target, xt_value key) {
|
|
1175
|
-
if (XT_IS_ARRAY(target)) return xt_array_get(target, key);
|
|
1176
|
-
if (XT_IS_OBJECT(target)) return xt_object_get(target, key);
|
|
1177
|
-
if (XT_IS_STRING(target)) {
|
|
1178
|
-
/* Property access on a string: `.length` and numeric indexing. */
|
|
1179
|
-
xt_string *s = xt_as_string(target);
|
|
1180
|
-
if (XT_IS_STRING(key)) {
|
|
1181
|
-
xt_string *k = xt_as_string(key);
|
|
1182
|
-
if (k->length == 6 && memcmp(k->data, "length", 6) == 0) return xt_number((double)s->length);
|
|
1183
|
-
}
|
|
1184
|
-
int32_t index = xt_to_int32(key);
|
|
1185
|
-
if (index >= 0 && (uint32_t)index < s->length) return xt_string_new(s->data + index, 1);
|
|
1186
|
-
return XT_UNDEFINED;
|
|
1187
|
-
}
|
|
1188
|
-
return XT_UNDEFINED;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
1192
|
-
if (XT_IS_ARRAY(target)) return xt_array_set(target, key, value);
|
|
1193
|
-
if (XT_IS_OBJECT(target)) return xt_object_set(target, key, value);
|
|
1194
|
-
return value;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
/* ------------------------------------------------------------------------- */
|
|
1198
|
-
/* Boxes */
|
|
1199
|
-
/* ------------------------------------------------------------------------- */
|
|
1200
|
-
|
|
1201
|
-
xt_value xt_box_new(xt_value value) { return xt_array_new(1, &value); }
|
|
1202
|
-
|
|
1203
|
-
xt_value xt_box_get(xt_value box) { return xt_array_get(box, xt_number(0)); }
|
|
1204
|
-
|
|
1205
|
-
xt_value xt_box_set(xt_value box, xt_value value) { return xt_array_set(box, xt_number(0), value); }
|
|
1206
|
-
|
|
1207
|
-
xt_value xt_is_nullish(xt_value value) { return xt_bool(value == XT_UNDEFINED || value == XT_NULL); }
|
|
1208
|
-
|
|
1209
|
-
/* ------------------------------------------------------------------------- */
|
|
1210
|
-
/* Functions */
|
|
1211
|
-
/* ------------------------------------------------------------------------- */
|
|
1212
|
-
|
|
1213
|
-
xt_value xt_arg(int32_t argc, xt_value *argv, int32_t index) {
|
|
1214
|
-
if (index < 0 || index >= argc) return XT_UNDEFINED;
|
|
1215
|
-
return argv[index];
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
xt_value xt_closure_new(void *fn, int32_t env_count, xt_value *env) {
|
|
1219
|
-
xt_function *function = (xt_function *)xt_alloc(sizeof(xt_function), XT_OBJECT_KIND_FUNCTION);
|
|
1220
|
-
function->code = (xt_code_fn)fn;
|
|
1221
|
-
function->environment_count = env_count;
|
|
1222
|
-
function->arity = -1;
|
|
1223
|
-
function->properties = NULL;
|
|
1224
|
-
function->environment = NULL;
|
|
1225
|
-
if (env_count > 0) {
|
|
1226
|
-
function->environment = (xt_value *)malloc(sizeof(xt_value) * (size_t)env_count);
|
|
1227
|
-
for (int32_t i = 0; i < env_count; i++) function->environment[i] = env[i];
|
|
1228
|
-
}
|
|
1229
|
-
return XT_FROM_PTR(XT_TAG_FUNCTION, function);
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
xt_value xt_closure_call(xt_value value, int32_t argc, xt_value *argv) {
|
|
1233
|
-
if (!XT_IS_FUNCTION(value)) {
|
|
1234
|
-
xt_throw(xt_string_from_cstr("TypeError: value is not a function"));
|
|
1235
|
-
return XT_UNDEFINED;
|
|
1236
|
-
}
|
|
1237
|
-
xt_function *function = (xt_function *)XT_GET_PTR(value);
|
|
1238
|
-
/* The closure value itself is threaded through as `env` so the callee can
|
|
1239
|
-
* read its captures with xt_closure_env(env, i). */
|
|
1240
|
-
return function->code(value, argc, argv);
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
xt_value xt_closure_env(xt_value value, int32_t index) {
|
|
1244
|
-
if (!XT_IS_FUNCTION(value)) return XT_UNDEFINED;
|
|
1245
|
-
xt_function *function = (xt_function *)XT_GET_PTR(value);
|
|
1246
|
-
if (index < 0 || index >= function->environment_count) return XT_UNDEFINED;
|
|
1247
|
-
return function->environment[index];
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
int32_t xt_closure_arity(xt_value value) {
|
|
1251
|
-
if (!XT_IS_FUNCTION(value)) return -1;
|
|
1252
|
-
return ((xt_function *)XT_GET_PTR(value))->arity;
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
/* ------------------------------------------------------------------------- */
|
|
1256
|
-
/* Exceptions and output */
|
|
1257
|
-
/* ------------------------------------------------------------------------- */
|
|
1258
|
-
|
|
1259
|
-
/*
|
|
1260
|
-
* `try`/`catch` is implemented with a stack of setjmp frames. The compiler
|
|
1261
|
-
* allocates a frame with `xt_try_enter`, calls `_setjmp` on it, and pops it
|
|
1262
|
-
* with `xt_try_leave`. `xt_throw` longjmps into the innermost frame when one
|
|
1263
|
-
* is active, and only prints + exits when the exception is uncaught.
|
|
1264
|
-
*/
|
|
1265
|
-
typedef struct xt_try_frame {
|
|
1266
|
-
jmp_buf buf;
|
|
1267
|
-
struct xt_try_frame *prev;
|
|
1268
|
-
xt_value exception;
|
|
1269
|
-
} xt_try_frame;
|
|
1270
|
-
|
|
1271
|
-
static xt_try_frame *g_try_top = NULL;
|
|
1272
|
-
|
|
1273
|
-
void *xt_try_enter(void) {
|
|
1274
|
-
xt_try_frame *frame = (xt_try_frame *)calloc(1, sizeof(xt_try_frame));
|
|
1275
|
-
if (!frame) abort();
|
|
1276
|
-
frame->prev = g_try_top;
|
|
1277
|
-
frame->exception = XT_UNDEFINED;
|
|
1278
|
-
g_try_top = frame;
|
|
1279
|
-
return (void *)frame;
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
xt_value xt_try_exception(void *framePtr) {
|
|
1283
|
-
xt_try_frame *frame = (xt_try_frame *)framePtr;
|
|
1284
|
-
return frame ? frame->exception : XT_UNDEFINED;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
void xt_try_leave(void *framePtr) {
|
|
1288
|
-
xt_try_frame *frame = (xt_try_frame *)framePtr;
|
|
1289
|
-
if (frame && g_try_top == frame) g_try_top = frame->prev;
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
void xt_throw(xt_value v) {
|
|
1293
|
-
if (g_try_top) {
|
|
1294
|
-
g_try_top->exception = v;
|
|
1295
|
-
_longjmp(g_try_top->buf, 1);
|
|
1296
|
-
}
|
|
1297
|
-
xt_value message = xt_to_string(v);
|
|
1298
|
-
xt_string *s = xt_as_string(message);
|
|
1299
|
-
fprintf(stderr, "Uncaught %s\n", s->data);
|
|
1300
|
-
exit(1);
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
static void xt_print_value(xt_value v, FILE *out) {
|
|
1304
|
-
if (XT_IS_STRING(v)) {
|
|
1305
|
-
xt_string *s = xt_as_string(v);
|
|
1306
|
-
fwrite(s->data, 1, s->length, out);
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
xt_value text = xt_to_string(v);
|
|
1310
|
-
xt_string *s = xt_as_string(text);
|
|
1311
|
-
fwrite(s->data, 1, s->length, out);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
void xt_print(xt_value v) { xt_print_value(v, stdout); }
|
|
1315
|
-
void xt_println(xt_value v) {
|
|
1316
|
-
xt_print_value(v, stdout);
|
|
1317
|
-
fputc('\n', stdout);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
/*
|
|
1321
|
-
* console.log applies a Node-like inspection format: strings unquoted at the
|
|
1322
|
-
* top level, arrays as `[ a, b ]` and objects as `{ key: value }`.
|
|
1323
|
-
*/
|
|
1324
|
-
static void xt_inspect(xt_value v, FILE *out) {
|
|
1325
|
-
if (XT_IS_ARRAY(v)) {
|
|
1326
|
-
xt_array *array = (xt_array *)XT_GET_PTR(v);
|
|
1327
|
-
fputc('[', out);
|
|
1328
|
-
for (uint32_t i = 0; i < array->length; i++) {
|
|
1329
|
-
if (i > 0) fputs(", ", out);
|
|
1330
|
-
else fputc(' ', out);
|
|
1331
|
-
xt_inspect(array->items[i], out);
|
|
1332
|
-
}
|
|
1333
|
-
if (array->length > 0) fputc(' ', out);
|
|
1334
|
-
fputc(']', out);
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
if (XT_IS_OBJECT(v)) {
|
|
1338
|
-
xt_object *obj = (xt_object *)XT_GET_PTR(v);
|
|
1339
|
-
fputc('{', out);
|
|
1340
|
-
for (uint32_t i = 0; i < obj->count; i++) {
|
|
1341
|
-
if (i > 0) fputs(", ", out);
|
|
1342
|
-
else fputc(' ', out);
|
|
1343
|
-
fwrite(obj->properties[i].key->data, 1, obj->properties[i].key->length, out);
|
|
1344
|
-
fputs(": ", out);
|
|
1345
|
-
xt_inspect(obj->properties[i].value, out);
|
|
1346
|
-
}
|
|
1347
|
-
if (obj->count > 0) fputc(' ', out);
|
|
1348
|
-
fputc('}', out);
|
|
1349
|
-
return;
|
|
1350
|
-
}
|
|
1351
|
-
xt_print_value(v, out);
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
void xt_console_log(int32_t argc, xt_value *argv) {
|
|
1355
|
-
for (int32_t i = 0; i < argc; i++) {
|
|
1356
|
-
if (i > 0) fputc(' ', stdout);
|
|
1357
|
-
xt_inspect(argv[i], stdout);
|
|
1358
|
-
}
|
|
1359
|
-
fputc('\n', stdout);
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
static void xt_console_write(int32_t argc, xt_value *argv, FILE *out) {
|
|
1363
|
-
for (int32_t i = 0; i < argc; i++) {
|
|
1364
|
-
if (i > 0) fputc(' ', out);
|
|
1365
|
-
xt_inspect(argv[i], out);
|
|
1366
|
-
}
|
|
1367
|
-
fputc('\n', out);
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
void xt_console_info(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stdout); }
|
|
1371
|
-
void xt_console_warn(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stderr); }
|
|
1372
|
-
void xt_console_error(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stderr); }
|