@xom11/whiteboard 0.7.0 → 0.9.1
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 +51 -1
- package/dist/chunk-74VEEZBV.mjs +619 -0
- package/dist/chunk-74VEEZBV.mjs.map +1 -0
- package/dist/chunk-DU2NFHRR.mjs +103 -0
- package/dist/chunk-DU2NFHRR.mjs.map +1 -0
- package/dist/{chunk-SHFOGORM.mjs → chunk-DU3RHKT5.mjs} +4 -4
- package/dist/{chunk-SHFOGORM.mjs.map → chunk-DU3RHKT5.mjs.map} +1 -1
- package/dist/{chunk-HYXFHEDJ.mjs → chunk-IUVV52HO.mjs} +22 -7
- package/dist/chunk-IUVV52HO.mjs.map +1 -0
- package/dist/{chunk-BJX4YNA5.mjs → chunk-KEYZ5EZT.mjs} +26 -9
- package/dist/chunk-KEYZ5EZT.mjs.map +1 -0
- package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
- package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
- package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
- package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
- package/dist/geometry-2d.js +250 -218
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +2 -2
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +3276 -1201
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +3 -2
- package/dist/graph-2d.js +360 -66
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +2 -2
- package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
- package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
- package/dist/host-PIIDSMVE.mjs +3187 -0
- package/dist/host-PIIDSMVE.mjs.map +1 -0
- package/dist/{host-T2W6R6SO.mjs → host-VDNAJMLC.mjs} +221 -216
- package/dist/host-VDNAJMLC.mjs.map +1 -0
- package/dist/index.d.mts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +4365 -1821
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +246 -102
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-BJX4YNA5.mjs.map +0 -1
- package/dist/chunk-DJTBZEAR.mjs +0 -25
- package/dist/chunk-DJTBZEAR.mjs.map +0 -1
- package/dist/chunk-HM7RIXJE.mjs +0 -331
- package/dist/chunk-HM7RIXJE.mjs.map +0 -1
- package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
- package/dist/chunk-LPM4MM45.mjs.map +0 -1
- package/dist/host-T2W6R6SO.mjs.map +0 -1
- package/dist/host-XUFON6CQ.mjs +0 -1422
- package/dist/host-XUFON6CQ.mjs.map +0 -1
package/dist/geometry-3d.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { geometry3dStamp } from './chunk-
|
|
3
|
-
export { isGeometry3DCustomData } from './chunk-
|
|
2
|
+
export { geometry3dStamp } from './chunk-IUVV52HO.mjs';
|
|
3
|
+
export { isGeometry3DCustomData } from './chunk-DU2NFHRR.mjs';
|
|
4
|
+
import './chunk-HTBLO5JO.mjs';
|
|
4
5
|
import './chunk-BJTO5JO5.mjs';
|
|
5
6
|
//# sourceMappingURL=geometry-3d.mjs.map
|
|
6
7
|
//# sourceMappingURL=geometry-3d.mjs.map
|
package/dist/graph-2d.js
CHANGED
|
@@ -59,6 +59,335 @@ var init_serialize = __esm({
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
// src/stamps/graph-2d/evaluator.ts
|
|
63
|
+
function tokenize(src) {
|
|
64
|
+
const tokens = [];
|
|
65
|
+
let i = 0;
|
|
66
|
+
while (i < src.length) {
|
|
67
|
+
const ch = src[i];
|
|
68
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
69
|
+
i++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch >= "0" && ch <= "9" || ch === ".") {
|
|
73
|
+
let j = i;
|
|
74
|
+
let hasDot = false;
|
|
75
|
+
let hasExp = false;
|
|
76
|
+
while (j < src.length) {
|
|
77
|
+
const c = src[j];
|
|
78
|
+
if (c >= "0" && c <= "9") {
|
|
79
|
+
j++;
|
|
80
|
+
} else if (c === "." && !hasDot && !hasExp) {
|
|
81
|
+
hasDot = true;
|
|
82
|
+
j++;
|
|
83
|
+
} else if ((c === "e" || c === "E") && !hasExp) {
|
|
84
|
+
hasExp = true;
|
|
85
|
+
j++;
|
|
86
|
+
if (src[j] === "+" || src[j] === "-") j++;
|
|
87
|
+
} else {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const raw = src.slice(i, j);
|
|
92
|
+
if (!/[0-9]/.test(raw)) {
|
|
93
|
+
throw new Error(`S\u1ED1 kh\xF4ng h\u1EE3p l\u1EC7 t\u1EA1i v\u1ECB tr\xED ${i}: "${raw}"`);
|
|
94
|
+
}
|
|
95
|
+
tokens.push({ type: "NUMBER", value: raw, pos: i });
|
|
96
|
+
i = j;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z") {
|
|
100
|
+
let j = i;
|
|
101
|
+
while (j < src.length) {
|
|
102
|
+
const c = src[j];
|
|
103
|
+
if (c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_") {
|
|
104
|
+
j++;
|
|
105
|
+
} else {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
tokens.push({ type: "IDENT", value: src.slice(i, j), pos: i });
|
|
110
|
+
i = j;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (OPERATORS.has(ch)) {
|
|
114
|
+
tokens.push({ type: "OP", value: ch, pos: i });
|
|
115
|
+
i++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (ch === "(") {
|
|
119
|
+
tokens.push({ type: "LPAREN", value: ch, pos: i });
|
|
120
|
+
i++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (ch === ")") {
|
|
124
|
+
tokens.push({ type: "RPAREN", value: ch, pos: i });
|
|
125
|
+
i++;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (ch === ",") {
|
|
129
|
+
tokens.push({ type: "COMMA", value: ch, pos: i });
|
|
130
|
+
i++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
throw new Error(`K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7 t\u1EA1i v\u1ECB tr\xED ${i}: "${ch}"`);
|
|
134
|
+
}
|
|
135
|
+
return tokens;
|
|
136
|
+
}
|
|
137
|
+
function parseAst(src) {
|
|
138
|
+
const tokens = tokenize(src);
|
|
139
|
+
if (tokens.length === 0) throw new Error("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
|
|
140
|
+
const p = new Parser(tokens);
|
|
141
|
+
return p.parseExpression();
|
|
142
|
+
}
|
|
143
|
+
function evaluate(node, env) {
|
|
144
|
+
switch (node.kind) {
|
|
145
|
+
case "num":
|
|
146
|
+
return node.value;
|
|
147
|
+
case "ident": {
|
|
148
|
+
const name = node.name;
|
|
149
|
+
if (name === "x") return env.x;
|
|
150
|
+
if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) {
|
|
151
|
+
return ALLOWED_CONSTANTS[name];
|
|
152
|
+
}
|
|
153
|
+
if (name.length === 1 && Object.prototype.hasOwnProperty.call(env.params, name)) {
|
|
154
|
+
return env.params[name];
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`Identifier kh\xF4ng h\u1EE3p l\u1EC7: "${name}"`);
|
|
157
|
+
}
|
|
158
|
+
case "unary": {
|
|
159
|
+
const v = evaluate(node.arg, env);
|
|
160
|
+
return node.op === "-" ? -v : +v;
|
|
161
|
+
}
|
|
162
|
+
case "binary": {
|
|
163
|
+
const a = evaluate(node.lhs, env);
|
|
164
|
+
const b = evaluate(node.rhs, env);
|
|
165
|
+
switch (node.op) {
|
|
166
|
+
case "+":
|
|
167
|
+
return a + b;
|
|
168
|
+
case "-":
|
|
169
|
+
return a - b;
|
|
170
|
+
case "*":
|
|
171
|
+
return a * b;
|
|
172
|
+
case "/":
|
|
173
|
+
return a / b;
|
|
174
|
+
// có thể trả Infinity/NaN — đúng theo IEEE 754
|
|
175
|
+
case "^":
|
|
176
|
+
return Math.pow(a, b);
|
|
177
|
+
}
|
|
178
|
+
throw new Error(`To\xE1n t\u1EED kh\xF4ng h\u1ED7 tr\u1EE3: "${node.op}"`);
|
|
179
|
+
}
|
|
180
|
+
case "call": {
|
|
181
|
+
const fn = ALLOWED_FUNCTIONS[node.name];
|
|
182
|
+
if (typeof fn !== "function") {
|
|
183
|
+
throw new Error(`H\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${node.name}"`);
|
|
184
|
+
}
|
|
185
|
+
const args = node.args.map((a) => evaluate(a, env));
|
|
186
|
+
return fn(...args);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function collectFreeVars(node, out = /* @__PURE__ */ new Set()) {
|
|
191
|
+
switch (node.kind) {
|
|
192
|
+
case "num":
|
|
193
|
+
return out;
|
|
194
|
+
case "ident": {
|
|
195
|
+
const name = node.name;
|
|
196
|
+
if (name === "x") return out;
|
|
197
|
+
if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return out;
|
|
198
|
+
if (name.length === 1) out.add(name);
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
case "unary":
|
|
202
|
+
return collectFreeVars(node.arg, out);
|
|
203
|
+
case "binary":
|
|
204
|
+
collectFreeVars(node.lhs, out);
|
|
205
|
+
collectFreeVars(node.rhs, out);
|
|
206
|
+
return out;
|
|
207
|
+
case "call":
|
|
208
|
+
for (const a of node.args) collectFreeVars(a, out);
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function checkIdentifiers(node) {
|
|
213
|
+
switch (node.kind) {
|
|
214
|
+
case "num":
|
|
215
|
+
return null;
|
|
216
|
+
case "ident": {
|
|
217
|
+
const name = node.name;
|
|
218
|
+
if (name === "x") return null;
|
|
219
|
+
if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return null;
|
|
220
|
+
if (name.length === 1) return null;
|
|
221
|
+
return `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${name}"`;
|
|
222
|
+
}
|
|
223
|
+
case "unary":
|
|
224
|
+
return checkIdentifiers(node.arg);
|
|
225
|
+
case "binary":
|
|
226
|
+
return checkIdentifiers(node.lhs) ?? checkIdentifiers(node.rhs);
|
|
227
|
+
case "call": {
|
|
228
|
+
if (!Object.prototype.hasOwnProperty.call(ALLOWED_FUNCTIONS, node.name)) {
|
|
229
|
+
return `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${node.name}"`;
|
|
230
|
+
}
|
|
231
|
+
for (const a of node.args) {
|
|
232
|
+
const e = checkIdentifiers(a);
|
|
233
|
+
if (e) return e;
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
var ALLOWED_FUNCTIONS, ALLOWED_CONSTANTS, OPERATORS, Parser, ALLOWED_FUNCTION_NAMES;
|
|
240
|
+
var init_evaluator = __esm({
|
|
241
|
+
"src/stamps/graph-2d/evaluator.ts"() {
|
|
242
|
+
ALLOWED_FUNCTIONS = {
|
|
243
|
+
sin: Math.sin,
|
|
244
|
+
cos: Math.cos,
|
|
245
|
+
tan: Math.tan,
|
|
246
|
+
asin: Math.asin,
|
|
247
|
+
acos: Math.acos,
|
|
248
|
+
atan: Math.atan,
|
|
249
|
+
log: Math.log10,
|
|
250
|
+
// log = log10 (khớp với rewriteToJs)
|
|
251
|
+
ln: Math.log,
|
|
252
|
+
// ln = log tự nhiên
|
|
253
|
+
exp: Math.exp,
|
|
254
|
+
sqrt: Math.sqrt,
|
|
255
|
+
abs: Math.abs,
|
|
256
|
+
floor: Math.floor,
|
|
257
|
+
ceil: Math.ceil,
|
|
258
|
+
round: Math.round
|
|
259
|
+
};
|
|
260
|
+
ALLOWED_CONSTANTS = {
|
|
261
|
+
pi: Math.PI,
|
|
262
|
+
e: Math.E
|
|
263
|
+
};
|
|
264
|
+
OPERATORS = /* @__PURE__ */ new Set(["+", "-", "*", "/", "^"]);
|
|
265
|
+
Parser = class {
|
|
266
|
+
constructor(tokens) {
|
|
267
|
+
this.tokens = tokens;
|
|
268
|
+
this.pos = 0;
|
|
269
|
+
}
|
|
270
|
+
peek() {
|
|
271
|
+
return this.tokens[this.pos];
|
|
272
|
+
}
|
|
273
|
+
consume() {
|
|
274
|
+
const t = this.tokens[this.pos++];
|
|
275
|
+
if (!t) throw new Error("C\xFA ph\xE1p: h\u1EBFt token s\u1EDBm");
|
|
276
|
+
return t;
|
|
277
|
+
}
|
|
278
|
+
parseExpression() {
|
|
279
|
+
const node = this.parseAddSub();
|
|
280
|
+
if (this.pos < this.tokens.length) {
|
|
281
|
+
const t = this.tokens[this.pos];
|
|
282
|
+
throw new Error(`C\xFA ph\xE1p: token th\u1EEBa "${t.value}" t\u1EA1i v\u1ECB tr\xED ${t.pos}`);
|
|
283
|
+
}
|
|
284
|
+
return node;
|
|
285
|
+
}
|
|
286
|
+
// + - (left assoc)
|
|
287
|
+
parseAddSub() {
|
|
288
|
+
let lhs = this.parseMulDiv();
|
|
289
|
+
while (true) {
|
|
290
|
+
const t = this.peek();
|
|
291
|
+
if (t && t.type === "OP" && (t.value === "+" || t.value === "-")) {
|
|
292
|
+
this.consume();
|
|
293
|
+
const rhs = this.parseMulDiv();
|
|
294
|
+
lhs = { kind: "binary", op: t.value, lhs, rhs };
|
|
295
|
+
} else {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return lhs;
|
|
300
|
+
}
|
|
301
|
+
// * / (left assoc)
|
|
302
|
+
parseMulDiv() {
|
|
303
|
+
let lhs = this.parseUnary();
|
|
304
|
+
while (true) {
|
|
305
|
+
const t = this.peek();
|
|
306
|
+
if (t && t.type === "OP" && (t.value === "*" || t.value === "/")) {
|
|
307
|
+
this.consume();
|
|
308
|
+
const rhs = this.parseUnary();
|
|
309
|
+
lhs = { kind: "binary", op: t.value, lhs, rhs };
|
|
310
|
+
} else {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return lhs;
|
|
315
|
+
}
|
|
316
|
+
// unary + - (right assoc) sau đó parsePow
|
|
317
|
+
parseUnary() {
|
|
318
|
+
const t = this.peek();
|
|
319
|
+
if (t && t.type === "OP" && (t.value === "+" || t.value === "-")) {
|
|
320
|
+
this.consume();
|
|
321
|
+
const arg = this.parseUnary();
|
|
322
|
+
return { kind: "unary", op: t.value, arg };
|
|
323
|
+
}
|
|
324
|
+
return this.parsePow();
|
|
325
|
+
}
|
|
326
|
+
// ^ (right assoc)
|
|
327
|
+
parsePow() {
|
|
328
|
+
const lhs = this.parsePrimary();
|
|
329
|
+
const t = this.peek();
|
|
330
|
+
if (t && t.type === "OP" && t.value === "^") {
|
|
331
|
+
this.consume();
|
|
332
|
+
const rhs = this.parseUnary();
|
|
333
|
+
return { kind: "binary", op: "^", lhs, rhs };
|
|
334
|
+
}
|
|
335
|
+
return lhs;
|
|
336
|
+
}
|
|
337
|
+
parsePrimary() {
|
|
338
|
+
const t = this.peek();
|
|
339
|
+
if (!t) throw new Error("C\xFA ph\xE1p: thi\u1EBFu bi\u1EC3u th\u1EE9c");
|
|
340
|
+
if (t.type === "NUMBER") {
|
|
341
|
+
this.consume();
|
|
342
|
+
const v = Number(t.value);
|
|
343
|
+
return { kind: "num", value: v };
|
|
344
|
+
}
|
|
345
|
+
if (t.type === "IDENT") {
|
|
346
|
+
this.consume();
|
|
347
|
+
const next = this.peek();
|
|
348
|
+
if (next && next.type === "LPAREN") {
|
|
349
|
+
this.consume();
|
|
350
|
+
const args = [];
|
|
351
|
+
const lookahead = this.peek();
|
|
352
|
+
if (!lookahead || lookahead.type !== "RPAREN") {
|
|
353
|
+
args.push(this.parseAddSub());
|
|
354
|
+
while (true) {
|
|
355
|
+
const nx = this.peek();
|
|
356
|
+
if (nx && nx.type === "COMMA") {
|
|
357
|
+
this.consume();
|
|
358
|
+
args.push(this.parseAddSub());
|
|
359
|
+
} else {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const close = this.peek();
|
|
365
|
+
if (!close || close.type !== "RPAREN") {
|
|
366
|
+
throw new Error(`C\xFA ph\xE1p: thi\u1EBFu ")" sau h\xE0m "${t.value}"`);
|
|
367
|
+
}
|
|
368
|
+
this.consume();
|
|
369
|
+
return { kind: "call", name: t.value, args };
|
|
370
|
+
}
|
|
371
|
+
return { kind: "ident", name: t.value };
|
|
372
|
+
}
|
|
373
|
+
if (t.type === "LPAREN") {
|
|
374
|
+
this.consume();
|
|
375
|
+
const inner = this.parseAddSub();
|
|
376
|
+
const close = this.peek();
|
|
377
|
+
if (!close || close.type !== "RPAREN") {
|
|
378
|
+
throw new Error('C\xFA ph\xE1p: thi\u1EBFu ")"');
|
|
379
|
+
}
|
|
380
|
+
this.consume();
|
|
381
|
+
return inner;
|
|
382
|
+
}
|
|
383
|
+
throw new Error(`C\xFA ph\xE1p: token b\u1EA5t ng\u1EDD "${t.value}" t\u1EA1i v\u1ECB tr\xED ${t.pos}`);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
ALLOWED_FUNCTION_NAMES = new Set(Object.keys(ALLOWED_FUNCTIONS));
|
|
387
|
+
new Set(Object.keys(ALLOWED_CONSTANTS));
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
62
391
|
// src/stamps/graph-2d/parser.ts
|
|
63
392
|
function errResult(message) {
|
|
64
393
|
return { ok: false, error: message, freeVars: /* @__PURE__ */ new Set() };
|
|
@@ -67,13 +396,20 @@ function validate(expr) {
|
|
|
67
396
|
const trimmed = expr.trim();
|
|
68
397
|
if (!trimmed) return errResult("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
|
|
69
398
|
if (!ALLOWED_CHARS.test(trimmed)) return errResult("K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7");
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
399
|
+
let tokens;
|
|
400
|
+
try {
|
|
401
|
+
tokens = tokenize(trimmed);
|
|
402
|
+
} catch {
|
|
403
|
+
return errResult("L\u1ED7i c\xFA ph\xE1p");
|
|
404
|
+
}
|
|
405
|
+
const earlyFree = /* @__PURE__ */ new Set();
|
|
406
|
+
for (const tok of tokens) {
|
|
407
|
+
if (tok.type !== "IDENT") continue;
|
|
408
|
+
const id = tok.value;
|
|
73
409
|
if (id === "x" || id === "pi" || id === "e") continue;
|
|
74
|
-
if (
|
|
410
|
+
if (ALLOWED_FUNCTIONS2.has(id)) continue;
|
|
75
411
|
if (id.length === 1) {
|
|
76
|
-
|
|
412
|
+
earlyFree.add(id);
|
|
77
413
|
continue;
|
|
78
414
|
}
|
|
79
415
|
const hint = SUGGESTIONS[id];
|
|
@@ -81,90 +417,48 @@ function validate(expr) {
|
|
|
81
417
|
hint ? `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${id}". B\u1EA1n c\xF3 \xFD l\xE0 "${hint}" kh\xF4ng?` : `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${id}"`
|
|
82
418
|
);
|
|
83
419
|
}
|
|
420
|
+
let ast;
|
|
84
421
|
try {
|
|
85
|
-
|
|
86
|
-
const rewritten = rewriteToJs(trimmed, paramSubs);
|
|
87
|
-
new Function("x", `return (${rewritten})`);
|
|
422
|
+
ast = parseAst(trimmed);
|
|
88
423
|
} catch {
|
|
89
424
|
return errResult("L\u1ED7i c\xFA ph\xE1p");
|
|
90
425
|
}
|
|
426
|
+
const idErr = checkIdentifiers(ast);
|
|
427
|
+
if (idErr) return errResult(idErr);
|
|
428
|
+
const freeVars = collectFreeVars(ast);
|
|
429
|
+
for (const v of earlyFree) freeVars.add(v);
|
|
91
430
|
return { ok: true, freeVars };
|
|
92
431
|
}
|
|
93
|
-
function rewriteToJs(expr, params) {
|
|
94
|
-
let s = expr.replace(/\^/g, "**");
|
|
95
|
-
s = s.replace(/\bpi\b/g, "Math.PI");
|
|
96
|
-
s = s.replace(/\be\b/g, "Math.E");
|
|
97
|
-
for (const [from, to] of FUNCTION_REPLACEMENTS) {
|
|
98
|
-
s = s.replace(new RegExp(`\\b${from}\\b`, "g"), to);
|
|
99
|
-
}
|
|
100
|
-
for (const [name, value] of Object.entries(params)) {
|
|
101
|
-
if (name.length !== 1) continue;
|
|
102
|
-
s = s.replace(new RegExp(`\\b${name}\\b`, "g"), `(${value})`);
|
|
103
|
-
}
|
|
104
|
-
return s;
|
|
105
|
-
}
|
|
106
432
|
function compile(expr, paramValues) {
|
|
107
433
|
const v = validate(expr);
|
|
108
434
|
if (!v.ok) return { error: v.error ?? "Invalid" };
|
|
435
|
+
let ast;
|
|
109
436
|
try {
|
|
110
|
-
|
|
111
|
-
const raw = new Function("x", `return (${rewritten})`);
|
|
112
|
-
return (x) => {
|
|
113
|
-
try {
|
|
114
|
-
const y = raw(x);
|
|
115
|
-
return typeof y === "number" ? y : NaN;
|
|
116
|
-
} catch {
|
|
117
|
-
return NaN;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
437
|
+
ast = parseAst(expr.trim());
|
|
120
438
|
} catch (err) {
|
|
121
439
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
122
440
|
}
|
|
441
|
+
return (x) => {
|
|
442
|
+
try {
|
|
443
|
+
const y = evaluate(ast, { x, params: paramValues });
|
|
444
|
+
return typeof y === "number" ? y : NaN;
|
|
445
|
+
} catch {
|
|
446
|
+
return NaN;
|
|
447
|
+
}
|
|
448
|
+
};
|
|
123
449
|
}
|
|
124
|
-
var
|
|
450
|
+
var ALLOWED_FUNCTIONS2, ALLOWED_CHARS, SUGGESTIONS;
|
|
125
451
|
var init_parser = __esm({
|
|
126
452
|
"src/stamps/graph-2d/parser.ts"() {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"cos",
|
|
130
|
-
"tan",
|
|
131
|
-
"asin",
|
|
132
|
-
"acos",
|
|
133
|
-
"atan",
|
|
134
|
-
"log",
|
|
135
|
-
"ln",
|
|
136
|
-
"exp",
|
|
137
|
-
"sqrt",
|
|
138
|
-
"abs",
|
|
139
|
-
"floor",
|
|
140
|
-
"ceil",
|
|
141
|
-
"round"
|
|
142
|
-
]);
|
|
453
|
+
init_evaluator();
|
|
454
|
+
ALLOWED_FUNCTIONS2 = ALLOWED_FUNCTION_NAMES;
|
|
143
455
|
ALLOWED_CHARS = /^[a-zA-Z0-9_.+\-*/^()\s,]+$/;
|
|
144
|
-
IDENTIFIER_RE = /[a-zA-Z][a-zA-Z0-9_]*/g;
|
|
145
456
|
SUGGESTIONS = {
|
|
146
457
|
tg: "tan",
|
|
147
458
|
arcsin: "asin",
|
|
148
459
|
arccos: "acos",
|
|
149
460
|
arctan: "atan"
|
|
150
461
|
};
|
|
151
|
-
FUNCTION_REPLACEMENTS = [
|
|
152
|
-
// longest first để tránh substring conflict (asin trước sin)
|
|
153
|
-
["asin", "Math.asin"],
|
|
154
|
-
["acos", "Math.acos"],
|
|
155
|
-
["atan", "Math.atan"],
|
|
156
|
-
["sqrt", "Math.sqrt"],
|
|
157
|
-
["floor", "Math.floor"],
|
|
158
|
-
["round", "Math.round"],
|
|
159
|
-
["ceil", "Math.ceil"],
|
|
160
|
-
["sin", "Math.sin"],
|
|
161
|
-
["cos", "Math.cos"],
|
|
162
|
-
["tan", "Math.tan"],
|
|
163
|
-
["abs", "Math.abs"],
|
|
164
|
-
["exp", "Math.exp"],
|
|
165
|
-
["log", "Math.log10"],
|
|
166
|
-
["ln", "Math.log"]
|
|
167
|
-
];
|
|
168
462
|
}
|
|
169
463
|
});
|
|
170
464
|
|