solve-engine 1.0.0-beta.2 → 1.0.0-beta.4

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 (171) hide show
  1. package/dist/{Lexer-D9l4Yrk2.d.ts → Lexer-Cfm79Dak.d.cts} +18 -2
  2. package/dist/{Lexer-BJdhlnej.d.cts → Lexer-W9MBOP0V.d.ts} +18 -2
  3. package/dist/{PackageRegistry-DaH4aIKP.d.ts → PackageRegistry-CjDt-Jy_.d.ts} +60 -4
  4. package/dist/{PackageRegistry-D-Tv_7ca.d.cts → PackageRegistry-pHtKythi.d.cts} +60 -4
  5. package/dist/{Parselet-CK8cQu2a.d.cts → Parselet-Cu0bLSis.d.cts} +1 -1
  6. package/dist/{Parselet-ConOIdRO.d.ts → Parselet-DEdF9I7n.d.ts} +1 -1
  7. package/dist/{Token-0jpvUdPY.d.cts → Token-BzG5G4ja.d.cts} +44 -0
  8. package/dist/{Token-0jpvUdPY.d.ts → Token-BzG5G4ja.d.ts} +44 -0
  9. package/dist/TokenNormalizer-DGVa24Q-.d.cts +377 -0
  10. package/dist/TokenNormalizer-t_GotBxr.d.ts +377 -0
  11. package/dist/{chunk-HWSZJQCI.js → chunk-2CS6OMZK.js} +37 -15
  12. package/dist/chunk-2CS6OMZK.js.map +1 -0
  13. package/dist/{chunk-XJCMXB2E.cjs → chunk-2MV4HBKC.cjs} +187 -61
  14. package/dist/chunk-2MV4HBKC.cjs.map +1 -0
  15. package/dist/{chunk-J73SJHR3.cjs → chunk-2NPS5DQ3.cjs} +558 -341
  16. package/dist/chunk-2NPS5DQ3.cjs.map +1 -0
  17. package/dist/{chunk-QWY3VEZN.js → chunk-3MNKQZ77.js} +65 -12
  18. package/dist/chunk-3MNKQZ77.js.map +1 -0
  19. package/dist/{chunk-RZCWSXTA.cjs → chunk-43G3JT2E.cjs} +1491 -218
  20. package/dist/chunk-43G3JT2E.cjs.map +1 -0
  21. package/dist/{chunk-WQTTOGXC.cjs → chunk-4AVD7NZW.cjs} +4 -4
  22. package/dist/{chunk-WQTTOGXC.cjs.map → chunk-4AVD7NZW.cjs.map} +1 -1
  23. package/dist/{chunk-HTXVVJRA.cjs → chunk-4CVLFLOB.cjs} +112 -2
  24. package/dist/chunk-4CVLFLOB.cjs.map +1 -0
  25. package/dist/{chunk-5YEMOYSE.js → chunk-6GCKCWLB.js} +9 -3
  26. package/dist/chunk-6GCKCWLB.js.map +1 -0
  27. package/dist/{chunk-EBSPLUW4.cjs → chunk-AHCLWAM5.cjs} +28 -10
  28. package/dist/chunk-AHCLWAM5.cjs.map +1 -0
  29. package/dist/{chunk-KVILKGMS.js → chunk-AJA6LUI7.js} +36 -2
  30. package/dist/chunk-AJA6LUI7.js.map +1 -0
  31. package/dist/{chunk-EHAHVROS.cjs → chunk-CLVQBF5C.cjs} +5 -5
  32. package/dist/{chunk-EHAHVROS.cjs.map → chunk-CLVQBF5C.cjs.map} +1 -1
  33. package/dist/{chunk-34RRD7PC.js → chunk-CYFK5SY2.js} +111 -3
  34. package/dist/chunk-CYFK5SY2.js.map +1 -0
  35. package/dist/{chunk-M5LX5AOO.js → chunk-DDQVZGLO.js} +25 -12
  36. package/dist/chunk-DDQVZGLO.js.map +1 -0
  37. package/dist/{chunk-LR7YASZF.cjs → chunk-EAAHVJ4P.cjs} +3 -3
  38. package/dist/chunk-EAAHVJ4P.cjs.map +1 -0
  39. package/dist/{chunk-3PPFLFH4.js → chunk-FIFQPKBA.js} +1417 -144
  40. package/dist/chunk-FIFQPKBA.js.map +1 -0
  41. package/dist/{chunk-RIN643A3.js → chunk-GPPLSM2Z.js} +61 -2
  42. package/dist/chunk-GPPLSM2Z.js.map +1 -0
  43. package/dist/{chunk-4QADQTWS.js → chunk-KHJUSHFU.js} +231 -14
  44. package/dist/chunk-KHJUSHFU.js.map +1 -0
  45. package/dist/{chunk-NMD5VRN4.cjs → chunk-M4F66R4O.cjs} +74 -73
  46. package/dist/chunk-M4F66R4O.cjs.map +1 -0
  47. package/dist/{chunk-6NTVRDQV.cjs → chunk-M4Q47KHF.cjs} +126 -73
  48. package/dist/chunk-M4Q47KHF.cjs.map +1 -0
  49. package/dist/{chunk-LIPPNDBE.js → chunk-M5E34VG5.js} +3 -3
  50. package/dist/{chunk-LIPPNDBE.js.map → chunk-M5E34VG5.js.map} +1 -1
  51. package/dist/{chunk-53B6KDDJ.cjs → chunk-MBNQVDVC.cjs} +38 -3
  52. package/dist/{chunk-53B6KDDJ.cjs.map → chunk-MBNQVDVC.cjs.map} +1 -1
  53. package/dist/{chunk-SDGRK7EP.js → chunk-MEOHSQEH.js} +6 -5
  54. package/dist/chunk-MEOHSQEH.js.map +1 -0
  55. package/dist/{chunk-NMCRQP3Z.cjs → chunk-MTX2KVU7.cjs} +71 -70
  56. package/dist/chunk-MTX2KVU7.cjs.map +1 -0
  57. package/dist/{chunk-QNJ4ACRT.cjs → chunk-NJTXJ5AG.cjs} +37 -15
  58. package/dist/chunk-NJTXJ5AG.cjs.map +1 -0
  59. package/dist/{chunk-C4XZV6E7.cjs → chunk-NZFKROS7.cjs} +26 -20
  60. package/dist/chunk-NZFKROS7.cjs.map +1 -0
  61. package/dist/{chunk-64W6GLLZ.js → chunk-OFXOTECC.js} +24 -6
  62. package/dist/chunk-OFXOTECC.js.map +1 -0
  63. package/dist/{chunk-3YNVWKR2.cjs → chunk-OUHT5Z36.cjs} +28 -5
  64. package/dist/chunk-OUHT5Z36.cjs.map +1 -0
  65. package/dist/{chunk-EIGTWK5N.js → chunk-OWRHDUJD.js} +3 -3
  66. package/dist/chunk-OWRHDUJD.js.map +1 -0
  67. package/dist/{chunk-4MG4XKO2.js → chunk-PCPX42KL.js} +38 -3
  68. package/dist/{chunk-4MG4XKO2.js.map → chunk-PCPX42KL.js.map} +1 -1
  69. package/dist/{chunk-GW32KPCU.cjs → chunk-R24DI24X.cjs} +61 -2
  70. package/dist/chunk-R24DI24X.cjs.map +1 -0
  71. package/dist/{chunk-XVWCOTR6.js → chunk-SFQWJMKT.js} +7 -6
  72. package/dist/chunk-SFQWJMKT.js.map +1 -0
  73. package/dist/{chunk-6BKTCEUP.cjs → chunk-TY3TLZAW.cjs} +36 -2
  74. package/dist/chunk-TY3TLZAW.cjs.map +1 -0
  75. package/dist/{chunk-DM3LMRBC.js → chunk-UKPGSAZW.js} +187 -61
  76. package/dist/chunk-UKPGSAZW.js.map +1 -0
  77. package/dist/{chunk-NH2O2AUR.js → chunk-WC5FFSHB.js} +26 -4
  78. package/dist/chunk-WC5FFSHB.js.map +1 -0
  79. package/dist/{chunk-CLL7RUQV.cjs → chunk-WWOLFXTX.cjs} +40 -18
  80. package/dist/chunk-WWOLFXTX.cjs.map +1 -0
  81. package/dist/{chunk-JBSYC7BB.cjs → chunk-X2BWAZAU.cjs} +66 -53
  82. package/dist/chunk-X2BWAZAU.cjs.map +1 -0
  83. package/dist/{chunk-GOLDJNMZ.js → chunk-XBTEO4OB.js} +28 -5
  84. package/dist/chunk-XBTEO4OB.js.map +1 -0
  85. package/dist/{chunk-EPOXXJBK.js → chunk-YU2CUNFO.js} +3 -3
  86. package/dist/{chunk-EPOXXJBK.js.map → chunk-YU2CUNFO.js.map} +1 -1
  87. package/dist/constants.cjs +4 -4
  88. package/dist/constants.js +1 -1
  89. package/dist/engine.cjs +29 -30
  90. package/dist/engine.d.cts +6 -6
  91. package/dist/engine.d.ts +6 -6
  92. package/dist/engine.js +19 -20
  93. package/dist/format.cjs +7 -8
  94. package/dist/format.cjs.map +1 -1
  95. package/dist/format.js +2 -3
  96. package/dist/format.js.map +1 -1
  97. package/dist/index.cjs +28 -29
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.d.cts +6 -6
  100. package/dist/index.d.ts +6 -6
  101. package/dist/index.js +20 -21
  102. package/dist/index.js.map +1 -1
  103. package/dist/language.cjs +74 -12
  104. package/dist/language.cjs.map +1 -1
  105. package/dist/language.d.cts +6 -6
  106. package/dist/language.d.ts +6 -6
  107. package/dist/language.js +68 -6
  108. package/dist/language.js.map +1 -1
  109. package/dist/lexer.cjs +19 -19
  110. package/dist/lexer.d.cts +3 -3
  111. package/dist/lexer.d.ts +3 -3
  112. package/dist/lexer.js +7 -7
  113. package/dist/normalizer.cjs +12 -12
  114. package/dist/normalizer.d.cts +4 -210
  115. package/dist/normalizer.d.ts +4 -210
  116. package/dist/normalizer.js +6 -6
  117. package/dist/packages.cjs +36 -37
  118. package/dist/packages.d.cts +5 -47
  119. package/dist/packages.d.ts +5 -47
  120. package/dist/packages.js +13 -14
  121. package/dist/parser.cjs +12 -12
  122. package/dist/parser.d.cts +3 -2
  123. package/dist/parser.d.ts +3 -2
  124. package/dist/parser.js +4 -4
  125. package/dist/resolvers.d.cts +1 -1
  126. package/dist/resolvers.d.ts +1 -1
  127. package/dist/uom.cjs +14 -14
  128. package/dist/uom.d.cts +12 -4
  129. package/dist/uom.d.ts +12 -4
  130. package/dist/uom.js +4 -4
  131. package/dist/vm.cjs +17 -17
  132. package/dist/vm.js +6 -6
  133. package/package.json +2 -1
  134. package/dist/NormalizerRule-BrVoVjmP.d.cts +0 -163
  135. package/dist/NormalizerRule-CEjf1FyD.d.ts +0 -163
  136. package/dist/chunk-34RRD7PC.js.map +0 -1
  137. package/dist/chunk-3PPFLFH4.js.map +0 -1
  138. package/dist/chunk-3YNVWKR2.cjs.map +0 -1
  139. package/dist/chunk-4QADQTWS.js.map +0 -1
  140. package/dist/chunk-5YEMOYSE.js.map +0 -1
  141. package/dist/chunk-64W6GLLZ.js.map +0 -1
  142. package/dist/chunk-6BKTCEUP.cjs.map +0 -1
  143. package/dist/chunk-6NTVRDQV.cjs.map +0 -1
  144. package/dist/chunk-C4XZV6E7.cjs.map +0 -1
  145. package/dist/chunk-CLL7RUQV.cjs.map +0 -1
  146. package/dist/chunk-DM3LMRBC.js.map +0 -1
  147. package/dist/chunk-EBSPLUW4.cjs.map +0 -1
  148. package/dist/chunk-EIGTWK5N.js.map +0 -1
  149. package/dist/chunk-GOLDJNMZ.js.map +0 -1
  150. package/dist/chunk-GW32KPCU.cjs.map +0 -1
  151. package/dist/chunk-HTXVVJRA.cjs.map +0 -1
  152. package/dist/chunk-HWSZJQCI.js.map +0 -1
  153. package/dist/chunk-J73SJHR3.cjs.map +0 -1
  154. package/dist/chunk-JBSYC7BB.cjs.map +0 -1
  155. package/dist/chunk-KVILKGMS.js.map +0 -1
  156. package/dist/chunk-LR7YASZF.cjs.map +0 -1
  157. package/dist/chunk-M5LX5AOO.js.map +0 -1
  158. package/dist/chunk-NH2O2AUR.js.map +0 -1
  159. package/dist/chunk-NMCRQP3Z.cjs.map +0 -1
  160. package/dist/chunk-NMD5VRN4.cjs.map +0 -1
  161. package/dist/chunk-OT6OJY7C.cjs +0 -114
  162. package/dist/chunk-OT6OJY7C.cjs.map +0 -1
  163. package/dist/chunk-QNJ4ACRT.cjs.map +0 -1
  164. package/dist/chunk-QWY3VEZN.js.map +0 -1
  165. package/dist/chunk-RFYD5TJE.js +0 -111
  166. package/dist/chunk-RFYD5TJE.js.map +0 -1
  167. package/dist/chunk-RIN643A3.js.map +0 -1
  168. package/dist/chunk-RZCWSXTA.cjs.map +0 -1
  169. package/dist/chunk-SDGRK7EP.js.map +0 -1
  170. package/dist/chunk-XJCMXB2E.cjs.map +0 -1
  171. package/dist/chunk-XVWCOTR6.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { T as Token } from './Token-0jpvUdPY.js';
1
+ import { T as Token } from './Token-BzG5G4ja.cjs';
2
2
 
3
3
  /** Trie node for multi-word phrase matching. */
4
4
  interface PhraseNode {
@@ -102,7 +102,11 @@ declare class LexerToken implements Token {
102
102
  lineBreaks: number;
103
103
  line: number;
104
104
  col: number;
105
- constructor(type: string, typeId: number, value: string, text: string, offset: number, lineBreaks: number, line: number, col: number);
105
+ /** See {@link Token.sourceEnd}. Set only on normalizer-fused tokens. */
106
+ sourceEnd?: number | undefined;
107
+ constructor(type: string, typeId: number, value: string, text: string, offset: number, lineBreaks: number, line: number, col: number,
108
+ /** See {@link Token.sourceEnd}. Set only on normalizer-fused tokens. */
109
+ sourceEnd?: number | undefined);
106
110
  }
107
111
  /**
108
112
  * Plugin interface for extending the ExpressionLexer with custom tokens.
@@ -512,6 +516,18 @@ declare class Lexer {
512
516
  length: number;
513
517
  category: TokenCategory | undefined;
514
518
  }[];
519
+ /**
520
+ * The same tokens {@link getHighlightTokens} reduces, before reduction.
521
+ *
522
+ * Exists because normalization operates on tokens, not on the flattened
523
+ * shape, and a consumer that wants phrase-fused highlighting has to run the
524
+ * normalizer between the two. See `LanguageService.getSemanticTokens`.
525
+ *
526
+ * @param lineText - One line of source.
527
+ * @returns Every token on the line that is worth painting, unreduced.
528
+ */
529
+ getHighlightTokenObjects(lineText: string): Token[];
530
+ private collectTokenObjects;
515
531
  private collectHighlightTokens;
516
532
  }
517
533
  /**
@@ -1,4 +1,4 @@
1
- import { T as Token } from './Token-0jpvUdPY.cjs';
1
+ import { T as Token } from './Token-BzG5G4ja.js';
2
2
 
3
3
  /** Trie node for multi-word phrase matching. */
4
4
  interface PhraseNode {
@@ -102,7 +102,11 @@ declare class LexerToken implements Token {
102
102
  lineBreaks: number;
103
103
  line: number;
104
104
  col: number;
105
- constructor(type: string, typeId: number, value: string, text: string, offset: number, lineBreaks: number, line: number, col: number);
105
+ /** See {@link Token.sourceEnd}. Set only on normalizer-fused tokens. */
106
+ sourceEnd?: number | undefined;
107
+ constructor(type: string, typeId: number, value: string, text: string, offset: number, lineBreaks: number, line: number, col: number,
108
+ /** See {@link Token.sourceEnd}. Set only on normalizer-fused tokens. */
109
+ sourceEnd?: number | undefined);
106
110
  }
107
111
  /**
108
112
  * Plugin interface for extending the ExpressionLexer with custom tokens.
@@ -512,6 +516,18 @@ declare class Lexer {
512
516
  length: number;
513
517
  category: TokenCategory | undefined;
514
518
  }[];
519
+ /**
520
+ * The same tokens {@link getHighlightTokens} reduces, before reduction.
521
+ *
522
+ * Exists because normalization operates on tokens, not on the flattened
523
+ * shape, and a consumer that wants phrase-fused highlighting has to run the
524
+ * normalizer between the two. See `LanguageService.getSemanticTokens`.
525
+ *
526
+ * @param lineText - One line of source.
527
+ * @returns Every token on the line that is worth painting, unreduced.
528
+ */
529
+ getHighlightTokenObjects(lineText: string): Token[];
530
+ private collectTokenObjects;
515
531
  private collectHighlightTokens;
516
532
  }
517
533
  /**
@@ -1,15 +1,15 @@
1
- import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-ConOIdRO.js';
1
+ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-DEdF9I7n.js';
2
2
  import { V as Value } from './Value-KtAQNHur.js';
3
3
  import { IVariableSource } from './variables.js';
4
- import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-D9l4Yrk2.js';
4
+ import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-W9MBOP0V.js';
5
5
  import { IAsyncResolver } from './resolvers.js';
6
- import { T as TokenFusion, N as NormalizerRule } from './NormalizerRule-CEjf1FyD.js';
6
+ import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-t_GotBxr.js';
7
7
  import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, L as LineExecutionContext } from './ScopeManager-B5k0nk05.js';
8
8
  import { a as BytecodeProgram } from './BytecodeBuilder-3egMyuDi.js';
9
9
  import { QueryClient } from '@tanstack/query-core';
10
10
  import { E as EngineError } from './EngineError-DvPSnhZi.js';
11
11
  import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-CrsRPmsv.js';
12
- import { T as Token } from './Token-0jpvUdPY.js';
12
+ import { T as Token } from './Token-BzG5G4ja.js';
13
13
  import { D as DEFAULT_CONFIG, E as EngineConfig } from './Configuration-fVpd-HC-.js';
14
14
 
15
15
  /**
@@ -1958,6 +1958,17 @@ declare class ExpressionEngine {
1958
1958
  getContext(): EngineContext;
1959
1959
  getScopeManager(): ScopeManager;
1960
1960
  getLexer(): Lexer;
1961
+ /**
1962
+ * This engine's token normalizer, with every phrase and rule its packages
1963
+ * registered.
1964
+ *
1965
+ * Exposed for the highlighting path. `LanguageService` classifies tokens
1966
+ * after the lexer but can only see phrase-fused types (a date literal, a
1967
+ * timecode, a package's own fused token) by running the same normalizer
1968
+ * evaluation runs. Sharing this instance rather than building a second one
1969
+ * is what keeps the two from disagreeing.
1970
+ */
1971
+ getNormalizer(): TokenNormalizer;
1961
1972
  /**
1962
1973
  * Every completion candidate contributed by currently-registered
1963
1974
  * packages (`IEnginePackage.completionItems`), flattened across all of
@@ -2174,6 +2185,20 @@ interface LanguageServiceOptions {
2174
2185
  * evaluation engine's DAG snapshot instead.
2175
2186
  */
2176
2187
  variableNameSource?: () => Iterable<string>;
2188
+ /**
2189
+ * Run the normalizer on the highlighting path, so phrase-fused tokens are
2190
+ * classified as the thing the parser will actually see.
2191
+ *
2192
+ * Off by default, and the default is a judgement rather than an oversight.
2193
+ * Highlighting runs per keystroke, normalization is real work, and a host
2194
+ * that is happy with lexer-level categories should not start paying for
2195
+ * fusion because a new version shipped. Turn it on and `12/09/2026` is one
2196
+ * `datetime` span instead of five number and operator spans; leave it off
2197
+ * and nothing about this class changes.
2198
+ *
2199
+ * See `benchmarks/languageServiceBenchmarks.spec.ts` for what it costs.
2200
+ */
2201
+ normalizeForHighlighting?: boolean;
2177
2202
  }
2178
2203
  /**
2179
2204
  * Editor-agnostic "language server" for solve expressions: turns a line of
@@ -2229,11 +2254,42 @@ interface LanguageServiceOptions {
2229
2254
  declare class LanguageService {
2230
2255
  private engine;
2231
2256
  private variableNameSource;
2257
+ private readonly normalizeForHighlighting;
2232
2258
  private cache;
2233
2259
  private staticCompletionCandidates;
2234
2260
  private staticCompletionIndex;
2235
2261
  constructor(engine?: ExpressionEngine | null, options?: LanguageServiceOptions);
2236
2262
  private defaultVariableNames;
2263
+ /**
2264
+ * Lex a line, normalize it, and place every resulting token back in the
2265
+ * source text.
2266
+ *
2267
+ * ## Why placing them back is the hard part
2268
+ *
2269
+ * Normalization produces three kinds of token and only one of them can be
2270
+ * highlighted the obvious way.
2271
+ *
2272
+ * A token the normalizer left alone still describes its own text, so its
2273
+ * span is `offset` to `offset + value.length`, exactly as before.
2274
+ *
2275
+ * A FUSED token does not. `10 frames` becomes a FRAME_COUNT whose value is
2276
+ * `10`, and a timecode becomes a token whose value is a comma-separated
2277
+ * tuple appearing nowhere on the line. Those carry `sourceEnd`, set by
2278
+ * `createFusedToken`, which is the only place that knows where the fusion
2279
+ * ended.
2280
+ *
2281
+ * An INSERTED token has no text at all. Implicit multiplication puts a STAR
2282
+ * at the following token's offset, so `5(3)` gains a `*` sitting exactly
2283
+ * where the `(` is. Painting it would colour a character the reader never
2284
+ * typed as an operator, and would overlap the token that really is there.
2285
+ * These are dropped, detected by the one test that needs no cooperation
2286
+ * from any rule: a token with no recorded fusion span must match the text
2287
+ * at its own offset, and an inserted one does not.
2288
+ *
2289
+ * @param lineText - The raw line.
2290
+ * @returns Spans in the same shape `Lexer.getHighlightTokens` returns.
2291
+ */
2292
+ private classifyNormalized;
2237
2293
  /**
2238
2294
  * Classify every recognized token on one line.
2239
2295
  *
@@ -1,15 +1,15 @@
1
- import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-CK8cQu2a.cjs';
1
+ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-Cu0bLSis.cjs';
2
2
  import { V as Value } from './Value-KtAQNHur.cjs';
3
3
  import { IVariableSource } from './variables.cjs';
4
- import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-BJdhlnej.cjs';
4
+ import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-Cfm79Dak.cjs';
5
5
  import { IAsyncResolver } from './resolvers.cjs';
6
- import { T as TokenFusion, N as NormalizerRule } from './NormalizerRule-BrVoVjmP.cjs';
6
+ import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-DGVa24Q-.cjs';
7
7
  import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, L as LineExecutionContext } from './ScopeManager-CKfQtl7l.cjs';
8
8
  import { a as BytecodeProgram } from './BytecodeBuilder-3egMyuDi.cjs';
9
9
  import { QueryClient } from '@tanstack/query-core';
10
10
  import { E as EngineError } from './EngineError-DvPSnhZi.cjs';
11
11
  import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-DPUQt7yL.cjs';
12
- import { T as Token } from './Token-0jpvUdPY.cjs';
12
+ import { T as Token } from './Token-BzG5G4ja.cjs';
13
13
  import { D as DEFAULT_CONFIG, E as EngineConfig } from './Configuration-fVpd-HC-.cjs';
14
14
 
15
15
  /**
@@ -1958,6 +1958,17 @@ declare class ExpressionEngine {
1958
1958
  getContext(): EngineContext;
1959
1959
  getScopeManager(): ScopeManager;
1960
1960
  getLexer(): Lexer;
1961
+ /**
1962
+ * This engine's token normalizer, with every phrase and rule its packages
1963
+ * registered.
1964
+ *
1965
+ * Exposed for the highlighting path. `LanguageService` classifies tokens
1966
+ * after the lexer but can only see phrase-fused types (a date literal, a
1967
+ * timecode, a package's own fused token) by running the same normalizer
1968
+ * evaluation runs. Sharing this instance rather than building a second one
1969
+ * is what keeps the two from disagreeing.
1970
+ */
1971
+ getNormalizer(): TokenNormalizer;
1961
1972
  /**
1962
1973
  * Every completion candidate contributed by currently-registered
1963
1974
  * packages (`IEnginePackage.completionItems`), flattened across all of
@@ -2174,6 +2185,20 @@ interface LanguageServiceOptions {
2174
2185
  * evaluation engine's DAG snapshot instead.
2175
2186
  */
2176
2187
  variableNameSource?: () => Iterable<string>;
2188
+ /**
2189
+ * Run the normalizer on the highlighting path, so phrase-fused tokens are
2190
+ * classified as the thing the parser will actually see.
2191
+ *
2192
+ * Off by default, and the default is a judgement rather than an oversight.
2193
+ * Highlighting runs per keystroke, normalization is real work, and a host
2194
+ * that is happy with lexer-level categories should not start paying for
2195
+ * fusion because a new version shipped. Turn it on and `12/09/2026` is one
2196
+ * `datetime` span instead of five number and operator spans; leave it off
2197
+ * and nothing about this class changes.
2198
+ *
2199
+ * See `benchmarks/languageServiceBenchmarks.spec.ts` for what it costs.
2200
+ */
2201
+ normalizeForHighlighting?: boolean;
2177
2202
  }
2178
2203
  /**
2179
2204
  * Editor-agnostic "language server" for solve expressions: turns a line of
@@ -2229,11 +2254,42 @@ interface LanguageServiceOptions {
2229
2254
  declare class LanguageService {
2230
2255
  private engine;
2231
2256
  private variableNameSource;
2257
+ private readonly normalizeForHighlighting;
2232
2258
  private cache;
2233
2259
  private staticCompletionCandidates;
2234
2260
  private staticCompletionIndex;
2235
2261
  constructor(engine?: ExpressionEngine | null, options?: LanguageServiceOptions);
2236
2262
  private defaultVariableNames;
2263
+ /**
2264
+ * Lex a line, normalize it, and place every resulting token back in the
2265
+ * source text.
2266
+ *
2267
+ * ## Why placing them back is the hard part
2268
+ *
2269
+ * Normalization produces three kinds of token and only one of them can be
2270
+ * highlighted the obvious way.
2271
+ *
2272
+ * A token the normalizer left alone still describes its own text, so its
2273
+ * span is `offset` to `offset + value.length`, exactly as before.
2274
+ *
2275
+ * A FUSED token does not. `10 frames` becomes a FRAME_COUNT whose value is
2276
+ * `10`, and a timecode becomes a token whose value is a comma-separated
2277
+ * tuple appearing nowhere on the line. Those carry `sourceEnd`, set by
2278
+ * `createFusedToken`, which is the only place that knows where the fusion
2279
+ * ended.
2280
+ *
2281
+ * An INSERTED token has no text at all. Implicit multiplication puts a STAR
2282
+ * at the following token's offset, so `5(3)` gains a `*` sitting exactly
2283
+ * where the `(` is. Painting it would colour a character the reader never
2284
+ * typed as an operator, and would overlap the token that really is there.
2285
+ * These are dropped, detected by the one test that needs no cooperation
2286
+ * from any rule: a token with no recorded fusion span must match the text
2287
+ * at its own offset, and an inserted one does not.
2288
+ *
2289
+ * @param lineText - The raw line.
2290
+ * @returns Spans in the same shape `Lexer.getHighlightTokens` returns.
2291
+ */
2292
+ private classifyNormalized;
2237
2293
  /**
2238
2294
  * Classify every recognized token on one line.
2239
2295
  *
@@ -1,5 +1,5 @@
1
1
  import { B as BytecodeBuilder } from './BytecodeBuilder-3egMyuDi.cjs';
2
- import { T as Token } from './Token-0jpvUdPY.cjs';
2
+ import { T as Token } from './Token-BzG5G4ja.cjs';
3
3
  import { D as DiagnosticPipeline } from './pipeline-DPUQt7yL.cjs';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { B as BytecodeBuilder } from './BytecodeBuilder-3egMyuDi.js';
2
- import { T as Token } from './Token-0jpvUdPY.js';
2
+ import { T as Token } from './Token-BzG5G4ja.js';
3
3
  import { D as DiagnosticPipeline } from './pipeline-CrsRPmsv.js';
4
4
 
5
5
  /**
@@ -19,6 +19,20 @@ interface Token {
19
19
  lineBreaks: number;
20
20
  line: number;
21
21
  col: number;
22
+ /**
23
+ * End offset of the source text this token was fused from, exclusive.
24
+ *
25
+ * Only set on tokens the normalizer built by fusing several lexer tokens
26
+ * into one, and only there because those are the tokens whose `value` no
27
+ * longer describes the text they came from. `10 frames` fuses into a
28
+ * FRAME_COUNT whose value is `10`, so `offset + value.length` would cover
29
+ * two characters of a nine-character phrase.
30
+ *
31
+ * Evaluation never reads this. It exists so that a consumer painting a line
32
+ * (see `LanguageService.getSemanticTokens`) can highlight a fused phrase as
33
+ * the one thing the parser will see rather than as its parts.
34
+ */
35
+ sourceEnd?: number;
22
36
  }
23
37
  /**
24
38
  * Canonical token type name constants.
@@ -73,6 +87,7 @@ declare const TokenTypes: {
73
87
  readonly PI: "PI";
74
88
  readonly E: "E";
75
89
  readonly MOD: "MOD";
90
+ readonly AND_CONJ: "AND_CONJ";
76
91
  readonly OF: "OF";
77
92
  readonly INCREASE_BY: "INCREASE_BY";
78
93
  readonly DECREASE_BY: "DECREASE_BY";
@@ -124,6 +139,9 @@ declare const TokenTypes: {
124
139
  readonly ELSE: "ELSE";
125
140
  readonly AS: "AS";
126
141
  readonly CONVERTER_NAME: "CONVERTER_NAME";
142
+ readonly ROUNDED: "ROUNDED";
143
+ readonly TO_NEAREST: "TO_NEAREST";
144
+ readonly DECIMAL_PLACES: "DECIMAL_PLACES";
127
145
  readonly AVERAGE_OF: "AVERAGE_OF";
128
146
  readonly MEDIAN_OF: "MEDIAN_OF";
129
147
  readonly TOTAL_OF: "TOTAL_OF";
@@ -132,8 +150,22 @@ declare const TokenTypes: {
132
150
  readonly SMALLER_OF: "SMALLER_OF";
133
151
  readonly HALF_OF: "HALF_OF";
134
152
  readonly MIDPOINT_BETWEEN: "MIDPOINT_BETWEEN";
153
+ readonly REMAINDER_OF: "REMAINDER_OF";
154
+ readonly NTH_ROOT: "NTH_ROOT";
155
+ readonly LOG_PHRASE: "LOG_PHRASE";
156
+ readonly LOG_BASE: "LOG_BASE";
157
+ readonly GCD_OF: "GCD_OF";
158
+ readonly LCM_OF: "LCM_OF";
159
+ readonly SQUARE_ROOT_OF: "SQUARE_ROOT_OF";
160
+ readonly CUBE_ROOT_OF: "CUBE_ROOT_OF";
135
161
  readonly RANDOM_NUMBER: "RANDOM_NUMBER";
136
162
  readonly IS_TO: "IS_TO";
163
+ readonly IS: "IS";
164
+ readonly PCT_ON: "PCT_ON";
165
+ readonly PCT_OFF: "PCT_OFF";
166
+ readonly OF_WHAT: "OF_WHAT";
167
+ readonly OFF_WHAT: "OFF_WHAT";
168
+ readonly ON_WHAT: "ON_WHAT";
137
169
  readonly CLAMP: "CLAMP";
138
170
  readonly MAP: "MAP";
139
171
  readonly REDUCE: "REDUCE";
@@ -167,6 +199,13 @@ declare const TokenTypes: {
167
199
  readonly GLOBAL: "GLOBAL";
168
200
  readonly OVER: "OVER";
169
201
  readonly RATE_AT: "RATE_AT";
202
+ readonly AFTER: "AFTER";
203
+ readonly PRESENT_VALUE_OF: "PRESENT_VALUE_OF";
204
+ readonly ANNUAL_RETURN_ON: "ANNUAL_RETURN_ON";
205
+ readonly FOR_DURATION: "FOR_DURATION";
206
+ readonly COMPOUNDING: "COMPOUNDING";
207
+ readonly INVESTED: "INVESTED";
208
+ readonly RETURNED: "RETURNED";
170
209
  readonly COMPOUND_INTEREST_ON: "COMPOUND_INTEREST_ON";
171
210
  readonly INTEREST_ON: "INTEREST_ON";
172
211
  readonly DAILY_REPAYMENT_ON: "DAILY_REPAYMENT_ON";
@@ -179,6 +218,7 @@ declare const TokenTypes: {
179
218
  readonly TOTAL_LOAN_INTEREST_ON: "TOTAL_LOAN_INTEREST_ON";
180
219
  readonly TAX_ON: "TAX_ON";
181
220
  readonly TAX_OFF: "TAX_OFF";
221
+ readonly TAX_IN_PHRASE: "TAX_IN_PHRASE";
182
222
  readonly WHAT_IS: "WHAT_IS";
183
223
  readonly WHAT_WAS: "WHAT_WAS";
184
224
  readonly VALUE_OF: "VALUE_OF";
@@ -195,6 +235,10 @@ declare const TokenTypes: {
195
235
  readonly MONTH_ON: "MONTH_ON";
196
236
  readonly MONTH_IN: "MONTH_IN";
197
237
  readonly WEEK_ON: "WEEK_ON";
238
+ readonly DAYS_IN_PERIOD: "DAYS_IN_PERIOD";
239
+ readonly IN_TWO_UNITS: "IN_TWO_UNITS";
240
+ readonly PER_UNIT: "PER_UNIT";
241
+ readonly AT_RATE: "AT_RATE";
198
242
  readonly WEEK_IN: "WEEK_IN";
199
243
  readonly BETWEEN_UNIT: "BETWEEN_UNIT";
200
244
  readonly IS_WEEKEND: "IS_WEEKEND";
@@ -19,6 +19,20 @@ interface Token {
19
19
  lineBreaks: number;
20
20
  line: number;
21
21
  col: number;
22
+ /**
23
+ * End offset of the source text this token was fused from, exclusive.
24
+ *
25
+ * Only set on tokens the normalizer built by fusing several lexer tokens
26
+ * into one, and only there because those are the tokens whose `value` no
27
+ * longer describes the text they came from. `10 frames` fuses into a
28
+ * FRAME_COUNT whose value is `10`, so `offset + value.length` would cover
29
+ * two characters of a nine-character phrase.
30
+ *
31
+ * Evaluation never reads this. It exists so that a consumer painting a line
32
+ * (see `LanguageService.getSemanticTokens`) can highlight a fused phrase as
33
+ * the one thing the parser will see rather than as its parts.
34
+ */
35
+ sourceEnd?: number;
22
36
  }
23
37
  /**
24
38
  * Canonical token type name constants.
@@ -73,6 +87,7 @@ declare const TokenTypes: {
73
87
  readonly PI: "PI";
74
88
  readonly E: "E";
75
89
  readonly MOD: "MOD";
90
+ readonly AND_CONJ: "AND_CONJ";
76
91
  readonly OF: "OF";
77
92
  readonly INCREASE_BY: "INCREASE_BY";
78
93
  readonly DECREASE_BY: "DECREASE_BY";
@@ -124,6 +139,9 @@ declare const TokenTypes: {
124
139
  readonly ELSE: "ELSE";
125
140
  readonly AS: "AS";
126
141
  readonly CONVERTER_NAME: "CONVERTER_NAME";
142
+ readonly ROUNDED: "ROUNDED";
143
+ readonly TO_NEAREST: "TO_NEAREST";
144
+ readonly DECIMAL_PLACES: "DECIMAL_PLACES";
127
145
  readonly AVERAGE_OF: "AVERAGE_OF";
128
146
  readonly MEDIAN_OF: "MEDIAN_OF";
129
147
  readonly TOTAL_OF: "TOTAL_OF";
@@ -132,8 +150,22 @@ declare const TokenTypes: {
132
150
  readonly SMALLER_OF: "SMALLER_OF";
133
151
  readonly HALF_OF: "HALF_OF";
134
152
  readonly MIDPOINT_BETWEEN: "MIDPOINT_BETWEEN";
153
+ readonly REMAINDER_OF: "REMAINDER_OF";
154
+ readonly NTH_ROOT: "NTH_ROOT";
155
+ readonly LOG_PHRASE: "LOG_PHRASE";
156
+ readonly LOG_BASE: "LOG_BASE";
157
+ readonly GCD_OF: "GCD_OF";
158
+ readonly LCM_OF: "LCM_OF";
159
+ readonly SQUARE_ROOT_OF: "SQUARE_ROOT_OF";
160
+ readonly CUBE_ROOT_OF: "CUBE_ROOT_OF";
135
161
  readonly RANDOM_NUMBER: "RANDOM_NUMBER";
136
162
  readonly IS_TO: "IS_TO";
163
+ readonly IS: "IS";
164
+ readonly PCT_ON: "PCT_ON";
165
+ readonly PCT_OFF: "PCT_OFF";
166
+ readonly OF_WHAT: "OF_WHAT";
167
+ readonly OFF_WHAT: "OFF_WHAT";
168
+ readonly ON_WHAT: "ON_WHAT";
137
169
  readonly CLAMP: "CLAMP";
138
170
  readonly MAP: "MAP";
139
171
  readonly REDUCE: "REDUCE";
@@ -167,6 +199,13 @@ declare const TokenTypes: {
167
199
  readonly GLOBAL: "GLOBAL";
168
200
  readonly OVER: "OVER";
169
201
  readonly RATE_AT: "RATE_AT";
202
+ readonly AFTER: "AFTER";
203
+ readonly PRESENT_VALUE_OF: "PRESENT_VALUE_OF";
204
+ readonly ANNUAL_RETURN_ON: "ANNUAL_RETURN_ON";
205
+ readonly FOR_DURATION: "FOR_DURATION";
206
+ readonly COMPOUNDING: "COMPOUNDING";
207
+ readonly INVESTED: "INVESTED";
208
+ readonly RETURNED: "RETURNED";
170
209
  readonly COMPOUND_INTEREST_ON: "COMPOUND_INTEREST_ON";
171
210
  readonly INTEREST_ON: "INTEREST_ON";
172
211
  readonly DAILY_REPAYMENT_ON: "DAILY_REPAYMENT_ON";
@@ -179,6 +218,7 @@ declare const TokenTypes: {
179
218
  readonly TOTAL_LOAN_INTEREST_ON: "TOTAL_LOAN_INTEREST_ON";
180
219
  readonly TAX_ON: "TAX_ON";
181
220
  readonly TAX_OFF: "TAX_OFF";
221
+ readonly TAX_IN_PHRASE: "TAX_IN_PHRASE";
182
222
  readonly WHAT_IS: "WHAT_IS";
183
223
  readonly WHAT_WAS: "WHAT_WAS";
184
224
  readonly VALUE_OF: "VALUE_OF";
@@ -195,6 +235,10 @@ declare const TokenTypes: {
195
235
  readonly MONTH_ON: "MONTH_ON";
196
236
  readonly MONTH_IN: "MONTH_IN";
197
237
  readonly WEEK_ON: "WEEK_ON";
238
+ readonly DAYS_IN_PERIOD: "DAYS_IN_PERIOD";
239
+ readonly IN_TWO_UNITS: "IN_TWO_UNITS";
240
+ readonly PER_UNIT: "PER_UNIT";
241
+ readonly AT_RATE: "AT_RATE";
198
242
  readonly WEEK_IN: "WEEK_IN";
199
243
  readonly BETWEEN_UNIT: "BETWEEN_UNIT";
200
244
  readonly IS_WEEKEND: "IS_WEEKEND";