solve-engine 1.0.0-beta.0 → 1.0.0-beta.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.
Files changed (253) hide show
  1. package/dist/BytecodeBuilder-3egMyuDi.d.cts +274 -0
  2. package/dist/BytecodeBuilder-3egMyuDi.d.ts +274 -0
  3. package/dist/Configuration-fVpd-HC-.d.cts +228 -0
  4. package/dist/Configuration-fVpd-HC-.d.ts +228 -0
  5. package/dist/EngineError-DvPSnhZi.d.cts +304 -0
  6. package/dist/EngineError-DvPSnhZi.d.ts +304 -0
  7. package/dist/Lexer-BJdhlnej.d.cts +531 -0
  8. package/dist/Lexer-D9l4Yrk2.d.ts +531 -0
  9. package/dist/NormalizerRule-BrVoVjmP.d.cts +163 -0
  10. package/dist/NormalizerRule-CEjf1FyD.d.ts +163 -0
  11. package/dist/PackageRegistry-D-Tv_7ca.d.cts +2572 -0
  12. package/dist/PackageRegistry-DaH4aIKP.d.ts +2572 -0
  13. package/dist/Parselet-CK8cQu2a.d.cts +299 -0
  14. package/dist/Parselet-ConOIdRO.d.ts +299 -0
  15. package/dist/ScopeManager-B5k0nk05.d.ts +559 -0
  16. package/dist/ScopeManager-CKfQtl7l.d.cts +559 -0
  17. package/dist/Token-0jpvUdPY.d.cts +245 -0
  18. package/dist/Token-0jpvUdPY.d.ts +245 -0
  19. package/dist/VMCheckpoints-BmDwIXgK.d.ts +171 -0
  20. package/dist/VMCheckpoints-CqFuLwrT.d.cts +171 -0
  21. package/dist/Value-KtAQNHur.d.cts +295 -0
  22. package/dist/Value-KtAQNHur.d.ts +295 -0
  23. package/dist/chunk-2RWAXT6O.js +3 -0
  24. package/dist/chunk-2RWAXT6O.js.map +1 -0
  25. package/dist/chunk-34RRD7PC.js +1492 -0
  26. package/dist/chunk-34RRD7PC.js.map +1 -0
  27. package/dist/chunk-3PPFLFH4.js +5097 -0
  28. package/dist/chunk-3PPFLFH4.js.map +1 -0
  29. package/dist/chunk-3VGDNEE3.js +84 -0
  30. package/dist/chunk-3VGDNEE3.js.map +1 -0
  31. package/dist/chunk-3YNVWKR2.cjs +370 -0
  32. package/dist/chunk-3YNVWKR2.cjs.map +1 -0
  33. package/dist/chunk-47LRVGOT.cjs +316 -0
  34. package/dist/chunk-47LRVGOT.cjs.map +1 -0
  35. package/dist/chunk-4MG4XKO2.js +1644 -0
  36. package/dist/chunk-4MG4XKO2.js.map +1 -0
  37. package/dist/chunk-4QADQTWS.js +2392 -0
  38. package/dist/chunk-4QADQTWS.js.map +1 -0
  39. package/dist/chunk-53B6KDDJ.cjs +1651 -0
  40. package/dist/chunk-53B6KDDJ.cjs.map +1 -0
  41. package/dist/chunk-5X2PTP6F.cjs +104 -0
  42. package/dist/chunk-5X2PTP6F.cjs.map +1 -0
  43. package/dist/chunk-5YEMOYSE.js +264 -0
  44. package/dist/chunk-5YEMOYSE.js.map +1 -0
  45. package/dist/chunk-64W6GLLZ.js +481 -0
  46. package/dist/chunk-64W6GLLZ.js.map +1 -0
  47. package/dist/chunk-6BKTCEUP.cjs +233 -0
  48. package/dist/chunk-6BKTCEUP.cjs.map +1 -0
  49. package/dist/chunk-6NTVRDQV.cjs +1642 -0
  50. package/dist/chunk-6NTVRDQV.cjs.map +1 -0
  51. package/dist/chunk-6RDSQURE.js +290 -0
  52. package/dist/chunk-6RDSQURE.js.map +1 -0
  53. package/dist/chunk-75JP4BZX.js +93 -0
  54. package/dist/chunk-75JP4BZX.js.map +1 -0
  55. package/dist/chunk-B7LXJ6LC.cjs +36 -0
  56. package/dist/chunk-B7LXJ6LC.cjs.map +1 -0
  57. package/dist/chunk-BVAR5J2F.js +257 -0
  58. package/dist/chunk-BVAR5J2F.js.map +1 -0
  59. package/dist/chunk-C4XZV6E7.cjs +269 -0
  60. package/dist/chunk-C4XZV6E7.cjs.map +1 -0
  61. package/dist/chunk-CCJPSHGI.cjs +40 -0
  62. package/dist/chunk-CCJPSHGI.cjs.map +1 -0
  63. package/dist/chunk-CLL7RUQV.cjs +316 -0
  64. package/dist/chunk-CLL7RUQV.cjs.map +1 -0
  65. package/dist/chunk-DM3LMRBC.js +329 -0
  66. package/dist/chunk-DM3LMRBC.js.map +1 -0
  67. package/dist/chunk-DUEZFTK2.js +38 -0
  68. package/dist/chunk-DUEZFTK2.js.map +1 -0
  69. package/dist/chunk-EBSPLUW4.cjs +489 -0
  70. package/dist/chunk-EBSPLUW4.cjs.map +1 -0
  71. package/dist/chunk-EHAHVROS.cjs +261 -0
  72. package/dist/chunk-EHAHVROS.cjs.map +1 -0
  73. package/dist/chunk-EIGTWK5N.js +167 -0
  74. package/dist/chunk-EIGTWK5N.js.map +1 -0
  75. package/dist/chunk-EPOXXJBK.js +96 -0
  76. package/dist/chunk-EPOXXJBK.js.map +1 -0
  77. package/dist/chunk-FEAIOR7N.cjs +91 -0
  78. package/dist/chunk-FEAIOR7N.cjs.map +1 -0
  79. package/dist/chunk-G535KJEG.js +312 -0
  80. package/dist/chunk-G535KJEG.js.map +1 -0
  81. package/dist/chunk-GCM5JHMX.js +88 -0
  82. package/dist/chunk-GCM5JHMX.js.map +1 -0
  83. package/dist/chunk-GOLDJNMZ.js +368 -0
  84. package/dist/chunk-GOLDJNMZ.js.map +1 -0
  85. package/dist/chunk-GW32KPCU.cjs +360 -0
  86. package/dist/chunk-GW32KPCU.cjs.map +1 -0
  87. package/dist/chunk-HTXVVJRA.cjs +1549 -0
  88. package/dist/chunk-HTXVVJRA.cjs.map +1 -0
  89. package/dist/chunk-HWSZJQCI.js +311 -0
  90. package/dist/chunk-HWSZJQCI.js.map +1 -0
  91. package/dist/chunk-IF532O7C.js +101 -0
  92. package/dist/chunk-IF532O7C.js.map +1 -0
  93. package/dist/chunk-J73SJHR3.cjs +2415 -0
  94. package/dist/chunk-J73SJHR3.cjs.map +1 -0
  95. package/dist/chunk-JBSYC7BB.cjs +7473 -0
  96. package/dist/chunk-JBSYC7BB.cjs.map +1 -0
  97. package/dist/chunk-KVILKGMS.js +228 -0
  98. package/dist/chunk-KVILKGMS.js.map +1 -0
  99. package/dist/chunk-LIPPNDBE.js +258 -0
  100. package/dist/chunk-LIPPNDBE.js.map +1 -0
  101. package/dist/chunk-LR7YASZF.cjs +172 -0
  102. package/dist/chunk-LR7YASZF.cjs.map +1 -0
  103. package/dist/chunk-M4N5GA5Z.cjs +309 -0
  104. package/dist/chunk-M4N5GA5Z.cjs.map +1 -0
  105. package/dist/chunk-M5LX5AOO.js +7459 -0
  106. package/dist/chunk-M5LX5AOO.js.map +1 -0
  107. package/dist/chunk-MZOVP3WI.cjs +96 -0
  108. package/dist/chunk-MZOVP3WI.cjs.map +1 -0
  109. package/dist/chunk-N5YFR4UP.cjs +10 -0
  110. package/dist/chunk-N5YFR4UP.cjs.map +1 -0
  111. package/dist/chunk-NGBBNL4K.cjs +4 -0
  112. package/dist/chunk-NGBBNL4K.cjs.map +1 -0
  113. package/dist/chunk-NH2O2AUR.js +146 -0
  114. package/dist/chunk-NH2O2AUR.js.map +1 -0
  115. package/dist/chunk-NLNDMWZC.js +31 -0
  116. package/dist/chunk-NLNDMWZC.js.map +1 -0
  117. package/dist/chunk-NMCRQP3Z.cjs +778 -0
  118. package/dist/chunk-NMCRQP3Z.cjs.map +1 -0
  119. package/dist/chunk-NMD5VRN4.cjs +1327 -0
  120. package/dist/chunk-NMD5VRN4.cjs.map +1 -0
  121. package/dist/chunk-OC6LTXYS.cjs +286 -0
  122. package/dist/chunk-OC6LTXYS.cjs.map +1 -0
  123. package/dist/chunk-OT6OJY7C.cjs +114 -0
  124. package/dist/chunk-OT6OJY7C.cjs.map +1 -0
  125. package/dist/chunk-PFSDGGYL.js +8 -0
  126. package/dist/chunk-PFSDGGYL.js.map +1 -0
  127. package/dist/chunk-QNJ4ACRT.cjs +155 -0
  128. package/dist/chunk-QNJ4ACRT.cjs.map +1 -0
  129. package/dist/chunk-QWY3VEZN.js +1637 -0
  130. package/dist/chunk-QWY3VEZN.js.map +1 -0
  131. package/dist/chunk-RFYD5TJE.js +111 -0
  132. package/dist/chunk-RFYD5TJE.js.map +1 -0
  133. package/dist/chunk-RIN643A3.js +354 -0
  134. package/dist/chunk-RIN643A3.js.map +1 -0
  135. package/dist/chunk-RZCWSXTA.cjs +5122 -0
  136. package/dist/chunk-RZCWSXTA.cjs.map +1 -0
  137. package/dist/chunk-SDGRK7EP.js +774 -0
  138. package/dist/chunk-SDGRK7EP.js.map +1 -0
  139. package/dist/chunk-VEKIWQ3F.cjs +39 -0
  140. package/dist/chunk-VEKIWQ3F.cjs.map +1 -0
  141. package/dist/chunk-WQTTOGXC.cjs +99 -0
  142. package/dist/chunk-WQTTOGXC.cjs.map +1 -0
  143. package/dist/chunk-X6K3HUOY.cjs +86 -0
  144. package/dist/chunk-X6K3HUOY.cjs.map +1 -0
  145. package/dist/chunk-XJCMXB2E.cjs +333 -0
  146. package/dist/chunk-XJCMXB2E.cjs.map +1 -0
  147. package/dist/chunk-XVWCOTR6.js +1324 -0
  148. package/dist/chunk-XVWCOTR6.js.map +1 -0
  149. package/dist/chunk-YLJ4XMA6.js +33 -0
  150. package/dist/chunk-YLJ4XMA6.js.map +1 -0
  151. package/dist/chunk-Z2XP767T.cjs +35 -0
  152. package/dist/chunk-Z2XP767T.cjs.map +1 -0
  153. package/dist/chunk-Z4X2RWOF.js +37 -0
  154. package/dist/chunk-Z4X2RWOF.js.map +1 -0
  155. package/dist/constants.cjs +22 -0
  156. package/dist/constants.cjs.map +1 -0
  157. package/dist/constants.d.cts +12 -0
  158. package/dist/constants.d.ts +12 -0
  159. package/dist/constants.js +5 -0
  160. package/dist/constants.js.map +1 -0
  161. package/dist/engine.cjs +79 -0
  162. package/dist/engine.cjs.map +1 -0
  163. package/dist/engine.d.cts +587 -0
  164. package/dist/engine.d.ts +587 -0
  165. package/dist/engine.js +34 -0
  166. package/dist/engine.js.map +1 -0
  167. package/dist/errors.cjs +81 -0
  168. package/dist/errors.cjs.map +1 -0
  169. package/dist/errors.d.cts +95 -0
  170. package/dist/errors.d.ts +95 -0
  171. package/dist/errors.js +4 -0
  172. package/dist/errors.js.map +1 -0
  173. package/dist/format.cjs +171 -0
  174. package/dist/format.cjs.map +1 -0
  175. package/dist/format.d.cts +51 -0
  176. package/dist/format.d.ts +51 -0
  177. package/dist/format.js +168 -0
  178. package/dist/format.js.map +1 -0
  179. package/dist/index.cjs +97 -0
  180. package/dist/index.cjs.map +1 -0
  181. package/dist/index.d.cts +152 -0
  182. package/dist/index.d.ts +152 -0
  183. package/dist/index.js +75 -0
  184. package/dist/index.js.map +1 -0
  185. package/dist/language.cjs +308 -0
  186. package/dist/language.cjs.map +1 -0
  187. package/dist/language.d.cts +110 -0
  188. package/dist/language.d.ts +110 -0
  189. package/dist/language.js +287 -0
  190. package/dist/language.js.map +1 -0
  191. package/dist/lexer.cjs +64 -0
  192. package/dist/lexer.cjs.map +1 -0
  193. package/dist/lexer.d.cts +56 -0
  194. package/dist/lexer.d.ts +56 -0
  195. package/dist/lexer.js +11 -0
  196. package/dist/lexer.js.map +1 -0
  197. package/dist/normalizer.cjs +40 -0
  198. package/dist/normalizer.cjs.map +1 -0
  199. package/dist/normalizer.d.cts +364 -0
  200. package/dist/normalizer.d.ts +364 -0
  201. package/dist/normalizer.js +11 -0
  202. package/dist/normalizer.js.map +1 -0
  203. package/dist/packages.cjs +121 -0
  204. package/dist/packages.cjs.map +1 -0
  205. package/dist/packages.d.cts +691 -0
  206. package/dist/packages.d.ts +691 -0
  207. package/dist/packages.js +24 -0
  208. package/dist/packages.js.map +1 -0
  209. package/dist/parser.cjs +55 -0
  210. package/dist/parser.cjs.map +1 -0
  211. package/dist/parser.d.cts +46 -0
  212. package/dist/parser.d.ts +46 -0
  213. package/dist/parser.js +10 -0
  214. package/dist/parser.js.map +1 -0
  215. package/dist/pipeline-CrsRPmsv.d.ts +285 -0
  216. package/dist/pipeline-DPUQt7yL.d.cts +285 -0
  217. package/dist/resolvers.cjs +13 -0
  218. package/dist/resolvers.cjs.map +1 -0
  219. package/dist/resolvers.d.cts +111 -0
  220. package/dist/resolvers.d.ts +111 -0
  221. package/dist/resolvers.js +4 -0
  222. package/dist/resolvers.js.map +1 -0
  223. package/dist/services.cjs +21 -0
  224. package/dist/services.cjs.map +1 -0
  225. package/dist/services.d.cts +30 -0
  226. package/dist/services.d.ts +30 -0
  227. package/dist/services.js +4 -0
  228. package/dist/services.js.map +1 -0
  229. package/dist/uom.cjs +56 -0
  230. package/dist/uom.cjs.map +1 -0
  231. package/dist/uom.d.cts +224 -0
  232. package/dist/uom.d.ts +224 -0
  233. package/dist/uom.js +11 -0
  234. package/dist/uom.js.map +1 -0
  235. package/dist/utilities.cjs +32 -0
  236. package/dist/utilities.cjs.map +1 -0
  237. package/dist/utilities.d.cts +113 -0
  238. package/dist/utilities.d.ts +113 -0
  239. package/dist/utilities.js +7 -0
  240. package/dist/utilities.js.map +1 -0
  241. package/dist/variables.cjs +17 -0
  242. package/dist/variables.cjs.map +1 -0
  243. package/dist/variables.d.cts +87 -0
  244. package/dist/variables.d.ts +87 -0
  245. package/dist/variables.js +4 -0
  246. package/dist/variables.js.map +1 -0
  247. package/dist/vm.cjs +284 -0
  248. package/dist/vm.cjs.map +1 -0
  249. package/dist/vm.d.cts +68 -0
  250. package/dist/vm.d.ts +68 -0
  251. package/dist/vm.js +194 -0
  252. package/dist/vm.js.map +1 -0
  253. package/package.json +4 -4
@@ -0,0 +1,531 @@
1
+ import { T as Token } from './Token-0jpvUdPY.cjs';
2
+
3
+ /** Trie node for multi-word phrase matching. */
4
+ interface PhraseNode {
5
+ /** Complete phrase token type (null = intermediate node) */
6
+ type?: string;
7
+ children: Map<string, PhraseNode>;
8
+ }
9
+ /**
10
+ * The optimized lookup structure built by TokenClassRegistry.build().
11
+ * Consumed by the Lexer for O(1) keyword → token type lookups and
12
+ * O(word-count) phrase matching.
13
+ */
14
+ interface TokenLookup {
15
+ /** Lowercase keyword → token type. O(1) Map lookup. */
16
+ keywordToType: Map<string, string>;
17
+ /** Phrase trie for multi-word matching. Root node with children maps.
18
+ * Null if no phrases registered. */
19
+ phraseTrie: PhraseNode | null;
20
+ /** Set of lowercase first-words of all registered phrases.
21
+ * Used by the lexer to emit IDENT (not a phrase keyword) for words
22
+ * that start multi-word phrases, deferring to the PhraseMatcher.
23
+ *
24
+ * Example: "to" is in phraseStartWords because "to the power of" is a phrase.
25
+ * When the lexer sees "to", it emits IDENT and lets the phrase matcher
26
+ * combine "to the power of" into a single CARET token.
27
+ *
28
+ * This prevents plugins from accidentally overriding phrase-start words.
29
+ */
30
+ phraseStartWords: Set<string>;
31
+ /** Case-sensitive unit names for UNIT fallback after keyword lookup fails. */
32
+ unitNames: ReadonlySet<string>;
33
+ }
34
+
35
+ /** What a line is, structurally, before anything tries to evaluate it. */
36
+ type MarkdownLineType = 'expression' | 'prose' | 'heading' | 'blockquote' | 'list' | 'code_fence' | 'math_fence' | 'table' | 'table_separator' | 'hr' | 'wikilink' | 'comment' | 'empty';
37
+ /**
38
+ * What a line is and whether it holds anything to evaluate.
39
+ *
40
+ * Produced by a character-level scan that never consults the keyword, unit or
41
+ * operator tables, so any lexer gives the same answer for the same line. See
42
+ * `__tests__/lexer/LineClassificationIsVocabularyIndependent.spec.ts`.
43
+ */
44
+ interface LineClassification {
45
+ /** The type of this markdown line */
46
+ type: MarkdownLineType;
47
+ /** Whether this line should be skipped (no expression evaluation) */
48
+ skip: boolean;
49
+ /** Whether the line contains inline solve markers (`s`...``) */
50
+ hasInlineSolve: boolean;
51
+ }
52
+ /** Inline solve position with precise coordinates */
53
+ interface InlineSolveSpan {
54
+ /** Character offset of the `s`` marker */
55
+ start: number;
56
+ /** Character offset past the closing `` ` `` */
57
+ end: number;
58
+ /** The expression text between the backticks */
59
+ expression: string;
60
+ /** 1-based column of the `s`` marker */
61
+ columnNumber: number;
62
+ /** Token index of INLINE_SOLVE_START in the line's token array (set during tokenization). */
63
+ startTokenIndex?: number;
64
+ /** Token index of closing BACKTICK_OPEN in the line's token array (set during tokenization). */
65
+ endTokenIndex?: number;
66
+ }
67
+ /**
68
+ * Result from a single line processed by scanDocument().
69
+ * Combines line classification, tokenized tokens, and inline solve spans
70
+ * into a single structure, eliminating the need for separate classifyLine()
71
+ * findInlineSolves(), and per-line Lexer.reset() calls.
72
+ */
73
+ interface ScanLineResult {
74
+ /** The raw line text (without trailing newline). */
75
+ text: string;
76
+ /** 1-based line number within the document. */
77
+ lineNumber: number;
78
+ /** Character offset of the line start within the document. */
79
+ startOffset: number;
80
+ /** Character offset of the line end (before newline). */
81
+ endOffset: number;
82
+ /** The line classification. */
83
+ classification: LineClassification;
84
+ /** Tokenized tokens (empty array if line is skipped). */
85
+ tokens: Token[];
86
+ /** Inline solve spans found in this line (empty if none). */
87
+ inlineSolves: InlineSolveSpan[];
88
+ }
89
+ /**
90
+ * A token, as the lexer produces it.
91
+ *
92
+ * A class rather than an object literal because tokens are created on every
93
+ * keystroke and a shared hidden class keeps that path predictable for the
94
+ * engine running it.
95
+ */
96
+ declare class LexerToken implements Token {
97
+ type: string;
98
+ typeId: number;
99
+ value: string;
100
+ text: string;
101
+ offset: number;
102
+ lineBreaks: number;
103
+ line: number;
104
+ col: number;
105
+ constructor(type: string, typeId: number, value: string, text: string, offset: number, lineBreaks: number, line: number, col: number);
106
+ }
107
+ /**
108
+ * Plugin interface for extending the ExpressionLexer with custom tokens.
109
+ *
110
+ * Plugins can register:
111
+ * - `keywords`: Map identifier strings to custom token types (checked after locale keywords).
112
+ * - `operators`: Map multi-character operator sequences to custom token types.
113
+ * - `units`: Register additional unit identifiers (checked alongside built-in units).
114
+ *
115
+ * Multi-word phrase matching has been moved to the TokenNormalizer post-lexer
116
+ * stage. To register phrase patterns, use `IEnginePackage.normalizerRules` instead.
117
+ *
118
+ * All registrations are additive, built-in patterns still work.
119
+ */
120
+ interface LexerVocabulary {
121
+ /**
122
+ * Keyword → tokenType mappings. Each key is a lowercase identifier that,
123
+ * when encountered, will emit the specified token type instead of IDENT.
124
+ * These are checked AFTER the locale's built-in keywordMap, so locale
125
+ * keywords take priority.
126
+ */
127
+ keywords?: Record<string, string>;
128
+ /**
129
+ * Multi-character operator → tokenType mappings. Each key is the exact
130
+ * character sequence (e.g., "::", "->", "=>") and the value is the token
131
+ * type to emit. Two-character operators take priority during matching.
132
+ * Built-in operators (==, !=, >=, <=, <<, >>) always take priority.
133
+ */
134
+ operators?: Record<string, string>;
135
+ /**
136
+ * Additional unit identifiers to recognize (e.g., "gp", "osrs", "tile").
137
+ * These are checked alongside the built-in `knownUnits` set.
138
+ */
139
+ units?: string[];
140
+ /**
141
+ * Whole-line patterns matched against the RAW line text, BEFORE any
142
+ * per-character tokenization begins.
143
+ *
144
+ * Every other extension point in this file (`keywords`/`operators`/
145
+ * `units`, plus `IEnginePackage.phrases`/`normalizerRules`) transforms
146
+ * a token STREAM -- they all assume the line is, at some granularity,
147
+ * valid Solve syntax. This hook exists for the one shape that isn't:
148
+ * a package whose grammar captures arbitrary free-form text terminated
149
+ * by a fixed marker (e.g. a natural-language query ending in `= ?`),
150
+ * where the text itself ("distance to the moon") would never tokenize
151
+ * or parse as a normal expression and must be captured verbatim
152
+ * instead -- see `packages/knowledge/` for the reference use.
153
+ *
154
+ * Each entry's `pattern` is tested (via `RegExp.exec`) against the
155
+ * full, untrimmed line text. If it matches AND capture group 1 is
156
+ * non-empty after trimming, the ENTIRE line becomes a single
157
+ * synthetic token of `tokenType` whose `value`/`text` is the trimmed
158
+ * capture group -- the character-by-character scanner never runs for
159
+ * that line. Patterns are tried in registration order; the first
160
+ * match wins. A package registering a rule here still needs a
161
+ * `prefixParselets` entry for `tokenType` to actually consume the
162
+ * resulting token.
163
+ *
164
+ * Because this bypasses tokenization entirely, a matching line can
165
+ * contain characters that would otherwise be lexer errors (unmatched
166
+ * quotes, stray symbols, ...) -- by design, since the whole point is
167
+ * to hand the package raw text the normal pipeline was never meant to
168
+ * parse.
169
+ */
170
+ rawLinePatterns?: Array<{
171
+ pattern: RegExp;
172
+ tokenType: string;
173
+ }>;
174
+ }
175
+ /**
176
+ * Character-by-character tokenizer for expression text.
177
+ *
178
+ * Scans a raw line/expression string into a stream of typed tokens
179
+ * (numbers, identifiers, operators, units, keywords, ...), handling
180
+ * markdown-line classification (`classifyLine`), inline `` s`...` `` solve
181
+ * spans, and package-contributed vocabulary (registered via
182
+ * {@link registerVocabulary}/{@link unregisterVocabulary}, keywords
183
+ * operators, and units a package wants recognized as their own token
184
+ * types rather than falling through to generic identifiers).
185
+ *
186
+ * Most consumers should use the higher-level {@link Lexer} wrapper, which
187
+ * adds streaming `next()`/`peek()` access over this class's scan results.
188
+ */
189
+ declare class ExpressionLexer {
190
+ private static readonly CHAR_CLASS;
191
+ /**
192
+ * Configured TokenLookup from TokenClassRegistry. When set, replaces
193
+ * the internal keyword map and unit set with registry-built equivalents.
194
+ * Enables data-driven keyword/unit registration across locale keywords,
195
+ * provider keywords, and plugins.
196
+ *
197
+ * Set at construction time via the constructor parameter. Plugin-registered
198
+ * keywords/units (via registerVocabulary()) are checked alongside
199
+ * the configuredLookup, neither source is bypassed.
200
+ */ private configuredLookup;
201
+ private input;
202
+ private pos;
203
+ private len;
204
+ private line;
205
+ private lineStartPos;
206
+ private keywordMap;
207
+ private mergedKeywords;
208
+ private mergedUnits;
209
+ private pluginKeywordMap;
210
+ private pluginOperators;
211
+ private pluginUnits;
212
+ private hasPluginOps;
213
+ private localeCode;
214
+ private locale;
215
+ /**
216
+ * Inline solve spans collected during the most recent tokenization pass.
217
+ * Populated by [Symbol.iterator]() and consumed by scanDocument().
218
+ */
219
+ _inlineSolveSpans: InlineSolveSpan[];
220
+ private pluginRawLinePatterns;
221
+ /**
222
+ * If a `rawLinePatterns` rule matches the FULL text most recently passed
223
+ * to {@link reset}, this holds the single synthetic token that
224
+ * {@link tokenizeAll} should return instead of running the
225
+ * character-by-character scanner. Cleared (re-evaluated) on every
226
+ * {@link reset} call. `null` when no plugin registered any raw-line
227
+ * patterns, or none matched, the overwhelmingly common case, checked
228
+ * with a `length === 0` guard before ever touching this field so a
229
+ * plugin-free lexer pays zero cost for the feature.
230
+ */
231
+ private pendingRawLineToken;
232
+ /**
233
+ * Test `text` against every registered `rawLinePatterns` rule, in
234
+ * registration order. Returns a synthetic token for the first rule
235
+ * whose `pattern` matches AND whose capture group 1 is non-empty after
236
+ * trimming; returns `null` if no rule matches (the normal
237
+ * character-by-character scanner should run instead).
238
+ */
239
+ private matchRawLine;
240
+ /** Rebuild merged keyword and unit collections after plugin registration. */
241
+ private rebuildMergedCollections;
242
+ constructor(localeCode?: string, lookup?: TokenLookup);
243
+ /**
244
+ * Register a plugin to extend the lexer with custom tokens.
245
+ *
246
+ * All registrations are additive, built-in patterns still work.
247
+ * Keywords, operators, and units from the plugin are merged
248
+ * with existing ones. Calling multiple times adds more entries.
249
+ *
250
+ * Note: multi-word phrases are now handled by the TokenNormalizer
251
+ * (see `IEnginePackage.normalizerRules`), not the lexer.
252
+ *
253
+ * Built-in tokens CANNOT be overridden. Throws a EngineError if the
254
+ * plugin attempts to register a keyword, operator, or unit
255
+ * that conflicts with a built-in one.
256
+ */
257
+ registerVocabulary(plugin: LexerVocabulary): void;
258
+ /**
259
+ * Unregister a plugin, removing its custom tokens from the lexer.
260
+ *
261
+ * This is the inverse of registerVocabulary(). All keywords, operators,
262
+ * and units registered by the plugin are removed. After
263
+ * unregistration, those tokens will revert to their default behavior
264
+ * (e.g., keywords become IDENT, operators become ERROR).
265
+ *
266
+ * Calling unregisterVocabulary with a plugin that was never registered
267
+ * is safe, it simply has no effect.
268
+ */
269
+ unregisterVocabulary(plugin: LexerVocabulary): void;
270
+ reset(input: string): void;
271
+ /**
272
+ * Scan a full document text in a single pass, classifying each line and
273
+ * tokenizing non-skipped lines.
274
+ *
275
+ * Replaces the separate classifyLine() + findInlineSolves() + per-line
276
+ * reset() + tokenizeAll() pattern with a single character-by-character
277
+ * walk through the entire document. Key benefits:
278
+ *
279
+ * - **Single reset()**: `this.pos`, `this.len`, `this.line`, and
280
+ * `this.lineStartPos` are set once for the whole document, not per-line.
281
+ * - **Single classification**: classifyLine() runs once per line inline;
282
+ * skipped lines are jumped over without tokenization.
283
+ * - **Shared tokenization**: Non-skipped lines are tokenized using the
284
+ * existing state machine, yielding Token[] without per-line reset().
285
+ * - **Inline solve detection**: findInlineSolves() is called only for
286
+ * lines that classifyLine() marks as having inline solves.
287
+ *
288
+ * Tokenization is scoped to each line by temporarily restricting
289
+ * `this.len` to the line end position, so the [Symbol.iterator]
290
+ * generator naturally stops at the line boundary. After tokenization,
291
+ * `this.len` is restored and `this.pos` advances past the newline.
292
+ *
293
+ * @param text The full document text (with newlines).
294
+ * @returns Array of ScanLineResult, one per line, in document order.
295
+ */
296
+ scanDocument(text: string): ScanLineResult[];
297
+ /**
298
+ * Tokenize an expression string into an array of Tokens.
299
+ *
300
+ * Delegates to the lazy [Symbol.iterator]() generator and collects all
301
+ * yielded tokens via Array.from(). For memory-sensitive use cases, prefer
302
+ * iterating the lexer directly with for...of to avoid array allocation.
303
+ *
304
+ * Optimizations:
305
+ * - CHAR_CLASS jump table (Uint8Array) → switch on small integers
306
+ * - Direct character-code dispatch (c0 cached pattern)
307
+ * - Mathematical digit parsing (integer math, not slice+parseFloat)
308
+ * - Inline operator tokenizer with two-char peek-ahead
309
+ * - Whitespace eliminated in-lexer (never emitted)
310
+ * - 0-char and 1-char fast paths
311
+ */
312
+ tokenizeAll(): Token[];
313
+ /**
314
+ * Lazy token-by-token generator. Yields each token without allocating an
315
+ * intermediate Token[] array. Supports for...of and spread usage.
316
+ *
317
+ * Usage:
318
+ * for (const t of lexer) { ... } // lazy, no array allocation
319
+ * const tokens = [...lexer]; // materializes via spread
320
+ * const tokens = lexer.tokenizeAll(); // materializes via Array.from()
321
+ *
322
+ * IMPORTANT: This generator captures `this.len` ONCE at creation time
323
+ * (const len = this.len). `scanDocument()` relies on this behavior to
324
+ * scope tokenization to a single line by temporarily restricting
325
+ * `this.len` to the line end position before creating the iterator.
326
+ * Do NOT refactor to re-read `this.len` mid-loop without also updating
327
+ * `scanDocument()`.
328
+ */
329
+ [Symbol.iterator](): Generator<Token, void, undefined>;
330
+ /**
331
+ * Character-by-character number parsing.
332
+ *
333
+ * Supports: integers, floats, scientific notation (1.5e10, 1.5e-10),
334
+ * hex (0xFF), binary (0b1010), octal (0o17), BigInt suffix (123n),
335
+ * thousands separators (1,234 or 1.234.567).
336
+ *
337
+ * Returns a LexerToken and advances `this.pos` past the number.
338
+ */
339
+ private tokenizeNumber;
340
+ /**
341
+ * Reads [a-zA-Z_][a-zA-Z0-9_]* and resolves to:
342
+ * - A unit type (via knownUnits, case-sensitive)
343
+ * - A keyword type (via locale keywordMap, case-insensitive)
344
+ * - IDENT if none of the above
345
+ *
346
+ * Multi-word phrases (e.g., "to the power of") are handled by the
347
+ * TokenNormalizer post-lexer pass, not the lexer.
348
+ */
349
+ private tokenizeIdentifier;
350
+ /**
351
+ * Peek past in-expression whitespace (space, tab) from `pos` to check
352
+ * if the next significant character is '('.
353
+ */
354
+ private isFollowedByLParen;
355
+ /**
356
+ * Reads an operator/punctuation token.
357
+ * Handles two-char operators (==, !=, >=, <=, **) and the special
358
+ * cases << (LSHIFT) and >> (RSHIFT).
359
+ */
360
+ private tokenizeOperator;
361
+ /**
362
+ * Reads a double-quoted string literal. Supports backslash escapes.
363
+ */
364
+ private tokenizeString;
365
+ /**
366
+ * L1 expression gating: quickly determine if a line contains any
367
+ * characters that indicate an expression (digits, operators, currency,
368
+ * backticks, parentheses, etc.).
369
+ *
370
+ * Pure prose lines (e.g., "The quick brown fox jumps over the lazy dog")
371
+ * return false and can be skipped without full tokenization (L2).
372
+ *
373
+ * This is a fast character-by-character scan that stops at the first
374
+ * expression indicator. Called once per line in classifyFromPositions().
375
+ */
376
+ static hasExpressionIndicators(input: string, start: number, end: number): boolean;
377
+ /**
378
+ * Classify a line by its character positions within this.input.
379
+ * Reads directly from this.input using start/end boundaries.
380
+ * DOES NOT modify this.pos, purely a read-only classifier.
381
+ */
382
+ private classifyFromPositions;
383
+ /**
384
+ * Classify a single line of markdown text.
385
+ */
386
+ classifyLine(lineText: string): LineClassification;
387
+ /**
388
+ * Every keyword this lexer currently recognizes, locale keywords
389
+ * (`pi`, `sqrt`, `convert`, ...) merged with any plugin-contributed ones
390
+ * from `registerVocabulary()` (e.g. a package's custom keywords), mapped to
391
+ * the token type they lex to. A snapshot copy, not a live reference
392
+ * mutating the return value has no effect on the lexer.
393
+ */
394
+ getKeywords(): Record<string, string>;
395
+ /**
396
+ * Find all inline solve markers in a line with precise coordinate mapping.
397
+ */
398
+ findInlineSolves(lineText: string): InlineSolveSpan[];
399
+ /**
400
+ * Reads a comment: # to end of line, or // to end of line.
401
+ */
402
+ private tokenizeComment;
403
+ }
404
+
405
+ /**
406
+ * Lexer state machine modes.
407
+ * - Main: document-level scanning with markdown classification
408
+ * - Inline: expression embedded in markdown inline solve (`s\`...\``)
409
+ * - String: inside a double-quoted string literal
410
+ */
411
+ declare enum LexerState {
412
+ Main = "main",
413
+ Inline = "inline",
414
+ String = "string"
415
+ }
416
+
417
+ /**
418
+ * Editor-agnostic semantic classification for a lexer token.
419
+ *
420
+ * This is the entire contract solve-js's language service exposes to any
421
+ * editor integration (CodeMirror, VS Code, ...): a category, nothing about
422
+ * how it should be rendered. CSS class names, VS Code semantic-token
423
+ * indices, etc. are all downstream adapter concerns. See
424
+ * `language/adapters/`.
425
+ *
426
+ * The built-in categories cover every core (non-plugin) token type. Solve-js
427
+ * packages may contribute additional, open-ended category strings for their
428
+ * own custom token types (see `registerTokenCategory` in TokenCategoryMap.ts)
429
+ *, the `(string & {})` union member keeps those assignable without widening
430
+ * the whole type to a bare `string` and losing autocomplete for the built-ins.
431
+ */
432
+ type TokenCategory = "number" | "string" | "keyword" | "operator" | "comparison" | "bitwise" | "function" | "variable" | "unit" | "datetime" | "vector" | "punctuation" | "error" | (string & {});
433
+
434
+ /**
435
+ * Public tokenizer wrapper around {@link ExpressionLexer}.
436
+ *
437
+ * `ExpressionLexer` does the actual character-by-character scanning;
438
+ * `Lexer` adds a materialized-token-array streaming interface
439
+ * (`next()`/`peek()`) plus line-classification state (`reset()`) so
440
+ * callers can iterate a line's tokens without re-scanning on each peek.
441
+ *
442
+ * Each `ExpressionEngine` instance owns its own `Lexer`, and packages
443
+ * extend it via {@link registerVocabulary} (keywords, operators, units)
444
+ * see `IEnginePackage.lexerVocabulary`.
445
+ */
446
+ declare class Lexer {
447
+ /** Expression-mode lexer (Phase A: V8-optimized, replaces moo) */
448
+ private expressionLexer;
449
+ private currentState;
450
+ private peekedToken;
451
+ private hasPeeked;
452
+ private tokens;
453
+ private tokenIdx;
454
+ /**
455
+ * @param localeCode - Locale code (e.g., "en", "de"). Defaults to "en".
456
+ * @param tokenLookup - Optional TokenLookup from TokenClassRegistry.
457
+ * When provided, configures ExpressionLexer to use registry-built
458
+ * keyword/unit/phrase lookups instead of internal instance maps.
459
+ */
460
+ constructor(localeCode?: string, tokenLookup?: TokenLookup);
461
+ reset(input: string, state?: LexerState): void;
462
+ /**
463
+ * Classify a single line of markdown text (Phase B).
464
+ * Delegates to the ExpressionLexer's character-by-character scanner.
465
+ */
466
+ classifyLine(lineText: string): LineClassification;
467
+ /**
468
+ * Find all inline solve markers in a line (Phase B).
469
+ * Delegates to the ExpressionLexer's character-by-character scanner.
470
+ */
471
+ findInlineSolves(lineText: string): InlineSolveSpan[];
472
+ /**
473
+ * Every keyword this lexer currently recognizes (locale + plugin-contributed),
474
+ * mapped to the token type it lexes to. Delegates to the ExpressionLexer.
475
+ */
476
+ getKeywords(): Record<string, string>;
477
+ next(): Token | undefined;
478
+ peek(): Token | undefined;
479
+ [Symbol.iterator](): Iterator<Token>;
480
+ /**
481
+ * Register a plugin to extend the lexer with custom tokens.
482
+ * Delegates to the underlying ExpressionLexer.
483
+ *
484
+ * @see LexerVocabulary for the supported extension points.
485
+ */
486
+ registerVocabulary(plugin: LexerVocabulary): void;
487
+ /**
488
+ * Unregister a plugin, removing its custom tokens from the lexer.
489
+ * Delegates to the underlying ExpressionLexer.
490
+ */
491
+ unregisterVocabulary(plugin: LexerVocabulary): void;
492
+ /**
493
+ * Reset the lexer for expression-only text, skips the classifyLine()
494
+ * overhead in reset() for callers that already know the input is an
495
+ * evaluable expression (e.g., after isEmptyLine() confirmed non-skip).
496
+ */
497
+ resetExpression(input: string): void;
498
+ /**
499
+ * Scan a full document in one pass, classifying each line and
500
+ * tokenizing non-skipped lines. Delegates to ExpressionLexer.
501
+ *
502
+ * @returns ScanLineResult[], one per line, with classification + tokens.
503
+ */
504
+ scanDocument(text: string): ScanLineResult[];
505
+ getState(): LexerState;
506
+ setState(state: LexerState): void;
507
+ getHighlightTokens(lineText: string): {
508
+ type: string;
509
+ value: string;
510
+ offset: number;
511
+ col: number;
512
+ length: number;
513
+ category: TokenCategory | undefined;
514
+ }[];
515
+ private collectHighlightTokens;
516
+ }
517
+ /**
518
+ * A lexer for operations that do not depend on registered vocabulary.
519
+ *
520
+ * Line classification and inline-solve detection read characters looking for
521
+ * headings, comment markers, fences and backtick spans, and never consult the
522
+ * keyword, unit or operator tables. Every lexer therefore returns the same
523
+ * answer, so the callers that have no engine to ask can use this one. Checked
524
+ * by `__tests__/lexer/LineClassificationIsVocabularyIndependent.spec.ts`.
525
+ *
526
+ * Do not tokenize with this. An engine's own lexer carries the vocabulary its
527
+ * packages registered; this one carries none.
528
+ */
529
+ declare const sharedLexer: Lexer;
530
+
531
+ export { ExpressionLexer as E, type InlineSolveSpan as I, Lexer as L, type MarkdownLineType as M, type ScanLineResult as S, type TokenLookup as T, LexerState as a, LexerToken as b, type LexerVocabulary as c, type LineClassification as d, type TokenCategory as e, sharedLexer as s };