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.
- package/dist/BytecodeBuilder-3egMyuDi.d.cts +274 -0
- package/dist/BytecodeBuilder-3egMyuDi.d.ts +274 -0
- package/dist/Configuration-fVpd-HC-.d.cts +228 -0
- package/dist/Configuration-fVpd-HC-.d.ts +228 -0
- package/dist/EngineError-DvPSnhZi.d.cts +304 -0
- package/dist/EngineError-DvPSnhZi.d.ts +304 -0
- package/dist/Lexer-BJdhlnej.d.cts +531 -0
- package/dist/Lexer-D9l4Yrk2.d.ts +531 -0
- package/dist/NormalizerRule-BrVoVjmP.d.cts +163 -0
- package/dist/NormalizerRule-CEjf1FyD.d.ts +163 -0
- package/dist/PackageRegistry-D-Tv_7ca.d.cts +2572 -0
- package/dist/PackageRegistry-DaH4aIKP.d.ts +2572 -0
- package/dist/Parselet-CK8cQu2a.d.cts +299 -0
- package/dist/Parselet-ConOIdRO.d.ts +299 -0
- package/dist/ScopeManager-B5k0nk05.d.ts +559 -0
- package/dist/ScopeManager-CKfQtl7l.d.cts +559 -0
- package/dist/Token-0jpvUdPY.d.cts +245 -0
- package/dist/Token-0jpvUdPY.d.ts +245 -0
- package/dist/VMCheckpoints-BmDwIXgK.d.ts +171 -0
- package/dist/VMCheckpoints-CqFuLwrT.d.cts +171 -0
- package/dist/Value-KtAQNHur.d.cts +295 -0
- package/dist/Value-KtAQNHur.d.ts +295 -0
- package/dist/chunk-2RWAXT6O.js +3 -0
- package/dist/chunk-2RWAXT6O.js.map +1 -0
- package/dist/chunk-34RRD7PC.js +1492 -0
- package/dist/chunk-34RRD7PC.js.map +1 -0
- package/dist/chunk-3PPFLFH4.js +5097 -0
- package/dist/chunk-3PPFLFH4.js.map +1 -0
- package/dist/chunk-3VGDNEE3.js +84 -0
- package/dist/chunk-3VGDNEE3.js.map +1 -0
- package/dist/chunk-3YNVWKR2.cjs +370 -0
- package/dist/chunk-3YNVWKR2.cjs.map +1 -0
- package/dist/chunk-47LRVGOT.cjs +316 -0
- package/dist/chunk-47LRVGOT.cjs.map +1 -0
- package/dist/chunk-4MG4XKO2.js +1644 -0
- package/dist/chunk-4MG4XKO2.js.map +1 -0
- package/dist/chunk-4QADQTWS.js +2392 -0
- package/dist/chunk-4QADQTWS.js.map +1 -0
- package/dist/chunk-53B6KDDJ.cjs +1651 -0
- package/dist/chunk-53B6KDDJ.cjs.map +1 -0
- package/dist/chunk-5X2PTP6F.cjs +104 -0
- package/dist/chunk-5X2PTP6F.cjs.map +1 -0
- package/dist/chunk-5YEMOYSE.js +264 -0
- package/dist/chunk-5YEMOYSE.js.map +1 -0
- package/dist/chunk-64W6GLLZ.js +481 -0
- package/dist/chunk-64W6GLLZ.js.map +1 -0
- package/dist/chunk-6BKTCEUP.cjs +233 -0
- package/dist/chunk-6BKTCEUP.cjs.map +1 -0
- package/dist/chunk-6NTVRDQV.cjs +1642 -0
- package/dist/chunk-6NTVRDQV.cjs.map +1 -0
- package/dist/chunk-6RDSQURE.js +290 -0
- package/dist/chunk-6RDSQURE.js.map +1 -0
- package/dist/chunk-75JP4BZX.js +93 -0
- package/dist/chunk-75JP4BZX.js.map +1 -0
- package/dist/chunk-B7LXJ6LC.cjs +36 -0
- package/dist/chunk-B7LXJ6LC.cjs.map +1 -0
- package/dist/chunk-BVAR5J2F.js +257 -0
- package/dist/chunk-BVAR5J2F.js.map +1 -0
- package/dist/chunk-C4XZV6E7.cjs +269 -0
- package/dist/chunk-C4XZV6E7.cjs.map +1 -0
- package/dist/chunk-CCJPSHGI.cjs +40 -0
- package/dist/chunk-CCJPSHGI.cjs.map +1 -0
- package/dist/chunk-CLL7RUQV.cjs +316 -0
- package/dist/chunk-CLL7RUQV.cjs.map +1 -0
- package/dist/chunk-DM3LMRBC.js +329 -0
- package/dist/chunk-DM3LMRBC.js.map +1 -0
- package/dist/chunk-DUEZFTK2.js +38 -0
- package/dist/chunk-DUEZFTK2.js.map +1 -0
- package/dist/chunk-EBSPLUW4.cjs +489 -0
- package/dist/chunk-EBSPLUW4.cjs.map +1 -0
- package/dist/chunk-EHAHVROS.cjs +261 -0
- package/dist/chunk-EHAHVROS.cjs.map +1 -0
- package/dist/chunk-EIGTWK5N.js +167 -0
- package/dist/chunk-EIGTWK5N.js.map +1 -0
- package/dist/chunk-EPOXXJBK.js +96 -0
- package/dist/chunk-EPOXXJBK.js.map +1 -0
- package/dist/chunk-FEAIOR7N.cjs +91 -0
- package/dist/chunk-FEAIOR7N.cjs.map +1 -0
- package/dist/chunk-G535KJEG.js +312 -0
- package/dist/chunk-G535KJEG.js.map +1 -0
- package/dist/chunk-GCM5JHMX.js +88 -0
- package/dist/chunk-GCM5JHMX.js.map +1 -0
- package/dist/chunk-GOLDJNMZ.js +368 -0
- package/dist/chunk-GOLDJNMZ.js.map +1 -0
- package/dist/chunk-GW32KPCU.cjs +360 -0
- package/dist/chunk-GW32KPCU.cjs.map +1 -0
- package/dist/chunk-HTXVVJRA.cjs +1549 -0
- package/dist/chunk-HTXVVJRA.cjs.map +1 -0
- package/dist/chunk-HWSZJQCI.js +311 -0
- package/dist/chunk-HWSZJQCI.js.map +1 -0
- package/dist/chunk-IF532O7C.js +101 -0
- package/dist/chunk-IF532O7C.js.map +1 -0
- package/dist/chunk-J73SJHR3.cjs +2415 -0
- package/dist/chunk-J73SJHR3.cjs.map +1 -0
- package/dist/chunk-JBSYC7BB.cjs +7473 -0
- package/dist/chunk-JBSYC7BB.cjs.map +1 -0
- package/dist/chunk-KVILKGMS.js +228 -0
- package/dist/chunk-KVILKGMS.js.map +1 -0
- package/dist/chunk-LIPPNDBE.js +258 -0
- package/dist/chunk-LIPPNDBE.js.map +1 -0
- package/dist/chunk-LR7YASZF.cjs +172 -0
- package/dist/chunk-LR7YASZF.cjs.map +1 -0
- package/dist/chunk-M4N5GA5Z.cjs +309 -0
- package/dist/chunk-M4N5GA5Z.cjs.map +1 -0
- package/dist/chunk-M5LX5AOO.js +7459 -0
- package/dist/chunk-M5LX5AOO.js.map +1 -0
- package/dist/chunk-MZOVP3WI.cjs +96 -0
- package/dist/chunk-MZOVP3WI.cjs.map +1 -0
- package/dist/chunk-N5YFR4UP.cjs +10 -0
- package/dist/chunk-N5YFR4UP.cjs.map +1 -0
- package/dist/chunk-NGBBNL4K.cjs +4 -0
- package/dist/chunk-NGBBNL4K.cjs.map +1 -0
- package/dist/chunk-NH2O2AUR.js +146 -0
- package/dist/chunk-NH2O2AUR.js.map +1 -0
- package/dist/chunk-NLNDMWZC.js +31 -0
- package/dist/chunk-NLNDMWZC.js.map +1 -0
- package/dist/chunk-NMCRQP3Z.cjs +778 -0
- package/dist/chunk-NMCRQP3Z.cjs.map +1 -0
- package/dist/chunk-NMD5VRN4.cjs +1327 -0
- package/dist/chunk-NMD5VRN4.cjs.map +1 -0
- package/dist/chunk-OC6LTXYS.cjs +286 -0
- package/dist/chunk-OC6LTXYS.cjs.map +1 -0
- package/dist/chunk-OT6OJY7C.cjs +114 -0
- package/dist/chunk-OT6OJY7C.cjs.map +1 -0
- package/dist/chunk-PFSDGGYL.js +8 -0
- package/dist/chunk-PFSDGGYL.js.map +1 -0
- package/dist/chunk-QNJ4ACRT.cjs +155 -0
- package/dist/chunk-QNJ4ACRT.cjs.map +1 -0
- package/dist/chunk-QWY3VEZN.js +1637 -0
- package/dist/chunk-QWY3VEZN.js.map +1 -0
- package/dist/chunk-RFYD5TJE.js +111 -0
- package/dist/chunk-RFYD5TJE.js.map +1 -0
- package/dist/chunk-RIN643A3.js +354 -0
- package/dist/chunk-RIN643A3.js.map +1 -0
- package/dist/chunk-RZCWSXTA.cjs +5122 -0
- package/dist/chunk-RZCWSXTA.cjs.map +1 -0
- package/dist/chunk-SDGRK7EP.js +774 -0
- package/dist/chunk-SDGRK7EP.js.map +1 -0
- package/dist/chunk-VEKIWQ3F.cjs +39 -0
- package/dist/chunk-VEKIWQ3F.cjs.map +1 -0
- package/dist/chunk-WQTTOGXC.cjs +99 -0
- package/dist/chunk-WQTTOGXC.cjs.map +1 -0
- package/dist/chunk-X6K3HUOY.cjs +86 -0
- package/dist/chunk-X6K3HUOY.cjs.map +1 -0
- package/dist/chunk-XJCMXB2E.cjs +333 -0
- package/dist/chunk-XJCMXB2E.cjs.map +1 -0
- package/dist/chunk-XVWCOTR6.js +1324 -0
- package/dist/chunk-XVWCOTR6.js.map +1 -0
- package/dist/chunk-YLJ4XMA6.js +33 -0
- package/dist/chunk-YLJ4XMA6.js.map +1 -0
- package/dist/chunk-Z2XP767T.cjs +35 -0
- package/dist/chunk-Z2XP767T.cjs.map +1 -0
- package/dist/chunk-Z4X2RWOF.js +37 -0
- package/dist/chunk-Z4X2RWOF.js.map +1 -0
- package/dist/constants.cjs +22 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.d.cts +12 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -0
- package/dist/engine.cjs +79 -0
- package/dist/engine.cjs.map +1 -0
- package/dist/engine.d.cts +587 -0
- package/dist/engine.d.ts +587 -0
- package/dist/engine.js +34 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.cjs +81 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +95 -0
- package/dist/errors.d.ts +95 -0
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -0
- package/dist/format.cjs +171 -0
- package/dist/format.cjs.map +1 -0
- package/dist/format.d.cts +51 -0
- package/dist/format.d.ts +51 -0
- package/dist/format.js +168 -0
- package/dist/format.js.map +1 -0
- package/dist/index.cjs +97 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +152 -0
- package/dist/index.d.ts +152 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/language.cjs +308 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +110 -0
- package/dist/language.d.ts +110 -0
- package/dist/language.js +287 -0
- package/dist/language.js.map +1 -0
- package/dist/lexer.cjs +64 -0
- package/dist/lexer.cjs.map +1 -0
- package/dist/lexer.d.cts +56 -0
- package/dist/lexer.d.ts +56 -0
- package/dist/lexer.js +11 -0
- package/dist/lexer.js.map +1 -0
- package/dist/normalizer.cjs +40 -0
- package/dist/normalizer.cjs.map +1 -0
- package/dist/normalizer.d.cts +364 -0
- package/dist/normalizer.d.ts +364 -0
- package/dist/normalizer.js +11 -0
- package/dist/normalizer.js.map +1 -0
- package/dist/packages.cjs +121 -0
- package/dist/packages.cjs.map +1 -0
- package/dist/packages.d.cts +691 -0
- package/dist/packages.d.ts +691 -0
- package/dist/packages.js +24 -0
- package/dist/packages.js.map +1 -0
- package/dist/parser.cjs +55 -0
- package/dist/parser.cjs.map +1 -0
- package/dist/parser.d.cts +46 -0
- package/dist/parser.d.ts +46 -0
- package/dist/parser.js +10 -0
- package/dist/parser.js.map +1 -0
- package/dist/pipeline-CrsRPmsv.d.ts +285 -0
- package/dist/pipeline-DPUQt7yL.d.cts +285 -0
- package/dist/resolvers.cjs +13 -0
- package/dist/resolvers.cjs.map +1 -0
- package/dist/resolvers.d.cts +111 -0
- package/dist/resolvers.d.ts +111 -0
- package/dist/resolvers.js +4 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/services.cjs +21 -0
- package/dist/services.cjs.map +1 -0
- package/dist/services.d.cts +30 -0
- package/dist/services.d.ts +30 -0
- package/dist/services.js +4 -0
- package/dist/services.js.map +1 -0
- package/dist/uom.cjs +56 -0
- package/dist/uom.cjs.map +1 -0
- package/dist/uom.d.cts +224 -0
- package/dist/uom.d.ts +224 -0
- package/dist/uom.js +11 -0
- package/dist/uom.js.map +1 -0
- package/dist/utilities.cjs +32 -0
- package/dist/utilities.cjs.map +1 -0
- package/dist/utilities.d.cts +113 -0
- package/dist/utilities.d.ts +113 -0
- package/dist/utilities.js +7 -0
- package/dist/utilities.js.map +1 -0
- package/dist/variables.cjs +17 -0
- package/dist/variables.cjs.map +1 -0
- package/dist/variables.d.cts +87 -0
- package/dist/variables.d.ts +87 -0
- package/dist/variables.js +4 -0
- package/dist/variables.js.map +1 -0
- package/dist/vm.cjs +284 -0
- package/dist/vm.cjs.map +1 -0
- package/dist/vm.d.cts +68 -0
- package/dist/vm.d.ts +68 -0
- package/dist/vm.js +194 -0
- package/dist/vm.js.map +1 -0
- 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 };
|