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,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 };
|