@sciexpr/parser 0.1.0 → 0.1.2

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 CHANGED
@@ -139,8 +139,6 @@ var LatexTokenizer = class {
139
139
  this.pos++;
140
140
  return { type: "ampersand" /* Ampersand */, value: "&", position: start, length: 1 };
141
141
  }
142
- if (ch === "\\" && this.input[this.pos + 1] === "\\") {
143
- }
144
142
  if (/[0-9]/.test(ch)) {
145
143
  this.pos++;
146
144
  while (this.pos < this.input.length && /[0-9.]/.test(this.input[this.pos])) {
@@ -182,9 +180,17 @@ var LatexParser = class {
182
180
  // ==========================================================================
183
181
  // Parse Expressions
184
182
  // ==========================================================================
185
- parseExpressions() {
183
+ /**
184
+ * Parse a sequence of expressions.
185
+ * @param stopType Optional token type to stop at (e.g. CloseBrace inside a group).
186
+ * When set, the loop breaks BEFORE consuming the stop token.
187
+ */
188
+ parseExpressions(stopType) {
186
189
  const exprs = [];
187
190
  while (!this.isEOF()) {
191
+ if (stopType !== void 0 && this.peek()?.type === stopType) {
192
+ break;
193
+ }
188
194
  const expr = this.parseExpression();
189
195
  if (expr) exprs.push(expr);
190
196
  }
@@ -220,6 +226,10 @@ var LatexParser = class {
220
226
  this.advance();
221
227
  expr = (0, import_core2.createSymbol)(token.value, this.classifySymbol(token.value));
222
228
  break;
229
+ case "ampersand" /* Ampersand */:
230
+ this.advance();
231
+ expr = (0, import_core2.createSymbol)("&", import_core.SymbolType.Ordinary);
232
+ break;
223
233
  case "number" /* Number */:
224
234
  this.advance();
225
235
  expr = (0, import_core2.createSymbol)(token.value, import_core.SymbolType.Number);
@@ -230,13 +240,13 @@ var LatexParser = class {
230
240
  // Skip whitespace
231
241
  case "openParen" /* OpenParen */:
232
242
  this.advance();
233
- const body = this.parseExpressions();
243
+ const body = this.parseExpressions("closeParen" /* CloseParen */);
234
244
  this.skipType("closeParen" /* CloseParen */);
235
245
  expr = (0, import_core2.createDelimiter)("(", ")", body.length === 1 ? body[0] : (0, import_core2.createGroup)(body));
236
246
  break;
237
247
  case "openBracket" /* OpenBracket */:
238
248
  this.advance();
239
- const bracketBody = this.parseExpressions();
249
+ const bracketBody = this.parseExpressions("closeBracket" /* CloseBracket */);
240
250
  this.skipType("closeBracket" /* CloseBracket */);
241
251
  expr = (0, import_core2.createDelimiter)("[", "]", bracketBody.length === 1 ? bracketBody[0] : (0, import_core2.createGroup)(bracketBody));
242
252
  break;
@@ -346,7 +356,7 @@ var LatexParser = class {
346
356
  /** 解析 { expressions } 分组 */
347
357
  parseGroup() {
348
358
  this.skipType("openBrace" /* OpenBrace */);
349
- const children = this.parseExpressions();
359
+ const children = this.parseExpressions("closeBrace" /* CloseBrace */);
350
360
  this.skipType("closeBrace" /* CloseBrace */);
351
361
  return (0, import_core2.createGroup)(children);
352
362
  }
@@ -453,9 +463,17 @@ var ChemicalParser = class {
453
463
  }
454
464
  groupCount = parseInt(numStr, 10);
455
465
  if (groupCount && groupCount > 1) {
456
- for (const atom of groupAtoms) {
457
- atom.count = (atom.count ?? 1) * groupCount;
458
- }
466
+ const multiplier = groupCount;
467
+ const scaledAtoms = groupAtoms.map(
468
+ (atom) => (0, import_core4.createChemElement)(atom.symbol, {
469
+ count: (atom.count ?? 1) * multiplier,
470
+ isotope: atom.isotope,
471
+ charge: atom.charge,
472
+ chargeSign: atom.chargeSign
473
+ })
474
+ );
475
+ nodes.push((0, import_core4.createChemGroup)(scaledAtoms));
476
+ continue;
459
477
  }
460
478
  }
461
479
  nodes.push((0, import_core4.createChemGroup)(groupAtoms));
@@ -473,8 +491,12 @@ var ChemicalParser = class {
473
491
  const chargeMatch = chargeStr.match(/^(\d*)([+\-])$/);
474
492
  if (chargeMatch) {
475
493
  const el = lastNode;
476
- el.charge = parseInt(chargeMatch[1] || "1", 10);
477
- el.chargeSign = chargeMatch[2];
494
+ nodes[nodes.length - 1] = (0, import_core4.createChemElement)(el.symbol, {
495
+ count: el.count,
496
+ isotope: el.isotope,
497
+ charge: parseInt(chargeMatch[1] || "1", 10),
498
+ chargeSign: chargeMatch[2]
499
+ });
478
500
  }
479
501
  }
480
502
  continue;
@@ -495,6 +517,7 @@ var ChemicalParser = class {
495
517
  };
496
518
 
497
519
  // src/registry.ts
520
+ var import_core5 = require("@sciexpr/core");
498
521
  var ParserRegistry = class {
499
522
  parsers = /* @__PURE__ */ new Map();
500
523
  defaultParsers;
@@ -535,6 +558,9 @@ var ParserRegistry = class {
535
558
  * @returns 解析后的 AST
536
559
  */
537
560
  parse(input, options) {
561
+ if (!input || input.trim().length === 0) {
562
+ return (0, import_core5.createRoot)(import_core5.ExpressionKind.Text, []);
563
+ }
538
564
  if (options?.hint && options.hint !== "auto") {
539
565
  const parser2 = this.parsers.get(options.hint);
540
566
  if (parser2) {
@@ -545,6 +571,9 @@ var ParserRegistry = class {
545
571
  if (parser) {
546
572
  return parser.parse(input, options);
547
573
  }
574
+ if (options?.hint === "physics") {
575
+ return this.defaultParsers[0].parse(input, options);
576
+ }
548
577
  return this.defaultParsers[0].parse(input, options);
549
578
  }
550
579
  /** 列出所有已注册的解析器 ID */
package/dist/index.d.cts CHANGED
@@ -14,7 +14,7 @@ interface IParser {
14
14
  /** 解析选项 */
15
15
  interface ParseOptions {
16
16
  /** 输入格式提示 */
17
- hint?: 'latex' | 'chemical' | 'unicode' | 'auto';
17
+ hint?: 'latex' | 'chemical' | 'physics' | 'unicode' | 'auto';
18
18
  /** 是否为 display 模式 */
19
19
  displayMode?: boolean;
20
20
  /** 自定义宏 */
@@ -83,6 +83,11 @@ declare class LatexParser implements IParser {
83
83
  private displayMode;
84
84
  canParse(input: string): boolean;
85
85
  parse(input: string, options?: ParseOptions): ExpressionRoot;
86
+ /**
87
+ * Parse a sequence of expressions.
88
+ * @param stopType Optional token type to stop at (e.g. CloseBrace inside a group).
89
+ * When set, the loop breaks BEFORE consuming the stop token.
90
+ */
86
91
  private parseExpressions;
87
92
  private parseExpression;
88
93
  /** 处理上/下标:检查 ^ 和 _ 后附加到 base 节点 */
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@ interface IParser {
14
14
  /** 解析选项 */
15
15
  interface ParseOptions {
16
16
  /** 输入格式提示 */
17
- hint?: 'latex' | 'chemical' | 'unicode' | 'auto';
17
+ hint?: 'latex' | 'chemical' | 'physics' | 'unicode' | 'auto';
18
18
  /** 是否为 display 模式 */
19
19
  displayMode?: boolean;
20
20
  /** 自定义宏 */
@@ -83,6 +83,11 @@ declare class LatexParser implements IParser {
83
83
  private displayMode;
84
84
  canParse(input: string): boolean;
85
85
  parse(input: string, options?: ParseOptions): ExpressionRoot;
86
+ /**
87
+ * Parse a sequence of expressions.
88
+ * @param stopType Optional token type to stop at (e.g. CloseBrace inside a group).
89
+ * When set, the loop breaks BEFORE consuming the stop token.
90
+ */
86
91
  private parseExpressions;
87
92
  private parseExpression;
88
93
  /** 处理上/下标:检查 ^ 和 _ 后附加到 base 节点 */
package/dist/index.js CHANGED
@@ -109,8 +109,6 @@ var LatexTokenizer = class {
109
109
  this.pos++;
110
110
  return { type: "ampersand" /* Ampersand */, value: "&", position: start, length: 1 };
111
111
  }
112
- if (ch === "\\" && this.input[this.pos + 1] === "\\") {
113
- }
114
112
  if (/[0-9]/.test(ch)) {
115
113
  this.pos++;
116
114
  while (this.pos < this.input.length && /[0-9.]/.test(this.input[this.pos])) {
@@ -161,9 +159,17 @@ var LatexParser = class {
161
159
  // ==========================================================================
162
160
  // Parse Expressions
163
161
  // ==========================================================================
164
- parseExpressions() {
162
+ /**
163
+ * Parse a sequence of expressions.
164
+ * @param stopType Optional token type to stop at (e.g. CloseBrace inside a group).
165
+ * When set, the loop breaks BEFORE consuming the stop token.
166
+ */
167
+ parseExpressions(stopType) {
165
168
  const exprs = [];
166
169
  while (!this.isEOF()) {
170
+ if (stopType !== void 0 && this.peek()?.type === stopType) {
171
+ break;
172
+ }
167
173
  const expr = this.parseExpression();
168
174
  if (expr) exprs.push(expr);
169
175
  }
@@ -199,6 +205,10 @@ var LatexParser = class {
199
205
  this.advance();
200
206
  expr = createSymbol(token.value, this.classifySymbol(token.value));
201
207
  break;
208
+ case "ampersand" /* Ampersand */:
209
+ this.advance();
210
+ expr = createSymbol("&", SymbolType.Ordinary);
211
+ break;
202
212
  case "number" /* Number */:
203
213
  this.advance();
204
214
  expr = createSymbol(token.value, SymbolType.Number);
@@ -209,13 +219,13 @@ var LatexParser = class {
209
219
  // Skip whitespace
210
220
  case "openParen" /* OpenParen */:
211
221
  this.advance();
212
- const body = this.parseExpressions();
222
+ const body = this.parseExpressions("closeParen" /* CloseParen */);
213
223
  this.skipType("closeParen" /* CloseParen */);
214
224
  expr = createDelimiter("(", ")", body.length === 1 ? body[0] : createGroup(body));
215
225
  break;
216
226
  case "openBracket" /* OpenBracket */:
217
227
  this.advance();
218
- const bracketBody = this.parseExpressions();
228
+ const bracketBody = this.parseExpressions("closeBracket" /* CloseBracket */);
219
229
  this.skipType("closeBracket" /* CloseBracket */);
220
230
  expr = createDelimiter("[", "]", bracketBody.length === 1 ? bracketBody[0] : createGroup(bracketBody));
221
231
  break;
@@ -325,7 +335,7 @@ var LatexParser = class {
325
335
  /** 解析 { expressions } 分组 */
326
336
  parseGroup() {
327
337
  this.skipType("openBrace" /* OpenBrace */);
328
- const children = this.parseExpressions();
338
+ const children = this.parseExpressions("closeBrace" /* CloseBrace */);
329
339
  this.skipType("closeBrace" /* CloseBrace */);
330
340
  return createGroup(children);
331
341
  }
@@ -432,9 +442,17 @@ var ChemicalParser = class {
432
442
  }
433
443
  groupCount = parseInt(numStr, 10);
434
444
  if (groupCount && groupCount > 1) {
435
- for (const atom of groupAtoms) {
436
- atom.count = (atom.count ?? 1) * groupCount;
437
- }
445
+ const multiplier = groupCount;
446
+ const scaledAtoms = groupAtoms.map(
447
+ (atom) => createChemElement(atom.symbol, {
448
+ count: (atom.count ?? 1) * multiplier,
449
+ isotope: atom.isotope,
450
+ charge: atom.charge,
451
+ chargeSign: atom.chargeSign
452
+ })
453
+ );
454
+ nodes.push(createChemGroup(scaledAtoms));
455
+ continue;
438
456
  }
439
457
  }
440
458
  nodes.push(createChemGroup(groupAtoms));
@@ -452,8 +470,12 @@ var ChemicalParser = class {
452
470
  const chargeMatch = chargeStr.match(/^(\d*)([+\-])$/);
453
471
  if (chargeMatch) {
454
472
  const el = lastNode;
455
- el.charge = parseInt(chargeMatch[1] || "1", 10);
456
- el.chargeSign = chargeMatch[2];
473
+ nodes[nodes.length - 1] = createChemElement(el.symbol, {
474
+ count: el.count,
475
+ isotope: el.isotope,
476
+ charge: parseInt(chargeMatch[1] || "1", 10),
477
+ chargeSign: chargeMatch[2]
478
+ });
457
479
  }
458
480
  }
459
481
  continue;
@@ -474,6 +496,7 @@ var ChemicalParser = class {
474
496
  };
475
497
 
476
498
  // src/registry.ts
499
+ import { createRoot as createRoot3, ExpressionKind as ExpressionKind3 } from "@sciexpr/core";
477
500
  var ParserRegistry = class {
478
501
  parsers = /* @__PURE__ */ new Map();
479
502
  defaultParsers;
@@ -514,6 +537,9 @@ var ParserRegistry = class {
514
537
  * @returns 解析后的 AST
515
538
  */
516
539
  parse(input, options) {
540
+ if (!input || input.trim().length === 0) {
541
+ return createRoot3(ExpressionKind3.Text, []);
542
+ }
517
543
  if (options?.hint && options.hint !== "auto") {
518
544
  const parser2 = this.parsers.get(options.hint);
519
545
  if (parser2) {
@@ -524,6 +550,9 @@ var ParserRegistry = class {
524
550
  if (parser) {
525
551
  return parser.parse(input, options);
526
552
  }
553
+ if (options?.hint === "physics") {
554
+ return this.defaultParsers[0].parse(input, options);
555
+ }
527
556
  return this.defaultParsers[0].parse(input, options);
528
557
  }
529
558
  /** 列出所有已注册的解析器 ID */
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@sciexpr/parser",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Scientific Expression Engine - LaTeX & Chemical formula parser",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
- "module": "./dist/index.mjs",
7
+ "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
12
+ "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },
@@ -43,7 +43,7 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "@sciexpr/core": "0.1.0"
46
+ "@sciexpr/core": "0.1.1"
47
47
  },
48
48
  "devDependencies": {
49
49
  "tsup": "^8.0.0",