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,299 @@
1
+ import { B as BytecodeBuilder } from './BytecodeBuilder-3egMyuDi.cjs';
2
+ import { T as Token } from './Token-0jpvUdPY.cjs';
3
+ import { D as DiagnosticPipeline } from './pipeline-DPUQt7yL.cjs';
4
+
5
+ /**
6
+ * Dual-keyed ParseletRegistry, accepts both string token types and
7
+ * integer token type IDs for fast dispatch in the Parser hot path.
8
+ *
9
+ * Providers call registerPrefix("NUMBER", ...) with string token types.
10
+ * Internally, we populate both string-keyed and integer-keyed maps so
11
+ * Parser.parseExpression() can use token.typeId (integer) for lookup
12
+ * while diagnostics and error messages use token.type (string).
13
+ *
14
+ * Performance: Integer Map.get() avoids string hashing, saving ~2-5ns
15
+ * per dispatch. With ~10-15 dispatches per expression, that's ~20-75ns.
16
+ */
17
+ declare class ParseletRegistry {
18
+ private prefixParselets;
19
+ private infixParselets;
20
+ private prefixById;
21
+ private infixById;
22
+ /**
23
+ * Register a prefix parselet for `tokenType`.
24
+ *
25
+ * If another parselet is already registered for this token type, it is
26
+ * silently overwritten by default (`Map.set()` semantics), the old
27
+ * parselet is simply unreachable from then on, with no error. This is
28
+ * a real footgun for third-party packages: two packages independently
29
+ * choosing the same custom token type will collide with zero signal
30
+ * about which one "won". Mirrors ResolverRegistry.register()'s and
31
+ * ExpressionEngine.registerPackage()'s existing "warn and replace"
32
+ * pattern for the same class of problem at the resolver-namespace and
33
+ * package-name levels.
34
+ *
35
+ * Note: this warns about registry-level collisions only. It does NOT
36
+ * detect the separate case where `tokenType` is one of PrecedenceParser's
37
+ * Tier-1 fast-path token types (NUMBER, STRING, IDENT, LPAREN, MINUS,
38
+ * PLUS, and the Tier-1 infix operators), those are deliberately kept
39
+ * registered here for introspection/diagnostics even though Tier-1
40
+ * always intercepts them before this registry is consulted (see
41
+ * PrecedenceParser.parsePrefix()'s docs), so warning there would
42
+ * misfire on that intentional, already-documented pattern.
43
+ */
44
+ registerPrefix(tokenType: string, parselet: PrefixParselet): void;
45
+ /** Register an infix parselet for `tokenType`. See {@link registerPrefix} for the collision-warning behavior this mirrors. */
46
+ registerInfix(tokenType: string, parselet: InfixParselet): void;
47
+ /** Iterate all registered prefix parselets for diagnostic display. */
48
+ getAllPrefix(): Array<{
49
+ tokenType: string;
50
+ bindingPower: number;
51
+ category?: string;
52
+ }>;
53
+ /** Iterate all registered infix parselets for diagnostic display. */
54
+ getAllInfix(): Array<{
55
+ tokenType: string;
56
+ leftBindingPower: number;
57
+ rightBindingPower: number;
58
+ category?: string;
59
+ }>;
60
+ /** Number of registered prefix parselets. */
61
+ get prefixCount(): number;
62
+ /** Number of registered infix parselets. */
63
+ get infixCount(): number;
64
+ /**
65
+ * Get prefix parselet by string token type OR integer typeId.
66
+ * Fast path for integer IDs (Parser hot path), fallback for strings
67
+ * (diagnostics, error messages, backwards compatibility).
68
+ */
69
+ getPrefix(tokenType: string | number): PrefixParselet | undefined;
70
+ /**
71
+ * Get infix parselet by string token type OR integer typeId.
72
+ * Fast path for integer IDs (Parser hot path), fallback for strings.
73
+ */
74
+ getInfix(tokenType: string | number): InfixParselet | undefined;
75
+ hasPrefix(tokenType: string): boolean;
76
+ hasInfix(tokenType: string): boolean;
77
+ clear(): void;
78
+ }
79
+ /**
80
+ * A process-wide parselet registry.
81
+ *
82
+ * @deprecated An engine builds its own registry and does not read this one, so
83
+ * registering here reaches nothing that evaluates. It survives for the
84
+ * deprecated {@link PackageRegistry} path only.
85
+ */
86
+ declare const sharedParseletRegistry: ParseletRegistry;
87
+
88
+ /**
89
+ * ── Hybrid Precedence Climbing Parser ─────────────────────────────────────────
90
+ *
91
+ * Two-tier dispatch strategy:
92
+ *
93
+ * Tier 1 (Fast Path): Inline switch on token.typeId for built-in operators.
94
+ * - Prefix: NUMBER, IDENT, LPAREN, MINUS, PLUS, STRING
95
+ * - Infix: checked via static BP_TABLE (Uint8Array indexed by typeId)
96
+ * - No Map.get(), no parselet function call overhead for ~95% of tokens
97
+ *
98
+ * Tier 2 (Plugin Path): ParseletRegistry fallback for custom/plugin tokens.
99
+ * - Prefix: Map.get(tokenTypeId) for PrefixParselet
100
+ * - Infix: Map.get(tokenTypeId) for InfixParselet
101
+ * - Full flexibility for custom syntax
102
+ *
103
+ * The parser emits directly to a BytecodeBuilder, no AST intermediate.
104
+ * Implements the same public API as the legacy Parser class so existing
105
+ * parselets continue to work without modification.
106
+ */
107
+ declare class PrecedenceParser {
108
+ private tokens;
109
+ private current;
110
+ private depth;
111
+ private maxDepth;
112
+ /** Cached registry reference, avoids property chain in hot loop */
113
+ private registry;
114
+ /** BytecodeBuilder, set before each parseExpression call */
115
+ private builder;
116
+ /** Diagnostic pipeline for parselet-matched events */
117
+ private diagnosticPipeline;
118
+ private currentExpression;
119
+ private localeCode;
120
+ /**
121
+ * Static binding power table, built once at module load, shared across all instances.
122
+ * Index = tokenTypeId, value = binding power (0 = not a built-in infix).
123
+ */
124
+ static readonly BP_TABLE: Uint8Array;
125
+ private static readonly NUMBER_ID;
126
+ private static readonly BIGINT_ID;
127
+ private static readonly STRING_ID;
128
+ private static readonly IDENT_ID;
129
+ private static readonly LPAREN_ID;
130
+ private static readonly RPAREN_ID;
131
+ private static readonly MINUS_ID;
132
+ private static readonly PLUS_ID;
133
+ private static readonly KEYWORD_ID;
134
+ private static readonly STAR_ID;
135
+ private static readonly SLASH_ID;
136
+ private static readonly MOD_ID;
137
+ private static readonly CARET_ID;
138
+ private static readonly LSHIFT_ID;
139
+ private static readonly RSHIFT_ID;
140
+ private static readonly BIT_AND_ID;
141
+ private static readonly BIT_OR_ID;
142
+ private static readonly BIT_XOR_ID;
143
+ private static readonly PERCENT_ID;
144
+ private static readonly OF_ID;
145
+ /**
146
+ * Inline opcode map for Tier 1 infix operators.
147
+ * tokenTypeId → OpCode. PERCENT and CARET are handled specially (not in this map).
148
+ */
149
+ private static readonly INFIX_OPCODE;
150
+ constructor(parseletRegistry: ParseletRegistry, maxDepth?: number, localeCode?: string);
151
+ /** Get locale code for NumberParselet to normalize separators */
152
+ getLocaleCode(): string;
153
+ /**
154
+ * Set the diagnostic pipeline for parselet matching events.
155
+ * Cleared after each non-cached parse to avoid holding refs.
156
+ */
157
+ setDiagnosticPipeline(pipeline: DiagnosticPipeline | undefined, expression: string): void;
158
+ /**
159
+ * Load tokens for parsing. Identical to Parser.load().
160
+ *
161
+ * @param hasParens - if false, skips the O(n) paren balance scan (~90% of expressions)
162
+ */
163
+ load(tokens: Token[], hasParens?: boolean): void;
164
+ /**
165
+ * Auto-balance unmatched parentheses.
166
+ */
167
+ private balanceParens;
168
+ /**
169
+ * Parse a full expression starting at the current token position.
170
+ *
171
+ * @param minBp - minimum binding power (precedence climbing threshold).
172
+ * For left-associative operators, the recursive call uses `bp + 1`.
173
+ * For right-associative operators (^), it uses `bp`.
174
+ * @param _builder - accepted for parselet API compatibility; always uses `this.builder`.
175
+ */
176
+ parseExpression(minBp?: number, _builder?: BytecodeBuilder): void;
177
+ /**
178
+ * Parse a prefix token. Built-in tokens (NUMBER, IDENT, LPAREN, etc.) are
179
+ * handled inline with zero registry lookup. Everything else falls through
180
+ * to the parselet registry (Tier 2).
181
+ */
182
+ private parsePrefix;
183
+ /**
184
+ * A single `^`-suffix shape: `matches` peeks ahead (consuming nothing) to
185
+ * check whether this shape starts at the current position; `emit` is only
186
+ * called immediately after `matches` returned true for that SAME position,
187
+ * and is responsible for consuming that shape's own trailing tokens and
188
+ * emitting its bytecode.
189
+ */
190
+ private static readonly CARET_SUFFIX_RULES;
191
+ /**
192
+ * After a `^` token (already consumed by the Tier-1 infix loop above),
193
+ * checks {@link CARET_SUFFIX_RULES} in order for a shape that means
194
+ * something other than ordinary exponentiation: `^T` (transpose) and `^-1`
195
+ * (matrix inverse, LITERALLY the integer exponent `-1`; `^-2`, `^-1.5`
196
+ * etc. still mean ordinary exponentiation). On a match, the rule's `emit`
197
+ * consumes that shape's own tokens and this returns `true`. On no match,
198
+ * consumes NOTHING, returning `false` so the caller falls through to
199
+ * ordinary `EXP` parsing.
200
+ *
201
+ * Every rule dispatches purely on SHAPE, never on operand type (unknowable
202
+ * at parse time): `inv()`'s own handler (`VMBuiltins.ts` index 65) returns
203
+ * `1/x` for a plain Number, byte-identical to what `Math.pow(x, -1)`
204
+ * already computed for `x^-1` before this feature existed, and a real
205
+ * matrix inverse for a Matrix, so `5^-1` still means exactly what it always
206
+ * has; only a Matrix operand actually inverts.
207
+ *
208
+ * Adding a future `^`-suffix shape is a new table entry here, not a new
209
+ * if-block. See the "Tier-1 shape-exception" pattern write-up above for
210
+ * why this table can't instead be a package-registered parselet.
211
+ */
212
+ private tryEmitMatrixCaretOp;
213
+ /**
214
+ * From an LPAREN token index, scan forward tracking paren depth and
215
+ * return the index of the matching RPAREN, or `null` if the parens never
216
+ * balance before the token stream ends. No emission, no position
217
+ * advance. Same class of technique as {@link balanceParens}'s own
218
+ * pre-scan, just exposed mid-parse instead of only at `load()` time.
219
+ * `openIdx` must point AT the LPAREN itself.
220
+ */
221
+ private findMatchingRParen;
222
+ /**
223
+ * Disambiguates a bare `IDENT` immediately followed by `(` between three
224
+ * things, using ONLY a bracket-depth scan (no backtracking, nothing is
225
+ * consumed until the shape is known):
226
+ * - `f(x) = expr`, a DEFINITION: the matching `)` is followed by `=`.
227
+ * - `f(5)`, a CALL to a (possibly not-yet-defined) function: anything
228
+ * else. This was never valid syntax before this feature (a bare
229
+ * `IDENT` immediately before `(` has no pre-existing "plain variable
230
+ * read + separately grouped `(...)`" meaning to preserve, confirmed
231
+ * via `BuiltinNormalizerRules.ts`'s `implicitMultiplyRule()`, which
232
+ * only inserts an implicit `*` for `NUMBER/RPAREN` immediately before
233
+ * `IDENT/LPAREN`, never for a bare `IDENT` immediately before
234
+ * `LPAREN`). Always commits to a CALL; an unregistered name produces a
235
+ * clear `UNDEFINED_FUNCTION` error at VM-execution time, the same
236
+ * forward-reference philosophy `LOAD_VAR`/`UNDEFINED_VARIABLE` already
237
+ * uses, rather than any parse-time registry lookup.
238
+ * If the parens never balance, this falls through to the ordinary
239
+ * `LOAD_VAR` path (a malformed expression surfaces its own parse error
240
+ * shortly after, from the normal expression grammar).
241
+ */
242
+ private parseUserFunctionDefOrCall;
243
+ /**
244
+ * A parameter name, accepted as either `IDENT` or `UNIT`, matches this
245
+ * codebase's established `:name = value` variable-name policy
246
+ * (`VariableParselet.ts` explicitly accepts `UNIT`-typed tokens too, e.g.
247
+ * `:b = 5` for the "b" bits unit) since common short parameter names
248
+ * like `h`/`l`/`b`/`t`/`s`/`m` collide with real unit abbreviations
249
+ * (hour, liter, bits, ton, second, meter, ...) and lex as `UNIT`, not
250
+ * `IDENT`.
251
+ */
252
+ private consumeParamName;
253
+ private parseUserFunctionDefinition;
254
+ private parseUserFunctionCall;
255
+ consume(expectedType?: string): Token;
256
+ match(expectedType: string): boolean;
257
+ peek(): Token | undefined;
258
+ /**
259
+ * Read-only lookahead `offset` tokens past the current position, without
260
+ * consuming anything, `peekAt(0)` is equivalent to {@link peek}.
261
+ * `this.tokens` is a plain in-memory array (not a stream), so this is a
262
+ * simple, safe index read; no rewind/checkpoint mechanism is needed since
263
+ * nothing is consumed.
264
+ */
265
+ peekAt(offset: number): Token | undefined;
266
+ previous(): Token | undefined;
267
+ private advance;
268
+ /** Set the builder to use for the current parse. Called by ExpressionEngine. */
269
+ setBuilder(builder: BytecodeBuilder): void;
270
+ private fireParseletMatched;
271
+ }
272
+
273
+ /**
274
+ * Handles a token appearing where a value is expected.
275
+ *
276
+ * A literal, a prefix operator such as unary minus, or a function name. The
277
+ * parser calls this when the token opens an expression rather than continuing
278
+ * one.
279
+ */
280
+ interface PrefixParselet {
281
+ /** Diagnostic category for this parselet (e.g. "Arithmetic", "Function", "Variable") */
282
+ readonly category: string;
283
+ parse(parser: PrecedenceParser, token: Token, builder: BytecodeBuilder): void;
284
+ }
285
+ /**
286
+ * Handles a token appearing after a value.
287
+ *
288
+ * A binary operator, or anything else that continues an expression already in
289
+ * progress. Its binding power decides how tightly it binds against neighbours.
290
+ */
291
+ interface InfixParselet {
292
+ /** Diagnostic category for this parselet (e.g. "Arithmetic", "UoM") */
293
+ readonly category: string;
294
+ /** Binding power (precedence), property access avoids vtable dispatch in hot loop */
295
+ readonly bindingPower: number;
296
+ parse(parser: PrecedenceParser, left: Token, token: Token, builder: BytecodeBuilder): void;
297
+ }
298
+
299
+ export { type InfixParselet as I, ParseletRegistry as P, PrecedenceParser as a, type PrefixParselet as b, sharedParseletRegistry as s };
@@ -0,0 +1,299 @@
1
+ import { B as BytecodeBuilder } from './BytecodeBuilder-3egMyuDi.js';
2
+ import { T as Token } from './Token-0jpvUdPY.js';
3
+ import { D as DiagnosticPipeline } from './pipeline-CrsRPmsv.js';
4
+
5
+ /**
6
+ * Dual-keyed ParseletRegistry, accepts both string token types and
7
+ * integer token type IDs for fast dispatch in the Parser hot path.
8
+ *
9
+ * Providers call registerPrefix("NUMBER", ...) with string token types.
10
+ * Internally, we populate both string-keyed and integer-keyed maps so
11
+ * Parser.parseExpression() can use token.typeId (integer) for lookup
12
+ * while diagnostics and error messages use token.type (string).
13
+ *
14
+ * Performance: Integer Map.get() avoids string hashing, saving ~2-5ns
15
+ * per dispatch. With ~10-15 dispatches per expression, that's ~20-75ns.
16
+ */
17
+ declare class ParseletRegistry {
18
+ private prefixParselets;
19
+ private infixParselets;
20
+ private prefixById;
21
+ private infixById;
22
+ /**
23
+ * Register a prefix parselet for `tokenType`.
24
+ *
25
+ * If another parselet is already registered for this token type, it is
26
+ * silently overwritten by default (`Map.set()` semantics), the old
27
+ * parselet is simply unreachable from then on, with no error. This is
28
+ * a real footgun for third-party packages: two packages independently
29
+ * choosing the same custom token type will collide with zero signal
30
+ * about which one "won". Mirrors ResolverRegistry.register()'s and
31
+ * ExpressionEngine.registerPackage()'s existing "warn and replace"
32
+ * pattern for the same class of problem at the resolver-namespace and
33
+ * package-name levels.
34
+ *
35
+ * Note: this warns about registry-level collisions only. It does NOT
36
+ * detect the separate case where `tokenType` is one of PrecedenceParser's
37
+ * Tier-1 fast-path token types (NUMBER, STRING, IDENT, LPAREN, MINUS,
38
+ * PLUS, and the Tier-1 infix operators), those are deliberately kept
39
+ * registered here for introspection/diagnostics even though Tier-1
40
+ * always intercepts them before this registry is consulted (see
41
+ * PrecedenceParser.parsePrefix()'s docs), so warning there would
42
+ * misfire on that intentional, already-documented pattern.
43
+ */
44
+ registerPrefix(tokenType: string, parselet: PrefixParselet): void;
45
+ /** Register an infix parselet for `tokenType`. See {@link registerPrefix} for the collision-warning behavior this mirrors. */
46
+ registerInfix(tokenType: string, parselet: InfixParselet): void;
47
+ /** Iterate all registered prefix parselets for diagnostic display. */
48
+ getAllPrefix(): Array<{
49
+ tokenType: string;
50
+ bindingPower: number;
51
+ category?: string;
52
+ }>;
53
+ /** Iterate all registered infix parselets for diagnostic display. */
54
+ getAllInfix(): Array<{
55
+ tokenType: string;
56
+ leftBindingPower: number;
57
+ rightBindingPower: number;
58
+ category?: string;
59
+ }>;
60
+ /** Number of registered prefix parselets. */
61
+ get prefixCount(): number;
62
+ /** Number of registered infix parselets. */
63
+ get infixCount(): number;
64
+ /**
65
+ * Get prefix parselet by string token type OR integer typeId.
66
+ * Fast path for integer IDs (Parser hot path), fallback for strings
67
+ * (diagnostics, error messages, backwards compatibility).
68
+ */
69
+ getPrefix(tokenType: string | number): PrefixParselet | undefined;
70
+ /**
71
+ * Get infix parselet by string token type OR integer typeId.
72
+ * Fast path for integer IDs (Parser hot path), fallback for strings.
73
+ */
74
+ getInfix(tokenType: string | number): InfixParselet | undefined;
75
+ hasPrefix(tokenType: string): boolean;
76
+ hasInfix(tokenType: string): boolean;
77
+ clear(): void;
78
+ }
79
+ /**
80
+ * A process-wide parselet registry.
81
+ *
82
+ * @deprecated An engine builds its own registry and does not read this one, so
83
+ * registering here reaches nothing that evaluates. It survives for the
84
+ * deprecated {@link PackageRegistry} path only.
85
+ */
86
+ declare const sharedParseletRegistry: ParseletRegistry;
87
+
88
+ /**
89
+ * ── Hybrid Precedence Climbing Parser ─────────────────────────────────────────
90
+ *
91
+ * Two-tier dispatch strategy:
92
+ *
93
+ * Tier 1 (Fast Path): Inline switch on token.typeId for built-in operators.
94
+ * - Prefix: NUMBER, IDENT, LPAREN, MINUS, PLUS, STRING
95
+ * - Infix: checked via static BP_TABLE (Uint8Array indexed by typeId)
96
+ * - No Map.get(), no parselet function call overhead for ~95% of tokens
97
+ *
98
+ * Tier 2 (Plugin Path): ParseletRegistry fallback for custom/plugin tokens.
99
+ * - Prefix: Map.get(tokenTypeId) for PrefixParselet
100
+ * - Infix: Map.get(tokenTypeId) for InfixParselet
101
+ * - Full flexibility for custom syntax
102
+ *
103
+ * The parser emits directly to a BytecodeBuilder, no AST intermediate.
104
+ * Implements the same public API as the legacy Parser class so existing
105
+ * parselets continue to work without modification.
106
+ */
107
+ declare class PrecedenceParser {
108
+ private tokens;
109
+ private current;
110
+ private depth;
111
+ private maxDepth;
112
+ /** Cached registry reference, avoids property chain in hot loop */
113
+ private registry;
114
+ /** BytecodeBuilder, set before each parseExpression call */
115
+ private builder;
116
+ /** Diagnostic pipeline for parselet-matched events */
117
+ private diagnosticPipeline;
118
+ private currentExpression;
119
+ private localeCode;
120
+ /**
121
+ * Static binding power table, built once at module load, shared across all instances.
122
+ * Index = tokenTypeId, value = binding power (0 = not a built-in infix).
123
+ */
124
+ static readonly BP_TABLE: Uint8Array;
125
+ private static readonly NUMBER_ID;
126
+ private static readonly BIGINT_ID;
127
+ private static readonly STRING_ID;
128
+ private static readonly IDENT_ID;
129
+ private static readonly LPAREN_ID;
130
+ private static readonly RPAREN_ID;
131
+ private static readonly MINUS_ID;
132
+ private static readonly PLUS_ID;
133
+ private static readonly KEYWORD_ID;
134
+ private static readonly STAR_ID;
135
+ private static readonly SLASH_ID;
136
+ private static readonly MOD_ID;
137
+ private static readonly CARET_ID;
138
+ private static readonly LSHIFT_ID;
139
+ private static readonly RSHIFT_ID;
140
+ private static readonly BIT_AND_ID;
141
+ private static readonly BIT_OR_ID;
142
+ private static readonly BIT_XOR_ID;
143
+ private static readonly PERCENT_ID;
144
+ private static readonly OF_ID;
145
+ /**
146
+ * Inline opcode map for Tier 1 infix operators.
147
+ * tokenTypeId → OpCode. PERCENT and CARET are handled specially (not in this map).
148
+ */
149
+ private static readonly INFIX_OPCODE;
150
+ constructor(parseletRegistry: ParseletRegistry, maxDepth?: number, localeCode?: string);
151
+ /** Get locale code for NumberParselet to normalize separators */
152
+ getLocaleCode(): string;
153
+ /**
154
+ * Set the diagnostic pipeline for parselet matching events.
155
+ * Cleared after each non-cached parse to avoid holding refs.
156
+ */
157
+ setDiagnosticPipeline(pipeline: DiagnosticPipeline | undefined, expression: string): void;
158
+ /**
159
+ * Load tokens for parsing. Identical to Parser.load().
160
+ *
161
+ * @param hasParens - if false, skips the O(n) paren balance scan (~90% of expressions)
162
+ */
163
+ load(tokens: Token[], hasParens?: boolean): void;
164
+ /**
165
+ * Auto-balance unmatched parentheses.
166
+ */
167
+ private balanceParens;
168
+ /**
169
+ * Parse a full expression starting at the current token position.
170
+ *
171
+ * @param minBp - minimum binding power (precedence climbing threshold).
172
+ * For left-associative operators, the recursive call uses `bp + 1`.
173
+ * For right-associative operators (^), it uses `bp`.
174
+ * @param _builder - accepted for parselet API compatibility; always uses `this.builder`.
175
+ */
176
+ parseExpression(minBp?: number, _builder?: BytecodeBuilder): void;
177
+ /**
178
+ * Parse a prefix token. Built-in tokens (NUMBER, IDENT, LPAREN, etc.) are
179
+ * handled inline with zero registry lookup. Everything else falls through
180
+ * to the parselet registry (Tier 2).
181
+ */
182
+ private parsePrefix;
183
+ /**
184
+ * A single `^`-suffix shape: `matches` peeks ahead (consuming nothing) to
185
+ * check whether this shape starts at the current position; `emit` is only
186
+ * called immediately after `matches` returned true for that SAME position,
187
+ * and is responsible for consuming that shape's own trailing tokens and
188
+ * emitting its bytecode.
189
+ */
190
+ private static readonly CARET_SUFFIX_RULES;
191
+ /**
192
+ * After a `^` token (already consumed by the Tier-1 infix loop above),
193
+ * checks {@link CARET_SUFFIX_RULES} in order for a shape that means
194
+ * something other than ordinary exponentiation: `^T` (transpose) and `^-1`
195
+ * (matrix inverse, LITERALLY the integer exponent `-1`; `^-2`, `^-1.5`
196
+ * etc. still mean ordinary exponentiation). On a match, the rule's `emit`
197
+ * consumes that shape's own tokens and this returns `true`. On no match,
198
+ * consumes NOTHING, returning `false` so the caller falls through to
199
+ * ordinary `EXP` parsing.
200
+ *
201
+ * Every rule dispatches purely on SHAPE, never on operand type (unknowable
202
+ * at parse time): `inv()`'s own handler (`VMBuiltins.ts` index 65) returns
203
+ * `1/x` for a plain Number, byte-identical to what `Math.pow(x, -1)`
204
+ * already computed for `x^-1` before this feature existed, and a real
205
+ * matrix inverse for a Matrix, so `5^-1` still means exactly what it always
206
+ * has; only a Matrix operand actually inverts.
207
+ *
208
+ * Adding a future `^`-suffix shape is a new table entry here, not a new
209
+ * if-block. See the "Tier-1 shape-exception" pattern write-up above for
210
+ * why this table can't instead be a package-registered parselet.
211
+ */
212
+ private tryEmitMatrixCaretOp;
213
+ /**
214
+ * From an LPAREN token index, scan forward tracking paren depth and
215
+ * return the index of the matching RPAREN, or `null` if the parens never
216
+ * balance before the token stream ends. No emission, no position
217
+ * advance. Same class of technique as {@link balanceParens}'s own
218
+ * pre-scan, just exposed mid-parse instead of only at `load()` time.
219
+ * `openIdx` must point AT the LPAREN itself.
220
+ */
221
+ private findMatchingRParen;
222
+ /**
223
+ * Disambiguates a bare `IDENT` immediately followed by `(` between three
224
+ * things, using ONLY a bracket-depth scan (no backtracking, nothing is
225
+ * consumed until the shape is known):
226
+ * - `f(x) = expr`, a DEFINITION: the matching `)` is followed by `=`.
227
+ * - `f(5)`, a CALL to a (possibly not-yet-defined) function: anything
228
+ * else. This was never valid syntax before this feature (a bare
229
+ * `IDENT` immediately before `(` has no pre-existing "plain variable
230
+ * read + separately grouped `(...)`" meaning to preserve, confirmed
231
+ * via `BuiltinNormalizerRules.ts`'s `implicitMultiplyRule()`, which
232
+ * only inserts an implicit `*` for `NUMBER/RPAREN` immediately before
233
+ * `IDENT/LPAREN`, never for a bare `IDENT` immediately before
234
+ * `LPAREN`). Always commits to a CALL; an unregistered name produces a
235
+ * clear `UNDEFINED_FUNCTION` error at VM-execution time, the same
236
+ * forward-reference philosophy `LOAD_VAR`/`UNDEFINED_VARIABLE` already
237
+ * uses, rather than any parse-time registry lookup.
238
+ * If the parens never balance, this falls through to the ordinary
239
+ * `LOAD_VAR` path (a malformed expression surfaces its own parse error
240
+ * shortly after, from the normal expression grammar).
241
+ */
242
+ private parseUserFunctionDefOrCall;
243
+ /**
244
+ * A parameter name, accepted as either `IDENT` or `UNIT`, matches this
245
+ * codebase's established `:name = value` variable-name policy
246
+ * (`VariableParselet.ts` explicitly accepts `UNIT`-typed tokens too, e.g.
247
+ * `:b = 5` for the "b" bits unit) since common short parameter names
248
+ * like `h`/`l`/`b`/`t`/`s`/`m` collide with real unit abbreviations
249
+ * (hour, liter, bits, ton, second, meter, ...) and lex as `UNIT`, not
250
+ * `IDENT`.
251
+ */
252
+ private consumeParamName;
253
+ private parseUserFunctionDefinition;
254
+ private parseUserFunctionCall;
255
+ consume(expectedType?: string): Token;
256
+ match(expectedType: string): boolean;
257
+ peek(): Token | undefined;
258
+ /**
259
+ * Read-only lookahead `offset` tokens past the current position, without
260
+ * consuming anything, `peekAt(0)` is equivalent to {@link peek}.
261
+ * `this.tokens` is a plain in-memory array (not a stream), so this is a
262
+ * simple, safe index read; no rewind/checkpoint mechanism is needed since
263
+ * nothing is consumed.
264
+ */
265
+ peekAt(offset: number): Token | undefined;
266
+ previous(): Token | undefined;
267
+ private advance;
268
+ /** Set the builder to use for the current parse. Called by ExpressionEngine. */
269
+ setBuilder(builder: BytecodeBuilder): void;
270
+ private fireParseletMatched;
271
+ }
272
+
273
+ /**
274
+ * Handles a token appearing where a value is expected.
275
+ *
276
+ * A literal, a prefix operator such as unary minus, or a function name. The
277
+ * parser calls this when the token opens an expression rather than continuing
278
+ * one.
279
+ */
280
+ interface PrefixParselet {
281
+ /** Diagnostic category for this parselet (e.g. "Arithmetic", "Function", "Variable") */
282
+ readonly category: string;
283
+ parse(parser: PrecedenceParser, token: Token, builder: BytecodeBuilder): void;
284
+ }
285
+ /**
286
+ * Handles a token appearing after a value.
287
+ *
288
+ * A binary operator, or anything else that continues an expression already in
289
+ * progress. Its binding power decides how tightly it binds against neighbours.
290
+ */
291
+ interface InfixParselet {
292
+ /** Diagnostic category for this parselet (e.g. "Arithmetic", "UoM") */
293
+ readonly category: string;
294
+ /** Binding power (precedence), property access avoids vtable dispatch in hot loop */
295
+ readonly bindingPower: number;
296
+ parse(parser: PrecedenceParser, left: Token, token: Token, builder: BytecodeBuilder): void;
297
+ }
298
+
299
+ export { type InfixParselet as I, ParseletRegistry as P, PrecedenceParser as a, type PrefixParselet as b, sharedParseletRegistry as s };