@sciexpr/core 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/dist/index.cjs ADDED
@@ -0,0 +1,579 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ExpressionClassifier: () => ExpressionClassifier,
24
+ ExpressionKind: () => ExpressionKind,
25
+ SymbolType: () => SymbolType,
26
+ ValidationErrorCode: () => ValidationErrorCode,
27
+ cloneNode: () => cloneNode,
28
+ collectNodes: () => collectNodes,
29
+ countNodes: () => countNodes,
30
+ createAccent: () => createAccent,
31
+ createChemBond: () => createChemBond,
32
+ createChemElement: () => createChemElement,
33
+ createChemGroup: () => createChemGroup,
34
+ createChemReaction: () => createChemReaction,
35
+ createColor: () => createColor,
36
+ createDelimiter: () => createDelimiter,
37
+ createFraction: () => createFraction,
38
+ createGroup: () => createGroup,
39
+ createLargeOp: () => createLargeOp,
40
+ createMatrix: () => createMatrix,
41
+ createRadical: () => createRadical,
42
+ createRoot: () => createRoot,
43
+ createScript: () => createScript,
44
+ createSpace: () => createSpace,
45
+ createSymbol: () => createSymbol,
46
+ createText: () => createText,
47
+ deserializeAST: () => deserializeAST,
48
+ findNode: () => findNode,
49
+ flattenNode: () => flattenNode,
50
+ generateNodeId: () => generateNodeId,
51
+ getNodeChildren: () => getNodeChildren,
52
+ isNodeType: () => isNodeType,
53
+ resetNodeIdCounter: () => resetNodeIdCounter,
54
+ safeDeserializeAST: () => safeDeserializeAST,
55
+ serializeAST: () => serializeAST,
56
+ serializeASTCompact: () => serializeASTCompact,
57
+ serializeASTPretty: () => serializeASTPretty,
58
+ traverseAST: () => traverseAST
59
+ });
60
+ module.exports = __toCommonJS(index_exports);
61
+
62
+ // src/types/ast.ts
63
+ var ExpressionKind = /* @__PURE__ */ ((ExpressionKind2) => {
64
+ ExpressionKind2["Math"] = "math";
65
+ ExpressionKind2["Molecule"] = "molecule";
66
+ ExpressionKind2["ChemicalEquation"] = "chemical-equation";
67
+ ExpressionKind2["Matrix"] = "matrix";
68
+ ExpressionKind2["Text"] = "text";
69
+ ExpressionKind2["Mixed"] = "mixed";
70
+ return ExpressionKind2;
71
+ })(ExpressionKind || {});
72
+ var SymbolType = /* @__PURE__ */ ((SymbolType2) => {
73
+ SymbolType2["Greek"] = "greek";
74
+ SymbolType2["Operator"] = "operator";
75
+ SymbolType2["Relation"] = "relation";
76
+ SymbolType2["Arrow"] = "arrow";
77
+ SymbolType2["Accent"] = "accent";
78
+ SymbolType2["Ordinary"] = "ordinary";
79
+ SymbolType2["Variable"] = "variable";
80
+ SymbolType2["Number"] = "number";
81
+ return SymbolType2;
82
+ })(SymbolType || {});
83
+
84
+ // src/types/validator.ts
85
+ var ValidationErrorCode = /* @__PURE__ */ ((ValidationErrorCode2) => {
86
+ ValidationErrorCode2["UNEXPECTED_NODE"] = "UNEXPECTED_NODE";
87
+ ValidationErrorCode2["EMPTY_EXPRESSION"] = "EMPTY_EXPRESSION";
88
+ ValidationErrorCode2["UNBALANCED_BRACE"] = "UNBALANCED_BRACE";
89
+ ValidationErrorCode2["UNBALANCED_BRACKET"] = "UNBALANCED_BRACKET";
90
+ ValidationErrorCode2["INVALID_SCRIPT"] = "INVALID_SCRIPT";
91
+ ValidationErrorCode2["DIVISION_BY_ZERO"] = "DIVISION_BY_ZERO";
92
+ ValidationErrorCode2["MATRIX_DIMENSION_MISMATCH"] = "MATRIX_DIMENSION_MISMATCH";
93
+ ValidationErrorCode2["NEGATIVE_RADICAL"] = "NEGATIVE_RADICAL";
94
+ ValidationErrorCode2["INVALID_ELEMENT_SYMBOL"] = "INVALID_ELEMENT_SYMBOL";
95
+ ValidationErrorCode2["INVALID_CHEMICAL_FORMULA"] = "INVALID_CHEMICAL_FORMULA";
96
+ ValidationErrorCode2["CHARGE_IMBALANCE"] = "CHARGE_IMBALANCE";
97
+ ValidationErrorCode2["ELEMENT_IMBALANCE"] = "ELEMENT_IMBALANCE";
98
+ ValidationErrorCode2["INVALID_BOND"] = "INVALID_BOND";
99
+ return ValidationErrorCode2;
100
+ })(ValidationErrorCode || {});
101
+
102
+ // src/node-factory.ts
103
+ var _nodeIdCounter = 0;
104
+ function generateNodeId() {
105
+ return `n${++_nodeIdCounter}`;
106
+ }
107
+ function resetNodeIdCounter() {
108
+ _nodeIdCounter = 0;
109
+ }
110
+ function createRoot(type, children, metadata) {
111
+ return {
112
+ kind: "root",
113
+ id: generateNodeId(),
114
+ type,
115
+ children,
116
+ metadata
117
+ };
118
+ }
119
+ function createSymbol(value, symbolType) {
120
+ return {
121
+ kind: "symbol",
122
+ id: generateNodeId(),
123
+ value,
124
+ symbolType
125
+ };
126
+ }
127
+ function createFraction(numerator, denominator) {
128
+ return {
129
+ kind: "frac",
130
+ id: generateNodeId(),
131
+ numerator,
132
+ denominator
133
+ };
134
+ }
135
+ function createScript(base, options) {
136
+ return {
137
+ kind: "script",
138
+ id: generateNodeId(),
139
+ base,
140
+ ...options
141
+ };
142
+ }
143
+ function createRadical(radicand, index) {
144
+ return {
145
+ kind: "radical",
146
+ id: generateNodeId(),
147
+ radicand,
148
+ index
149
+ };
150
+ }
151
+ function createDelimiter(left, right, body) {
152
+ return {
153
+ kind: "delimiter",
154
+ id: generateNodeId(),
155
+ left,
156
+ right,
157
+ body
158
+ };
159
+ }
160
+ function createMatrix(rows, options) {
161
+ return {
162
+ kind: "matrix",
163
+ id: generateNodeId(),
164
+ rows,
165
+ ...options
166
+ };
167
+ }
168
+ function createText(content) {
169
+ return {
170
+ kind: "text",
171
+ id: generateNodeId(),
172
+ content
173
+ };
174
+ }
175
+ function createSpace(width) {
176
+ return {
177
+ kind: "space",
178
+ id: generateNodeId(),
179
+ width
180
+ };
181
+ }
182
+ function createAccent(accentType, base) {
183
+ return {
184
+ kind: "accent",
185
+ id: generateNodeId(),
186
+ accentType,
187
+ base
188
+ };
189
+ }
190
+ function createLargeOp(opType, body, options) {
191
+ return {
192
+ kind: "largeop",
193
+ id: generateNodeId(),
194
+ opType,
195
+ body,
196
+ ...options
197
+ };
198
+ }
199
+ function createColor(color, body) {
200
+ return {
201
+ kind: "color",
202
+ id: generateNodeId(),
203
+ color,
204
+ body
205
+ };
206
+ }
207
+ function createGroup(children) {
208
+ return {
209
+ kind: "group",
210
+ id: generateNodeId(),
211
+ children
212
+ };
213
+ }
214
+ function createChemElement(symbol, options) {
215
+ return {
216
+ kind: "chem-element",
217
+ id: generateNodeId(),
218
+ symbol,
219
+ ...options
220
+ };
221
+ }
222
+ function createChemBond(bondType, left, right) {
223
+ return {
224
+ kind: "chem-bond",
225
+ id: generateNodeId(),
226
+ bondType,
227
+ left,
228
+ right
229
+ };
230
+ }
231
+ function createChemReaction(reactants, products, arrowType, conditions) {
232
+ return {
233
+ kind: "chem-reaction",
234
+ id: generateNodeId(),
235
+ reactants,
236
+ products,
237
+ arrowType,
238
+ conditions
239
+ };
240
+ }
241
+ function createChemGroup(atoms, name) {
242
+ return {
243
+ kind: "chem-group",
244
+ id: generateNodeId(),
245
+ name,
246
+ atoms
247
+ };
248
+ }
249
+ function cloneNode(node) {
250
+ const cloned = JSON.parse(JSON.stringify(node));
251
+ cloned.id = generateNodeId();
252
+ return cloned;
253
+ }
254
+ function isNodeType(node, kind) {
255
+ return node.kind === kind;
256
+ }
257
+ function flattenNode(node) {
258
+ const result = [node];
259
+ const children = getNodeChildren(node);
260
+ for (const child of children) {
261
+ result.push(...flattenNode(child));
262
+ }
263
+ return result;
264
+ }
265
+ function getNodeChildren(node) {
266
+ const n = node;
267
+ switch (n.kind) {
268
+ case "root":
269
+ case "group":
270
+ return n.children;
271
+ case "frac":
272
+ return [n.numerator, n.denominator];
273
+ case "script":
274
+ return [n.base, n.sub, n.super].filter(Boolean);
275
+ case "radical":
276
+ return [n.radicand, n.index].filter(Boolean);
277
+ case "delimiter":
278
+ return [n.body];
279
+ case "matrix":
280
+ return n.rows.flat();
281
+ case "color":
282
+ return [n.body];
283
+ case "accent":
284
+ return [n.base];
285
+ case "largeop":
286
+ return [n.body, n.lower, n.upper].filter(Boolean);
287
+ case "chem-bond":
288
+ return [n.left, n.right];
289
+ case "chem-reaction":
290
+ return [...n.reactants, ...n.products, n.conditions].filter(Boolean);
291
+ case "chem-group":
292
+ return n.atoms;
293
+ case "symbol":
294
+ case "text":
295
+ case "space":
296
+ case "chem-element":
297
+ return [];
298
+ default:
299
+ return [];
300
+ }
301
+ }
302
+
303
+ // src/ast-utils.ts
304
+ function traverseAST(node, visitor, options = {}) {
305
+ const { order = "pre" } = options;
306
+ let result;
307
+ if (order === "pre") {
308
+ result = visitNode(node, visitor);
309
+ }
310
+ const children = getChildren(node);
311
+ for (const child of children) {
312
+ traverseAST(child, visitor, options);
313
+ }
314
+ if (order === "post") {
315
+ result = visitNode(node, visitor);
316
+ }
317
+ return result;
318
+ }
319
+ function visitNode(node, visitor) {
320
+ let result;
321
+ const n = node;
322
+ switch (n.kind) {
323
+ case "root":
324
+ result = visitor.visitRoot?.(n);
325
+ break;
326
+ case "symbol":
327
+ result = visitor.visitSymbol?.(n);
328
+ break;
329
+ case "frac":
330
+ result = visitor.visitFraction?.(n);
331
+ break;
332
+ case "script":
333
+ result = visitor.visitScript?.(n);
334
+ break;
335
+ case "radical":
336
+ result = visitor.visitRadical?.(n);
337
+ break;
338
+ case "delimiter":
339
+ result = visitor.visitDelimiter?.(n);
340
+ break;
341
+ case "matrix":
342
+ result = visitor.visitMatrix?.(n);
343
+ break;
344
+ case "text":
345
+ result = visitor.visitText?.(n);
346
+ break;
347
+ case "space":
348
+ result = visitor.visitSpace?.(n);
349
+ break;
350
+ case "accent":
351
+ result = visitor.visitAccent?.(n);
352
+ break;
353
+ case "largeop":
354
+ result = visitor.visitLargeOp?.(n);
355
+ break;
356
+ case "color":
357
+ result = visitor.visitColor?.(n);
358
+ break;
359
+ case "group":
360
+ result = visitor.visitGroup?.(n);
361
+ break;
362
+ case "chem-element":
363
+ result = visitor.visitChemElement?.(n);
364
+ break;
365
+ case "chem-bond":
366
+ result = visitor.visitChemBond?.(n);
367
+ break;
368
+ case "chem-reaction":
369
+ result = visitor.visitChemReaction?.(n);
370
+ break;
371
+ case "chem-group":
372
+ result = visitor.visitChemGroup?.(n);
373
+ break;
374
+ default:
375
+ result = visitor.visitDefault?.(n);
376
+ break;
377
+ }
378
+ if (result === void 0 && visitor.visitDefault) {
379
+ result = visitor.visitDefault(node);
380
+ }
381
+ return result;
382
+ }
383
+ function getChildren(node) {
384
+ const n = node;
385
+ switch (n.kind) {
386
+ case "root":
387
+ case "group":
388
+ return n.children;
389
+ case "frac":
390
+ return [n.numerator, n.denominator];
391
+ case "script": {
392
+ const children = [n.base];
393
+ if (n.sub) children.push(n.sub);
394
+ if (n.super) children.push(n.super);
395
+ return children;
396
+ }
397
+ case "radical": {
398
+ const children = [n.radicand];
399
+ if (n.index) children.push(n.index);
400
+ return children;
401
+ }
402
+ case "delimiter":
403
+ return [n.body];
404
+ case "matrix":
405
+ return n.rows.flat();
406
+ case "color":
407
+ return [n.body];
408
+ case "accent":
409
+ return [n.base];
410
+ case "largeop": {
411
+ const children = [];
412
+ if (n.body) children.push(n.body);
413
+ if (n.lower) children.push(n.lower);
414
+ if (n.upper) children.push(n.upper);
415
+ return children;
416
+ }
417
+ case "chem-bond":
418
+ return [n.left, n.right];
419
+ case "chem-reaction": {
420
+ const children = [...n.reactants, ...n.products];
421
+ if (n.conditions) children.push(n.conditions);
422
+ return children;
423
+ }
424
+ case "chem-group":
425
+ return n.atoms;
426
+ case "symbol":
427
+ case "text":
428
+ case "space":
429
+ case "chem-element":
430
+ return [];
431
+ default:
432
+ return [];
433
+ }
434
+ }
435
+ function collectNodes(root, predicate) {
436
+ const results = [];
437
+ traverseAST(root, {
438
+ visitDefault(node) {
439
+ if (predicate(node)) {
440
+ results.push(node);
441
+ }
442
+ }
443
+ });
444
+ return results;
445
+ }
446
+ function findNode(root, predicate) {
447
+ return collectNodes(root, predicate)[0];
448
+ }
449
+ function countNodes(root) {
450
+ let count = 0;
451
+ traverseAST(root, {
452
+ visitDefault() {
453
+ count++;
454
+ }
455
+ });
456
+ return count;
457
+ }
458
+
459
+ // src/serializer.ts
460
+ function serializeAST(root) {
461
+ return JSON.stringify(root);
462
+ }
463
+ function deserializeAST(json) {
464
+ const parsed = JSON.parse(json);
465
+ if (!parsed || parsed.kind !== "root") {
466
+ throw new Error('Invalid AST JSON: root node must have kind "root"');
467
+ }
468
+ return parsed;
469
+ }
470
+ function safeDeserializeAST(json) {
471
+ try {
472
+ return deserializeAST(json);
473
+ } catch {
474
+ return null;
475
+ }
476
+ }
477
+ function serializeASTCompact(root) {
478
+ return JSON.stringify(root, (key, value) => {
479
+ if (key === "id" && typeof value === "string" && value.startsWith("n")) {
480
+ return void 0;
481
+ }
482
+ if (key === "source" && value === void 0) {
483
+ return void 0;
484
+ }
485
+ return value;
486
+ });
487
+ }
488
+ function serializeASTPretty(root) {
489
+ return JSON.stringify(root, null, 2);
490
+ }
491
+
492
+ // src/classifier.ts
493
+ var ExpressionClassifier = class {
494
+ /**
495
+ * 分类表达式
496
+ */
497
+ classify(root) {
498
+ if (root.type !== "mixed" /* Mixed */) {
499
+ return root.type;
500
+ }
501
+ const hasChemistryNodes = this.hasChemistryNodes(root);
502
+ const hasMathNodes = this.hasMathNodes(root);
503
+ if (hasChemistryNodes && !hasMathNodes) {
504
+ const hasReaction = collectNodes(root, (n) => n.kind === "chem-reaction").length > 0;
505
+ return hasReaction ? "chemical-equation" /* ChemicalEquation */ : "molecule" /* Molecule */;
506
+ }
507
+ if (hasMathNodes && hasChemistryNodes) {
508
+ return "mixed" /* Mixed */;
509
+ }
510
+ if (hasMathNodes) {
511
+ const hasMatrix = collectNodes(root, (n) => n.kind === "matrix").length > 0;
512
+ return hasMatrix ? "matrix" /* Matrix */ : "math" /* Math */;
513
+ }
514
+ return "text" /* Text */;
515
+ }
516
+ /**
517
+ * 检测是否包含化学相关节点
518
+ */
519
+ hasChemistryNodes(root) {
520
+ return collectNodes(
521
+ root,
522
+ (n) => ["chem-element", "chem-bond", "chem-reaction", "chem-group"].includes(n.kind)
523
+ ).length > 0;
524
+ }
525
+ /**
526
+ * 检测是否包含数学相关节点
527
+ */
528
+ hasMathNodes(root) {
529
+ return collectNodes(
530
+ root,
531
+ (n) => ["frac", "script", "radical", "delimiter", "matrix", "accent", "largeop"].includes(n.kind)
532
+ ).length > 0;
533
+ }
534
+ /**
535
+ * 检测是否为 display 模式(块级公式)
536
+ */
537
+ isDisplayMode(root) {
538
+ return root.metadata?.displayMode ?? false;
539
+ }
540
+ };
541
+ // Annotate the CommonJS export names for ESM import in node:
542
+ 0 && (module.exports = {
543
+ ExpressionClassifier,
544
+ ExpressionKind,
545
+ SymbolType,
546
+ ValidationErrorCode,
547
+ cloneNode,
548
+ collectNodes,
549
+ countNodes,
550
+ createAccent,
551
+ createChemBond,
552
+ createChemElement,
553
+ createChemGroup,
554
+ createChemReaction,
555
+ createColor,
556
+ createDelimiter,
557
+ createFraction,
558
+ createGroup,
559
+ createLargeOp,
560
+ createMatrix,
561
+ createRadical,
562
+ createRoot,
563
+ createScript,
564
+ createSpace,
565
+ createSymbol,
566
+ createText,
567
+ deserializeAST,
568
+ findNode,
569
+ flattenNode,
570
+ generateNodeId,
571
+ getNodeChildren,
572
+ isNodeType,
573
+ resetNodeIdCounter,
574
+ safeDeserializeAST,
575
+ serializeAST,
576
+ serializeASTCompact,
577
+ serializeASTPretty,
578
+ traverseAST
579
+ });