@rezless/vm 0.1.0
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/LICENSE +202 -0
- package/NOTICE +32 -0
- package/dist/builtins/base64.d.ts +25 -0
- package/dist/builtins/base64.d.ts.map +1 -0
- package/dist/builtins/base64.js +43 -0
- package/dist/builtins/base64.js.map +1 -0
- package/dist/builtins/chat.d.ts +10 -0
- package/dist/builtins/chat.d.ts.map +1 -0
- package/dist/builtins/chat.js +36 -0
- package/dist/builtins/chat.js.map +1 -0
- package/dist/builtins/dataserver.d.ts +26 -0
- package/dist/builtins/dataserver.d.ts.map +1 -0
- package/dist/builtins/dataserver.js +20 -0
- package/dist/builtins/dataserver.js.map +1 -0
- package/dist/builtins/detected.d.ts +40 -0
- package/dist/builtins/detected.d.ts.map +1 -0
- package/dist/builtins/detected.js +23 -0
- package/dist/builtins/detected.js.map +1 -0
- package/dist/builtins/hash.d.ts +6 -0
- package/dist/builtins/hash.d.ts.map +1 -0
- package/dist/builtins/hash.js +34 -0
- package/dist/builtins/hash.js.map +1 -0
- package/dist/builtins/http.d.ts +40 -0
- package/dist/builtins/http.d.ts.map +1 -0
- package/dist/builtins/http.js +87 -0
- package/dist/builtins/http.js.map +1 -0
- package/dist/builtins/identity.d.ts +14 -0
- package/dist/builtins/identity.d.ts.map +1 -0
- package/dist/builtins/identity.js +17 -0
- package/dist/builtins/identity.js.map +1 -0
- package/dist/builtins/index.d.ts +8 -0
- package/dist/builtins/index.d.ts.map +1 -0
- package/dist/builtins/index.js +232 -0
- package/dist/builtins/index.js.map +1 -0
- package/dist/builtins/inventory.d.ts +33 -0
- package/dist/builtins/inventory.d.ts.map +1 -0
- package/dist/builtins/inventory.js +134 -0
- package/dist/builtins/inventory.js.map +1 -0
- package/dist/builtins/json.d.ts +7 -0
- package/dist/builtins/json.d.ts.map +1 -0
- package/dist/builtins/json.js +690 -0
- package/dist/builtins/json.js.map +1 -0
- package/dist/builtins/link-info.d.ts +10 -0
- package/dist/builtins/link-info.d.ts.map +1 -0
- package/dist/builtins/link-info.js +26 -0
- package/dist/builtins/link-info.js.map +1 -0
- package/dist/builtins/linked.d.ts +23 -0
- package/dist/builtins/linked.d.ts.map +1 -0
- package/dist/builtins/linked.js +20 -0
- package/dist/builtins/linked.js.map +1 -0
- package/dist/builtins/linksetdata.d.ts +20 -0
- package/dist/builtins/linksetdata.d.ts.map +1 -0
- package/dist/builtins/linksetdata.js +196 -0
- package/dist/builtins/linksetdata.js.map +1 -0
- package/dist/builtins/listen.d.ts +33 -0
- package/dist/builtins/listen.d.ts.map +1 -0
- package/dist/builtins/listen.js +45 -0
- package/dist/builtins/listen.js.map +1 -0
- package/dist/builtins/lists.d.ts +37 -0
- package/dist/builtins/lists.d.ts.map +1 -0
- package/dist/builtins/lists.js +275 -0
- package/dist/builtins/lists.js.map +1 -0
- package/dist/builtins/math.d.ts +32 -0
- package/dist/builtins/math.d.ts.map +1 -0
- package/dist/builtins/math.js +104 -0
- package/dist/builtins/math.js.map +1 -0
- package/dist/builtins/object.d.ts +29 -0
- package/dist/builtins/object.d.ts.map +1 -0
- package/dist/builtins/object.js +50 -0
- package/dist/builtins/object.js.map +1 -0
- package/dist/builtins/prim-accessors.d.ts +56 -0
- package/dist/builtins/prim-accessors.d.ts.map +1 -0
- package/dist/builtins/prim-accessors.js +353 -0
- package/dist/builtins/prim-accessors.js.map +1 -0
- package/dist/builtins/prim-params.d.ts +12 -0
- package/dist/builtins/prim-params.d.ts.map +1 -0
- package/dist/builtins/prim-params.js +124 -0
- package/dist/builtins/prim-params.js.map +1 -0
- package/dist/builtins/script-control.d.ts +13 -0
- package/dist/builtins/script-control.d.ts.map +1 -0
- package/dist/builtins/script-control.js +41 -0
- package/dist/builtins/script-control.js.map +1 -0
- package/dist/builtins/strings.d.ts +23 -0
- package/dist/builtins/strings.d.ts.map +1 -0
- package/dist/builtins/strings.js +130 -0
- package/dist/builtins/strings.js.map +1 -0
- package/dist/builtins/time.d.ts +28 -0
- package/dist/builtins/time.d.ts.map +1 -0
- package/dist/builtins/time.js +54 -0
- package/dist/builtins/time.js.map +1 -0
- package/dist/clock.d.ts +75 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +125 -0
- package/dist/clock.js.map +1 -0
- package/dist/coverage.d.ts +110 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +221 -0
- package/dist/coverage.js.map +1 -0
- package/dist/dispatch.d.ts +19 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +42 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/env.d.ts +33 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +76 -0
- package/dist/env.js.map +1 -0
- package/dist/generated/constants.d.ts +967 -0
- package/dist/generated/constants.d.ts.map +1 -0
- package/dist/generated/constants.js +969 -0
- package/dist/generated/constants.js.map +1 -0
- package/dist/generated/constants_table.d.ts +11 -0
- package/dist/generated/constants_table.d.ts.map +1 -0
- package/dist/generated/constants_table.js +978 -0
- package/dist/generated/constants_table.js.map +1 -0
- package/dist/generated/events.d.ts +545 -0
- package/dist/generated/events.d.ts.map +1 -0
- package/dist/generated/events.js +49 -0
- package/dist/generated/events.js.map +1 -0
- package/dist/generated/functions.d.ts +6051 -0
- package/dist/generated/functions.d.ts.map +1 -0
- package/dist/generated/functions.js +526 -0
- package/dist/generated/functions.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/interpreter.d.ts +39 -0
- package/dist/interpreter.d.ts.map +1 -0
- package/dist/interpreter.js +381 -0
- package/dist/interpreter.js.map +1 -0
- package/dist/inventory.d.ts +52 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +45 -0
- package/dist/inventory.js.map +1 -0
- package/dist/linkset.d.ts +89 -0
- package/dist/linkset.d.ts.map +1 -0
- package/dist/linkset.js +179 -0
- package/dist/linkset.js.map +1 -0
- package/dist/prim-params.d.ts +221 -0
- package/dist/prim-params.d.ts.map +1 -0
- package/dist/prim-params.js +852 -0
- package/dist/prim-params.js.map +1 -0
- package/dist/prim.d.ts +79 -0
- package/dist/prim.d.ts.map +1 -0
- package/dist/prim.js +96 -0
- package/dist/prim.js.map +1 -0
- package/dist/random.d.ts +16 -0
- package/dist/random.d.ts.map +1 -0
- package/dist/random.js +29 -0
- package/dist/random.js.map +1 -0
- package/dist/runtime.d.ts +108 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +1 -0
- package/dist/script.d.ts +248 -0
- package/dist/script.d.ts.map +1 -0
- package/dist/script.js +667 -0
- package/dist/script.js.map +1 -0
- package/dist/values/coerce.d.ts +22 -0
- package/dist/values/coerce.d.ts.map +1 -0
- package/dist/values/coerce.js +184 -0
- package/dist/values/coerce.js.map +1 -0
- package/dist/values/format.d.ts +20 -0
- package/dist/values/format.d.ts.map +1 -0
- package/dist/values/format.js +47 -0
- package/dist/values/format.js.map +1 -0
- package/dist/values/ops.d.ts +19 -0
- package/dist/values/ops.d.ts.map +1 -0
- package/dist/values/ops.js +345 -0
- package/dist/values/ops.js.map +1 -0
- package/dist/values/types.d.ts +39 -0
- package/dist/values/types.d.ts.map +1 -0
- package/dist/values/types.js +68 -0
- package/dist/values/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
import { isVector, isRotation } from '../values/types.js';
|
|
2
|
+
import { JSON_INVALID, JSON_OBJECT, JSON_ARRAY, JSON_NUMBER, JSON_STRING, JSON_NULL, JSON_TRUE, JSON_FALSE, JSON_APPEND, JSON_DELETE, } from '../generated/constants.js';
|
|
3
|
+
const SENTINEL_STRINGS = new Set([
|
|
4
|
+
JSON_INVALID,
|
|
5
|
+
JSON_OBJECT,
|
|
6
|
+
JSON_ARRAY,
|
|
7
|
+
JSON_NUMBER,
|
|
8
|
+
JSON_STRING,
|
|
9
|
+
JSON_NULL,
|
|
10
|
+
JSON_TRUE,
|
|
11
|
+
JSON_FALSE,
|
|
12
|
+
JSON_DELETE,
|
|
13
|
+
]);
|
|
14
|
+
class Parser {
|
|
15
|
+
src;
|
|
16
|
+
i = 0;
|
|
17
|
+
constructor(src) {
|
|
18
|
+
this.src = src;
|
|
19
|
+
}
|
|
20
|
+
parseRoot() {
|
|
21
|
+
this.ws();
|
|
22
|
+
if (this.i >= this.src.length)
|
|
23
|
+
return null;
|
|
24
|
+
const v = this.parseValue();
|
|
25
|
+
if (v === null)
|
|
26
|
+
return null;
|
|
27
|
+
this.ws();
|
|
28
|
+
if (this.i !== this.src.length)
|
|
29
|
+
return null;
|
|
30
|
+
return v;
|
|
31
|
+
}
|
|
32
|
+
ws() {
|
|
33
|
+
while (this.i < this.src.length) {
|
|
34
|
+
const c = this.src[this.i];
|
|
35
|
+
if (c === ' ' || c === '\t' || c === '\n' || c === '\r')
|
|
36
|
+
this.i++;
|
|
37
|
+
else
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
parseValue() {
|
|
42
|
+
this.ws();
|
|
43
|
+
if (this.i >= this.src.length)
|
|
44
|
+
return null;
|
|
45
|
+
const c = this.src[this.i];
|
|
46
|
+
if (c === '{')
|
|
47
|
+
return this.parseObject();
|
|
48
|
+
if (c === '[')
|
|
49
|
+
return this.parseArray();
|
|
50
|
+
if (c === '"')
|
|
51
|
+
return this.parseString();
|
|
52
|
+
if (c === 't' && this.src.startsWith('true', this.i)) {
|
|
53
|
+
this.i += 4;
|
|
54
|
+
return { t: 'true' };
|
|
55
|
+
}
|
|
56
|
+
if (c === 'f' && this.src.startsWith('false', this.i)) {
|
|
57
|
+
this.i += 5;
|
|
58
|
+
return { t: 'false' };
|
|
59
|
+
}
|
|
60
|
+
if (c === 'n' && this.src.startsWith('null', this.i)) {
|
|
61
|
+
this.i += 4;
|
|
62
|
+
return { t: 'null' };
|
|
63
|
+
}
|
|
64
|
+
if (c === '-' || (c >= '0' && c <= '9'))
|
|
65
|
+
return this.parseNumber();
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
parseObject() {
|
|
69
|
+
this.i++;
|
|
70
|
+
const entries = [];
|
|
71
|
+
this.ws();
|
|
72
|
+
if (this.src[this.i] === '}') {
|
|
73
|
+
this.i++;
|
|
74
|
+
return { t: 'obj', entries };
|
|
75
|
+
}
|
|
76
|
+
while (true) {
|
|
77
|
+
this.ws();
|
|
78
|
+
if (this.src[this.i] !== '"')
|
|
79
|
+
return null;
|
|
80
|
+
const k = this.parseString();
|
|
81
|
+
if (k === null || k.t !== 'str')
|
|
82
|
+
return null;
|
|
83
|
+
this.ws();
|
|
84
|
+
if (this.src[this.i] !== ':')
|
|
85
|
+
return null;
|
|
86
|
+
this.i++;
|
|
87
|
+
this.ws();
|
|
88
|
+
const nc = this.src[this.i];
|
|
89
|
+
let v;
|
|
90
|
+
if (nc === ',' || nc === '}') {
|
|
91
|
+
v = { t: 'empty' };
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const parsed = this.parseValue();
|
|
95
|
+
if (parsed === null)
|
|
96
|
+
return null;
|
|
97
|
+
v = parsed;
|
|
98
|
+
}
|
|
99
|
+
entries.push([k.v, v]);
|
|
100
|
+
this.ws();
|
|
101
|
+
if (this.src[this.i] === '}') {
|
|
102
|
+
this.i++;
|
|
103
|
+
return { t: 'obj', entries };
|
|
104
|
+
}
|
|
105
|
+
if (this.src[this.i] === ',') {
|
|
106
|
+
this.i++;
|
|
107
|
+
this.ws();
|
|
108
|
+
if (this.src[this.i] === '}') {
|
|
109
|
+
this.i++;
|
|
110
|
+
return { t: 'obj', entries };
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
parseArray() {
|
|
118
|
+
this.i++;
|
|
119
|
+
const items = [];
|
|
120
|
+
this.ws();
|
|
121
|
+
if (this.src[this.i] === ']') {
|
|
122
|
+
this.i++;
|
|
123
|
+
return { t: 'arr', items };
|
|
124
|
+
}
|
|
125
|
+
while (true) {
|
|
126
|
+
this.ws();
|
|
127
|
+
const nc = this.src[this.i];
|
|
128
|
+
let v;
|
|
129
|
+
if (nc === ',' || nc === ']') {
|
|
130
|
+
v = { t: 'empty' };
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const parsed = this.parseValue();
|
|
134
|
+
if (parsed === null)
|
|
135
|
+
return null;
|
|
136
|
+
v = parsed;
|
|
137
|
+
}
|
|
138
|
+
items.push(v);
|
|
139
|
+
this.ws();
|
|
140
|
+
if (this.src[this.i] === ']') {
|
|
141
|
+
this.i++;
|
|
142
|
+
return { t: 'arr', items };
|
|
143
|
+
}
|
|
144
|
+
if (this.src[this.i] === ',') {
|
|
145
|
+
this.i++;
|
|
146
|
+
this.ws();
|
|
147
|
+
if (this.src[this.i] === ']') {
|
|
148
|
+
this.i++;
|
|
149
|
+
return { t: 'arr', items };
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
parseString() {
|
|
157
|
+
if (this.src[this.i] !== '"')
|
|
158
|
+
return null;
|
|
159
|
+
this.i++;
|
|
160
|
+
let out = '';
|
|
161
|
+
while (this.i < this.src.length) {
|
|
162
|
+
const c = this.src[this.i];
|
|
163
|
+
if (c === '"') {
|
|
164
|
+
this.i++;
|
|
165
|
+
return { t: 'str', v: out };
|
|
166
|
+
}
|
|
167
|
+
if (c === '\\') {
|
|
168
|
+
this.i++;
|
|
169
|
+
const e = this.src[this.i];
|
|
170
|
+
if (e === undefined)
|
|
171
|
+
return null;
|
|
172
|
+
this.i++;
|
|
173
|
+
if (e === '"')
|
|
174
|
+
out += '"';
|
|
175
|
+
else if (e === '\\')
|
|
176
|
+
out += '\\';
|
|
177
|
+
else if (e === '/')
|
|
178
|
+
out += '/';
|
|
179
|
+
else if (e === 'n')
|
|
180
|
+
out += '\n';
|
|
181
|
+
else if (e === 't')
|
|
182
|
+
out += '\t';
|
|
183
|
+
else if (e === 'r')
|
|
184
|
+
out += '\r';
|
|
185
|
+
else if (e === 'b')
|
|
186
|
+
out += '\b';
|
|
187
|
+
else if (e === 'f')
|
|
188
|
+
out += '\f';
|
|
189
|
+
else if (e === 'u') {
|
|
190
|
+
const hex = this.src.slice(this.i, this.i + 4);
|
|
191
|
+
if (hex.length !== 4 || !/^[0-9a-fA-F]+$/.test(hex))
|
|
192
|
+
return null;
|
|
193
|
+
this.i += 4;
|
|
194
|
+
out += String.fromCharCode(parseInt(hex, 16));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
out += e;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
out += c;
|
|
202
|
+
this.i++;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
parseNumber() {
|
|
208
|
+
const start = this.i;
|
|
209
|
+
if (this.src[this.i] === '-')
|
|
210
|
+
this.i++;
|
|
211
|
+
const digitStart = this.i;
|
|
212
|
+
while (this.i < this.src.length &&
|
|
213
|
+
this.src[this.i] >= '0' &&
|
|
214
|
+
this.src[this.i] <= '9') {
|
|
215
|
+
this.i++;
|
|
216
|
+
}
|
|
217
|
+
if (this.i === digitStart)
|
|
218
|
+
return null;
|
|
219
|
+
if (this.src[this.i] === '.') {
|
|
220
|
+
this.i++;
|
|
221
|
+
while (this.i < this.src.length &&
|
|
222
|
+
this.src[this.i] >= '0' &&
|
|
223
|
+
this.src[this.i] <= '9') {
|
|
224
|
+
this.i++;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (this.src[this.i] === 'e' || this.src[this.i] === 'E') {
|
|
228
|
+
this.i++;
|
|
229
|
+
if (this.src[this.i] === '+' || this.src[this.i] === '-')
|
|
230
|
+
this.i++;
|
|
231
|
+
const expStart = this.i;
|
|
232
|
+
while (this.i < this.src.length &&
|
|
233
|
+
this.src[this.i] >= '0' &&
|
|
234
|
+
this.src[this.i] <= '9') {
|
|
235
|
+
this.i++;
|
|
236
|
+
}
|
|
237
|
+
if (this.i === expStart)
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return { t: 'num', raw: this.src.slice(start, this.i) };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function parseJson(src) {
|
|
244
|
+
return new Parser(src).parseRoot();
|
|
245
|
+
}
|
|
246
|
+
function escapeJsonString(s) {
|
|
247
|
+
let out = '';
|
|
248
|
+
for (let i = 0; i < s.length; i++) {
|
|
249
|
+
const c = s[i];
|
|
250
|
+
const code = c.charCodeAt(0);
|
|
251
|
+
if (c === '"')
|
|
252
|
+
out += '\\"';
|
|
253
|
+
else if (c === '\\')
|
|
254
|
+
out += '\\\\';
|
|
255
|
+
else if (c === '\n')
|
|
256
|
+
out += '\\n';
|
|
257
|
+
else if (c === '\r')
|
|
258
|
+
out += '\\r';
|
|
259
|
+
else if (c === '\t')
|
|
260
|
+
out += '\\t';
|
|
261
|
+
else if (c === '\b')
|
|
262
|
+
out += '\\b';
|
|
263
|
+
else if (c === '\f')
|
|
264
|
+
out += '\\f';
|
|
265
|
+
else if (code < 0x20)
|
|
266
|
+
out += '\\u' + code.toString(16).padStart(4, '0');
|
|
267
|
+
else
|
|
268
|
+
out += c;
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
function printJson(n) {
|
|
273
|
+
switch (n.t) {
|
|
274
|
+
case 'null':
|
|
275
|
+
return 'null';
|
|
276
|
+
case 'true':
|
|
277
|
+
return 'true';
|
|
278
|
+
case 'false':
|
|
279
|
+
return 'false';
|
|
280
|
+
case 'num':
|
|
281
|
+
return n.raw;
|
|
282
|
+
case 'str':
|
|
283
|
+
return '"' + escapeJsonString(n.v) + '"';
|
|
284
|
+
case 'arr':
|
|
285
|
+
return '[' + n.items.map(printJson).join(',') + ']';
|
|
286
|
+
case 'obj':
|
|
287
|
+
return ('{' +
|
|
288
|
+
n.entries
|
|
289
|
+
.map(([k, v]) => '"' + escapeJsonString(k) + '":' + printJson(v))
|
|
290
|
+
.join(',') +
|
|
291
|
+
'}');
|
|
292
|
+
case 'empty':
|
|
293
|
+
return 'null';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function walk(root, specs) {
|
|
297
|
+
let cur = root;
|
|
298
|
+
for (const spec of specs) {
|
|
299
|
+
if (cur.t === 'empty')
|
|
300
|
+
return { kind: 'null' };
|
|
301
|
+
if (typeof spec === 'string') {
|
|
302
|
+
if (cur.t !== 'obj')
|
|
303
|
+
return { kind: 'invalid' };
|
|
304
|
+
let found = null;
|
|
305
|
+
for (const [k, v] of cur.entries)
|
|
306
|
+
if (k === spec)
|
|
307
|
+
found = v;
|
|
308
|
+
if (found === null)
|
|
309
|
+
return { kind: 'invalid' };
|
|
310
|
+
cur = found;
|
|
311
|
+
}
|
|
312
|
+
else if (typeof spec === 'number') {
|
|
313
|
+
if (cur.t !== 'arr')
|
|
314
|
+
return { kind: 'invalid' };
|
|
315
|
+
const idx = Math.trunc(spec);
|
|
316
|
+
if (idx < 0 || idx >= cur.items.length)
|
|
317
|
+
return { kind: 'invalid' };
|
|
318
|
+
cur = cur.items[idx];
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
return { kind: 'invalid' };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (cur.t === 'empty')
|
|
325
|
+
return { kind: 'null' };
|
|
326
|
+
return { kind: 'node', node: cur };
|
|
327
|
+
}
|
|
328
|
+
function nodeToGetString(n) {
|
|
329
|
+
switch (n.t) {
|
|
330
|
+
case 'null':
|
|
331
|
+
return JSON_NULL;
|
|
332
|
+
case 'true':
|
|
333
|
+
return JSON_TRUE;
|
|
334
|
+
case 'false':
|
|
335
|
+
return JSON_FALSE;
|
|
336
|
+
case 'num':
|
|
337
|
+
return n.raw;
|
|
338
|
+
case 'str':
|
|
339
|
+
return n.v;
|
|
340
|
+
case 'arr':
|
|
341
|
+
case 'obj':
|
|
342
|
+
return printJson(n);
|
|
343
|
+
case 'empty':
|
|
344
|
+
return JSON_NULL;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function nodeType(n) {
|
|
348
|
+
switch (n.t) {
|
|
349
|
+
case 'null':
|
|
350
|
+
return JSON_NULL;
|
|
351
|
+
case 'true':
|
|
352
|
+
return JSON_TRUE;
|
|
353
|
+
case 'false':
|
|
354
|
+
return JSON_FALSE;
|
|
355
|
+
case 'num':
|
|
356
|
+
return JSON_NUMBER;
|
|
357
|
+
case 'str':
|
|
358
|
+
return JSON_STRING;
|
|
359
|
+
case 'arr':
|
|
360
|
+
return JSON_ARRAY;
|
|
361
|
+
case 'obj':
|
|
362
|
+
return JSON_OBJECT;
|
|
363
|
+
case 'empty':
|
|
364
|
+
return JSON_NULL;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Convert an LSL string `value` argument into a JNode (or the 'delete'
|
|
368
|
+
// sentinel). Mirrors the documented LSL coercion rules from the wiki:
|
|
369
|
+
// FDDx sentinels become bare-word JSON; bare "true"/"false"/"null"
|
|
370
|
+
// become bare-word JSON; literally double-quoted strings, JSON numbers,
|
|
371
|
+
// and JSON object/array literals are parsed and embedded; everything
|
|
372
|
+
// else becomes a JSON string.
|
|
373
|
+
function valueToNode(value) {
|
|
374
|
+
if (value === JSON_DELETE)
|
|
375
|
+
return 'delete';
|
|
376
|
+
if (value === JSON_NULL)
|
|
377
|
+
return { t: 'null' };
|
|
378
|
+
if (value === JSON_TRUE)
|
|
379
|
+
return { t: 'true' };
|
|
380
|
+
if (value === JSON_FALSE)
|
|
381
|
+
return { t: 'false' };
|
|
382
|
+
const trimmed = value.trim();
|
|
383
|
+
if (trimmed === 'true')
|
|
384
|
+
return { t: 'true' };
|
|
385
|
+
if (trimmed === 'false')
|
|
386
|
+
return { t: 'false' };
|
|
387
|
+
if (trimmed === 'null')
|
|
388
|
+
return { t: 'null' };
|
|
389
|
+
if (trimmed.length > 0) {
|
|
390
|
+
const ch = trimmed[0];
|
|
391
|
+
if (ch === '{' ||
|
|
392
|
+
ch === '[' ||
|
|
393
|
+
ch === '"' ||
|
|
394
|
+
ch === '-' ||
|
|
395
|
+
(ch >= '0' && ch <= '9')) {
|
|
396
|
+
const parsed = parseJson(trimmed);
|
|
397
|
+
if (parsed !== null)
|
|
398
|
+
return parsed;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return { t: 'str', v: value };
|
|
402
|
+
}
|
|
403
|
+
function collapseDuplicateKeys(entries) {
|
|
404
|
+
// Wiki: any modification to an object collapses duplicate keys, keeping
|
|
405
|
+
// only the LAST occurrence (in its position).
|
|
406
|
+
const seen = new Set();
|
|
407
|
+
const reversed = [];
|
|
408
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
409
|
+
const [k, v] = entries[i];
|
|
410
|
+
if (!seen.has(k)) {
|
|
411
|
+
seen.add(k);
|
|
412
|
+
reversed.push([k, v]);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return reversed.reverse();
|
|
416
|
+
}
|
|
417
|
+
function setIn(root, specs, value) {
|
|
418
|
+
if (specs.length === 0) {
|
|
419
|
+
if (value === 'delete')
|
|
420
|
+
return { ok: false };
|
|
421
|
+
return { ok: true, node: value };
|
|
422
|
+
}
|
|
423
|
+
const head = specs[0];
|
|
424
|
+
const rest = specs.slice(1);
|
|
425
|
+
if (typeof head === 'number') {
|
|
426
|
+
const idx = Math.trunc(head);
|
|
427
|
+
const arr = root && root.t === 'arr'
|
|
428
|
+
? { t: 'arr', items: [...root.items] }
|
|
429
|
+
: { t: 'arr', items: [] };
|
|
430
|
+
if (idx === JSON_APPEND) {
|
|
431
|
+
if (rest.length === 0) {
|
|
432
|
+
if (value === 'delete')
|
|
433
|
+
return { ok: false };
|
|
434
|
+
arr.items.push(value);
|
|
435
|
+
return { ok: true, node: arr };
|
|
436
|
+
}
|
|
437
|
+
const sub = setIn(null, rest, value);
|
|
438
|
+
if (!sub.ok)
|
|
439
|
+
return { ok: false };
|
|
440
|
+
arr.items.push(sub.node);
|
|
441
|
+
return { ok: true, node: arr };
|
|
442
|
+
}
|
|
443
|
+
if (idx < 0)
|
|
444
|
+
return { ok: false };
|
|
445
|
+
if (idx > arr.items.length)
|
|
446
|
+
return { ok: false };
|
|
447
|
+
if (idx === arr.items.length) {
|
|
448
|
+
if (rest.length === 0) {
|
|
449
|
+
if (value === 'delete')
|
|
450
|
+
return { ok: true, node: arr };
|
|
451
|
+
arr.items.push(value);
|
|
452
|
+
return { ok: true, node: arr };
|
|
453
|
+
}
|
|
454
|
+
const sub = setIn(null, rest, value);
|
|
455
|
+
if (!sub.ok)
|
|
456
|
+
return { ok: false };
|
|
457
|
+
arr.items.push(sub.node);
|
|
458
|
+
return { ok: true, node: arr };
|
|
459
|
+
}
|
|
460
|
+
if (rest.length === 0) {
|
|
461
|
+
if (value === 'delete') {
|
|
462
|
+
// Cannot delete from arrays per wiki — return invalid.
|
|
463
|
+
return { ok: false };
|
|
464
|
+
}
|
|
465
|
+
arr.items[idx] = value;
|
|
466
|
+
return { ok: true, node: arr };
|
|
467
|
+
}
|
|
468
|
+
const sub = setIn(arr.items[idx], rest, value);
|
|
469
|
+
if (!sub.ok)
|
|
470
|
+
return { ok: false };
|
|
471
|
+
arr.items[idx] = sub.node;
|
|
472
|
+
return { ok: true, node: arr };
|
|
473
|
+
}
|
|
474
|
+
if (typeof head === 'string') {
|
|
475
|
+
const obj = root && root.t === 'obj'
|
|
476
|
+
? { t: 'obj', entries: collapseDuplicateKeys(root.entries) }
|
|
477
|
+
: { t: 'obj', entries: [] };
|
|
478
|
+
const existingIdx = obj.entries.findIndex(([k]) => k === head);
|
|
479
|
+
if (rest.length === 0) {
|
|
480
|
+
if (value === 'delete') {
|
|
481
|
+
if (existingIdx >= 0)
|
|
482
|
+
obj.entries.splice(existingIdx, 1);
|
|
483
|
+
return { ok: true, node: obj };
|
|
484
|
+
}
|
|
485
|
+
if (existingIdx >= 0)
|
|
486
|
+
obj.entries[existingIdx] = [head, value];
|
|
487
|
+
else
|
|
488
|
+
obj.entries.push([head, value]);
|
|
489
|
+
return { ok: true, node: obj };
|
|
490
|
+
}
|
|
491
|
+
const existing = existingIdx >= 0 ? obj.entries[existingIdx][1] : null;
|
|
492
|
+
const sub = setIn(existing, rest, value);
|
|
493
|
+
if (!sub.ok)
|
|
494
|
+
return { ok: false };
|
|
495
|
+
if (existingIdx >= 0)
|
|
496
|
+
obj.entries[existingIdx] = [head, sub.node];
|
|
497
|
+
else
|
|
498
|
+
obj.entries.push([head, sub.node]);
|
|
499
|
+
return { ok: true, node: obj };
|
|
500
|
+
}
|
|
501
|
+
return { ok: false };
|
|
502
|
+
}
|
|
503
|
+
function jsonNodeToLslListItem(n) {
|
|
504
|
+
if (n.t === 'arr' || n.t === 'obj')
|
|
505
|
+
return printJson(n);
|
|
506
|
+
switch (n.t) {
|
|
507
|
+
case 'null':
|
|
508
|
+
return JSON_NULL;
|
|
509
|
+
case 'true':
|
|
510
|
+
return JSON_TRUE;
|
|
511
|
+
case 'false':
|
|
512
|
+
return JSON_FALSE;
|
|
513
|
+
case 'num':
|
|
514
|
+
// Integer literal → LSL integer; otherwise → LSL float (which the
|
|
515
|
+
// LSL list type will canonicalize to 6 decimals when stringified).
|
|
516
|
+
if (/^-?\d+$/.test(n.raw)) {
|
|
517
|
+
const x = Number(n.raw);
|
|
518
|
+
if (Number.isFinite(x))
|
|
519
|
+
return x | 0;
|
|
520
|
+
}
|
|
521
|
+
return Number(n.raw);
|
|
522
|
+
case 'str':
|
|
523
|
+
return n.v;
|
|
524
|
+
case 'empty':
|
|
525
|
+
return JSON_NULL;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
export const llJson2List = (_ctx, args) => {
|
|
529
|
+
const src = args[0] ?? '';
|
|
530
|
+
if (src === '')
|
|
531
|
+
return [];
|
|
532
|
+
const root = parseJson(src);
|
|
533
|
+
if (root === null)
|
|
534
|
+
return [src];
|
|
535
|
+
if (root.t === 'arr')
|
|
536
|
+
return root.items.map(jsonNodeToLslListItem);
|
|
537
|
+
if (root.t === 'obj') {
|
|
538
|
+
const out = [];
|
|
539
|
+
for (const [k, v] of root.entries) {
|
|
540
|
+
out.push(k, jsonNodeToLslListItem(v));
|
|
541
|
+
}
|
|
542
|
+
return out;
|
|
543
|
+
}
|
|
544
|
+
return [jsonNodeToLslListItem(root)];
|
|
545
|
+
};
|
|
546
|
+
export const llJsonGetValue = (_ctx, args) => {
|
|
547
|
+
const json = args[0] ?? '';
|
|
548
|
+
const specs = args[1] ?? [];
|
|
549
|
+
if (json === '')
|
|
550
|
+
return JSON_INVALID;
|
|
551
|
+
if (SENTINEL_STRINGS.has(json))
|
|
552
|
+
return JSON_INVALID;
|
|
553
|
+
const root = parseJson(json);
|
|
554
|
+
if (root === null)
|
|
555
|
+
return JSON_INVALID;
|
|
556
|
+
const r = walk(root, specs);
|
|
557
|
+
if (r.kind === 'invalid')
|
|
558
|
+
return JSON_INVALID;
|
|
559
|
+
if (r.kind === 'null')
|
|
560
|
+
return JSON_NULL;
|
|
561
|
+
return nodeToGetString(r.node);
|
|
562
|
+
};
|
|
563
|
+
export const llJsonValueType = (_ctx, args) => {
|
|
564
|
+
const json = args[0] ?? '';
|
|
565
|
+
const specs = args[1] ?? [];
|
|
566
|
+
if (json === '')
|
|
567
|
+
return JSON_INVALID;
|
|
568
|
+
if (SENTINEL_STRINGS.has(json))
|
|
569
|
+
return JSON_INVALID;
|
|
570
|
+
const root = parseJson(json);
|
|
571
|
+
if (root === null)
|
|
572
|
+
return JSON_INVALID;
|
|
573
|
+
const r = walk(root, specs);
|
|
574
|
+
if (r.kind === 'invalid')
|
|
575
|
+
return JSON_INVALID;
|
|
576
|
+
if (r.kind === 'null')
|
|
577
|
+
return JSON_NULL;
|
|
578
|
+
return nodeType(r.node);
|
|
579
|
+
};
|
|
580
|
+
export const llJsonSetValue = (_ctx, args) => {
|
|
581
|
+
const json = args[0] ?? '';
|
|
582
|
+
const specs = args[1] ?? [];
|
|
583
|
+
const value = args[2] ?? '';
|
|
584
|
+
const root = json === '' || SENTINEL_STRINGS.has(json) ? null : parseJson(json);
|
|
585
|
+
const valueNode = valueToNode(value);
|
|
586
|
+
const r = setIn(root, specs, valueNode);
|
|
587
|
+
if (!r.ok)
|
|
588
|
+
return JSON_INVALID;
|
|
589
|
+
return printJson(r.node);
|
|
590
|
+
};
|
|
591
|
+
function lslStringToJsonElement(raw) {
|
|
592
|
+
// Wiki-documented coercion: trim whitespace, then check (in order):
|
|
593
|
+
// FDDx sentinels → bare word
|
|
594
|
+
// already-quoted → emit verbatim
|
|
595
|
+
// bare-word names → bare word
|
|
596
|
+
// valid JSON object/array → emit verbatim (kept-as-is, allows nesting)
|
|
597
|
+
// else → JSON-quoted string
|
|
598
|
+
const trimmed = raw.trim();
|
|
599
|
+
if (trimmed === JSON_TRUE)
|
|
600
|
+
return 'true';
|
|
601
|
+
if (trimmed === JSON_FALSE)
|
|
602
|
+
return 'false';
|
|
603
|
+
if (trimmed === JSON_NULL)
|
|
604
|
+
return 'null';
|
|
605
|
+
if (trimmed.length >= 2 &&
|
|
606
|
+
trimmed.startsWith('"') &&
|
|
607
|
+
trimmed.endsWith('"')) {
|
|
608
|
+
return trimmed;
|
|
609
|
+
}
|
|
610
|
+
if (trimmed === 'true' || trimmed === 'false' || trimmed === 'null') {
|
|
611
|
+
return trimmed;
|
|
612
|
+
}
|
|
613
|
+
if (trimmed.length > 0) {
|
|
614
|
+
const ch = trimmed[0];
|
|
615
|
+
if (ch === '{' || ch === '[' || ch === '-' || (ch >= '0' && ch <= '9')) {
|
|
616
|
+
const parsed = parseJson(trimmed);
|
|
617
|
+
if (parsed !== null)
|
|
618
|
+
return trimmed;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return '"' + escapeJsonString(trimmed) + '"';
|
|
622
|
+
}
|
|
623
|
+
function lslValueToJsonElement(v) {
|
|
624
|
+
if (typeof v === 'number') {
|
|
625
|
+
if (!Number.isFinite(v))
|
|
626
|
+
return null;
|
|
627
|
+
if (Number.isInteger(v))
|
|
628
|
+
return String(v | 0);
|
|
629
|
+
return v.toFixed(6);
|
|
630
|
+
}
|
|
631
|
+
if (typeof v === 'string')
|
|
632
|
+
return lslStringToJsonElement(v);
|
|
633
|
+
if (Array.isArray(v)) {
|
|
634
|
+
const parts = v.map(lslValueToJsonElement);
|
|
635
|
+
if (parts.some((p) => p === null))
|
|
636
|
+
return null;
|
|
637
|
+
return '[' + parts.join(',') + ']';
|
|
638
|
+
}
|
|
639
|
+
if (isVector(v)) {
|
|
640
|
+
const u = v;
|
|
641
|
+
return ('"<' +
|
|
642
|
+
u.x.toFixed(5) +
|
|
643
|
+
', ' +
|
|
644
|
+
u.y.toFixed(5) +
|
|
645
|
+
', ' +
|
|
646
|
+
u.z.toFixed(5) +
|
|
647
|
+
'>"');
|
|
648
|
+
}
|
|
649
|
+
if (isRotation(v)) {
|
|
650
|
+
const r = v;
|
|
651
|
+
return ('"<' +
|
|
652
|
+
r.x.toFixed(5) +
|
|
653
|
+
', ' +
|
|
654
|
+
r.y.toFixed(5) +
|
|
655
|
+
', ' +
|
|
656
|
+
r.z.toFixed(5) +
|
|
657
|
+
', ' +
|
|
658
|
+
r.s.toFixed(5) +
|
|
659
|
+
'>"');
|
|
660
|
+
}
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
export const llList2Json = (_ctx, args) => {
|
|
664
|
+
const type = args[0] ?? '';
|
|
665
|
+
const values = args[1] ?? [];
|
|
666
|
+
if (type === JSON_ARRAY) {
|
|
667
|
+
const parts = values.map(lslValueToJsonElement);
|
|
668
|
+
if (parts.some((p) => p === null))
|
|
669
|
+
return JSON_INVALID;
|
|
670
|
+
return '[' + parts.join(',') + ']';
|
|
671
|
+
}
|
|
672
|
+
if (type === JSON_OBJECT) {
|
|
673
|
+
if (values.length % 2 !== 0)
|
|
674
|
+
return JSON_INVALID;
|
|
675
|
+
const parts = [];
|
|
676
|
+
for (let i = 0; i < values.length; i += 2) {
|
|
677
|
+
const k = values[i];
|
|
678
|
+
const v = values[i + 1];
|
|
679
|
+
if (typeof k !== 'string')
|
|
680
|
+
return JSON_INVALID;
|
|
681
|
+
const ve = lslValueToJsonElement(v);
|
|
682
|
+
if (ve === null)
|
|
683
|
+
return JSON_INVALID;
|
|
684
|
+
parts.push('"' + escapeJsonString(k) + '":' + ve);
|
|
685
|
+
}
|
|
686
|
+
return '{' + parts.join(',') + '}';
|
|
687
|
+
}
|
|
688
|
+
return JSON_INVALID;
|
|
689
|
+
};
|
|
690
|
+
//# sourceMappingURL=json.js.map
|