@atomic-ehr/fhirpath 0.0.1-canary.0c6931e.20250727185306

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 (85) hide show
  1. package/README.md +473 -0
  2. package/dist/index.d.ts +462 -0
  3. package/dist/index.js +10307 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +58 -0
  6. package/src/analyzer/analyzer.ts +499 -0
  7. package/src/analyzer/model-provider.ts +244 -0
  8. package/src/analyzer/schemas/index.ts +2 -0
  9. package/src/analyzer/schemas/types.ts +40 -0
  10. package/src/analyzer/types.ts +142 -0
  11. package/src/api/builder.ts +157 -0
  12. package/src/api/errors.ts +145 -0
  13. package/src/api/expression.ts +156 -0
  14. package/src/api/index.ts +122 -0
  15. package/src/api/inspect.ts +99 -0
  16. package/src/api/registry.ts +128 -0
  17. package/src/api/types.ts +210 -0
  18. package/src/compiler/compiler.ts +546 -0
  19. package/src/compiler/index.ts +2 -0
  20. package/src/compiler/prototype-context-adapter.ts +99 -0
  21. package/src/compiler/types.ts +24 -0
  22. package/src/index.ts +107 -0
  23. package/src/interpreter/README.md +78 -0
  24. package/src/interpreter/interpreter.ts +475 -0
  25. package/src/interpreter/types.ts +108 -0
  26. package/src/lexer/char-tables.ts +37 -0
  27. package/src/lexer/errors.ts +31 -0
  28. package/src/lexer/index.ts +5 -0
  29. package/src/lexer/lexer.ts +745 -0
  30. package/src/lexer/token.ts +104 -0
  31. package/src/lexer2/index.md +232 -0
  32. package/src/lexer2/index.perf.test.ts +68 -0
  33. package/src/lexer2/index.test.ts +549 -0
  34. package/src/lexer2/index.ts +1251 -0
  35. package/src/lexer2/notes.md +173 -0
  36. package/src/lexer2/optimization-summary.md +718 -0
  37. package/src/parser/ast-factory.ts +220 -0
  38. package/src/parser/ast.ts +144 -0
  39. package/src/parser/collection-parser.ts +89 -0
  40. package/src/parser/diagnostic-messages.ts +216 -0
  41. package/src/parser/diagnostics.ts +85 -0
  42. package/src/parser/error-reporter.ts +230 -0
  43. package/src/parser/index.ts +3 -0
  44. package/src/parser/literal-parser.ts +103 -0
  45. package/src/parser/parse-error.ts +16 -0
  46. package/src/parser/parser-error-factory.ts +141 -0
  47. package/src/parser/parser-state.ts +134 -0
  48. package/src/parser/parser.ts +1272 -0
  49. package/src/parser/pprint.ts +169 -0
  50. package/src/parser/precedence-manager.ts +64 -0
  51. package/src/parser/source-mapper.ts +248 -0
  52. package/src/parser/special-constructs.ts +142 -0
  53. package/src/parser/token-navigator.ts +110 -0
  54. package/src/parser/types.ts +60 -0
  55. package/src/parser2/index.md +177 -0
  56. package/src/parser2/index.perf.test.ts +184 -0
  57. package/src/parser2/index.test.ts +305 -0
  58. package/src/parser2/index.ts +578 -0
  59. package/src/parser2/optimization-summary.md +176 -0
  60. package/src/registry/default-analyzers.ts +257 -0
  61. package/src/registry/default-compilers.ts +31 -0
  62. package/src/registry/index.ts +96 -0
  63. package/src/registry/operations/arithmetic.ts +506 -0
  64. package/src/registry/operations/collection.ts +425 -0
  65. package/src/registry/operations/comparison.ts +432 -0
  66. package/src/registry/operations/existence.ts +703 -0
  67. package/src/registry/operations/filtering.ts +358 -0
  68. package/src/registry/operations/literals.ts +341 -0
  69. package/src/registry/operations/logical.ts +439 -0
  70. package/src/registry/operations/math.ts +128 -0
  71. package/src/registry/operations/membership.ts +132 -0
  72. package/src/registry/operations/navigation.ts +52 -0
  73. package/src/registry/operations/string.ts +507 -0
  74. package/src/registry/operations/subsetting.ts +174 -0
  75. package/src/registry/operations/type-checking.ts +162 -0
  76. package/src/registry/operations/type-conversion.ts +404 -0
  77. package/src/registry/operations/type-operators.ts +308 -0
  78. package/src/registry/operations/utility.ts +644 -0
  79. package/src/registry/registry.ts +146 -0
  80. package/src/registry/types.ts +161 -0
  81. package/src/registry/utils/evaluation-helpers.ts +93 -0
  82. package/src/registry/utils/index.ts +3 -0
  83. package/src/registry/utils/type-system.ts +173 -0
  84. package/src/runtime/context.ts +158 -0
  85. package/src/runtime/debug-context.ts +135 -0
@@ -0,0 +1,176 @@
1
+ # Parser2 Performance Optimization Summary
2
+
3
+ ## Baseline Performance (2024-01-27)
4
+
5
+ ### Test Configuration
6
+ - **Test Suite**: 1,539 FHIRPath expressions from real-world invariants and search parameters
7
+ - **Iterations**: 5,000 per expression (7,695,000 total parser runs)
8
+ - **Hardware**: Bun v1.2.18 runtime
9
+ - **Parser**: Recursive-descent with Pratt operator precedence parsing
10
+
11
+ ### Baseline Metrics
12
+
13
+ | Metric | Value |
14
+ |--------|-------|
15
+ | **Total time** | 6.51 seconds |
16
+ | **Time per expression** | 0.0008ms (0.8 microseconds) |
17
+ | **Expressions per second** | 1,182,443 |
18
+ | **Average tokens per expression** | 11.2 |
19
+ | **Average AST nodes per expression** | 8.6 |
20
+ | **Time per token** | ~0.08 microseconds |
21
+
22
+ ### Performance by Expression Complexity
23
+
24
+ | Token Range | Count | Avg Time (ms) | Time/Token (μs) |
25
+ |-------------|-------|---------------|-----------------|
26
+ | 1-5 tokens | 716 | 0.0003 | 0.08 |
27
+ | 6-10 tokens | 410 | 0.0005 | 0.08 |
28
+ | 11-20 tokens | 251 | 0.0010 | 0.07 |
29
+ | 21-50 tokens | 130 | 0.0024 | 0.08 |
30
+ | 50+ tokens | 32 | 0.0093 | 0.08 |
31
+
32
+ ### Key Observations
33
+
34
+ 1. **Linear Scaling**: Performance scales linearly with token count, indicating O(n) complexity
35
+ 2. **Consistent Token Processing**: ~0.08μs per token across all complexity ranges
36
+ 3. **Fast Baseline**: Already processing over 1 million expressions per second
37
+ 4. **Memory Efficiency**: All tokens are pre-loaded, enabling random access without re-parsing
38
+
39
+ ### Slowest Expressions
40
+
41
+ The most complex expression has 780 tokens and takes 0.0589ms to parse:
42
+ ```
43
+ (ActivityDefinition.useContext.value...
44
+ ```
45
+
46
+ This represents a worst-case scenario that's still processed in under 0.06ms.
47
+
48
+ ## Design Strengths
49
+
50
+ 1. **Pratt Parsing**: Eliminates recursive precedence climbing, reducing call stack depth
51
+ 2. **Pre-tokenization**: All tokens generated upfront, avoiding interleaved lexing/parsing
52
+ 3. **Direct Token Access**: Array-based token storage enables O(1) lookahead
53
+ 4. **Minimal Allocations**: AST nodes created only when needed
54
+ 5. **Type-safe Node Creation**: Separate factory methods avoid dynamic object construction
55
+
56
+ ## Comparison with Lexer2
57
+
58
+ | Component | Expressions/sec | Relative Speed |
59
+ |-----------|----------------|----------------|
60
+ | Lexer2 | ~15,000,000 | 1.0x (baseline) |
61
+ | Parser2 | ~1,200,000 | 0.08x |
62
+
63
+ The parser is ~12x slower than the lexer, which is expected since it:
64
+ - Builds a complete AST structure
65
+ - Performs syntax validation
66
+ - Handles operator precedence
67
+ - Manages recursive structures
68
+
69
+ ## Potential Optimizations
70
+
71
+ While the baseline performance is already excellent, potential optimizations include:
72
+
73
+ 1. **Token Caching**: Cache frequently accessed token values to avoid repeated string operations
74
+ 2. **Specialized Node Pools**: Pre-allocate common node types to reduce allocation overhead
75
+ 3. **Inline Small Functions**: Further inline hot-path functions like `peek()` and `check()`
76
+ 4. **Precedence Table**: Replace switch statement with lookup table for operator precedence
77
+ 5. **String Interning**: Intern common identifier strings to reduce memory and comparison costs
78
+
79
+ ## ANTLR Parser Comparison
80
+
81
+ To provide additional context, we compared parser2 performance against the ANTLR-generated parser:
82
+
83
+ ### Overall Performance Comparison
84
+
85
+ | Parser | Expressions/sec | Time per Expression | Relative Performance |
86
+ |--------|----------------|-------------------|---------------------|
87
+ | ANTLR | 143,965 | 0.0069ms | 1.0x (baseline) |
88
+ | Parser2 | 1,030,046 | 0.0010ms | **7.2x faster** |
89
+
90
+ ### Complex Expression Performance
91
+
92
+ | Expression | ANTLR (ms) | Parser2 (ms) | Speedup |
93
+ |------------|------------|--------------|---------|
94
+ | Patient.name.where(use = 'official').given | 0.009 | 0.001 | 7.7x |
95
+ | (ActivityDefinition.useContext.value... | 0.009 | 0.001 | 7.1x |
96
+ | ($this is Range) implies ((low.empty()... | 0.027 | 0.002 | 11.2x |
97
+ | extension.exists() or (contentType.co... | 0.047 | 0.001 | 33.1x |
98
+
99
+
100
+
101
+ ### Key Differences
102
+
103
+ 1. **Architecture**: ANTLR uses table-driven parsing while parser2 uses recursive descent with Pratt parsing
104
+ 2. **Overhead**: ANTLR has more runtime overhead due to its generic parsing framework
105
+ 3. **Scalability**: Parser2 shows better performance on complex expressions (up to 33x faster)
106
+ 4. **Error Recovery**: ANTLR provides better error recovery but at a performance cost
107
+
108
+ ## Optimization: Bit-Packed Precedence (2024-01-27)
109
+
110
+ ### Implementation
111
+ Encoded operator precedence directly into TokenType enum values using bit-packing:
112
+ - High byte: Precedence (0-255)
113
+ - Low byte: Token ID (0-255)
114
+
115
+ ### Results
116
+ | Metric | Before | After | Improvement |
117
+ |--------|--------|-------|-------------|
118
+ | Expressions/sec | 1,182,443 | 1,214,209 | +2.7% |
119
+ | Time per expression | 0.8μs | 0.8μs | - |
120
+ | getPrecedence cycles | ~5-10 | ~1 | -90% |
121
+
122
+ ### Code Simplification
123
+ - Removed 30+ line switch statement
124
+ - Replaced with single bit shift: `type >>> 8`
125
+ - Precedence now co-located with token definitions
126
+
127
+ ### Note
128
+ A direct precedence value approach (without bit-packing) was also tested but resulted in 8.8% performance degradation due to lookup table overhead. The bit-packed approach with bit shift operation provides the best balance of performance and maintainability.
129
+
130
+ ## Additional Optimizations (2024-01-27)
131
+
132
+ ### 1. Method Inlining
133
+ Inlined hot path methods (`peek()`, `advance()`, `isAtEnd()`) to reduce function call overhead in `parseExpressionWithPrecedence()` and `parsePrimary()`.
134
+
135
+ **Results**:
136
+ - Baseline: 1,203,640 expressions/sec
137
+ - After inlining: 1,223,027 expressions/sec (+1.6%)
138
+
139
+ ### 2. Node Creation Inlining (Reverted)
140
+ Attempted to inline frequently used node creation functions (`createBinaryNode`, `createLiteralNode`, `createIdentifierNode`) but observed performance regression due to increased code size affecting CPU cache and JIT optimization.
141
+
142
+ **Results**: Performance decreased to 1,184,463 expressions/sec - optimization was reverted.
143
+
144
+ ### 3. Lookup Tables for Token Type Checking (Abandoned)
145
+ Attempted to use Uint8Array lookup tables for binary operator and keyword checking, but abandoned due to bit-packed token values exceeding array bounds (values > 256).
146
+
147
+ ### 4. Position Tracking Impact Analysis
148
+ Temporarily disabled position tracking to measure its performance cost.
149
+
150
+ **Results**:
151
+ - With position tracking: 1,226,319 expressions/sec
152
+ - Without position tracking: 1,212,703 expressions/sec (-1.1%)
153
+
154
+ **Surprising finding**: Removing position tracking actually decreased performance slightly, likely due to:
155
+ - Position tracking overhead is minimal (simple object property assignment)
156
+ - Code changes disrupted JIT compiler optimizations
157
+ - Modern JS engines optimize object creation very well
158
+
159
+ ### Key Findings:
160
+ - Simple method inlining in hot paths provides consistent gains
161
+ - Over-optimization (excessive inlining) can hurt performance
162
+ - JIT compiler already optimizes method calls effectively
163
+ - Position tracking has negligible performance impact
164
+ - Bit-packed TokenType with getPrecedence bit shift remains the best optimization
165
+ - Current optimized performance: **1,223,027 expressions/sec** (1.6% improvement)
166
+
167
+ ## Conclusion
168
+
169
+ The parser2 performance is exceptional:
170
+ - **7.2x faster** than the ANTLR-generated parser
171
+ - Processing over **1.2 million expressions per second**
172
+ - Consistent **linear O(n) scaling** with token count
173
+ - Sub-microsecond parsing times for typical expressions
174
+
175
+
176
+ The combination of recursive-descent parsing with Pratt operator precedence, enhanced with bit-packed precedence lookup, provides an optimal balance of simplicity, maintainability, and performance. Even with already excellent baseline performance, targeted optimizations like bit-packed precedence can provide measurable improvements while simplifying the codebase.
@@ -0,0 +1,257 @@
1
+ import type {
2
+ Analyzer,
3
+ TypeInfo,
4
+ TypeRef,
5
+ Operator,
6
+ Function,
7
+ Literal,
8
+ TypeConstraint,
9
+ TypeInferenceRule,
10
+ CardinalityInferenceRule
11
+ } from './types';
12
+ import type { ModelProvider } from '../analyzer/types';
13
+
14
+ // Helper to check if type matches constraint
15
+ export function matchesConstraint(type: TypeRef, constraint: TypeConstraint): boolean {
16
+ if (constraint.kind === 'any') return true;
17
+
18
+ if (!constraint.types) return true;
19
+
20
+ // For now, simple type name matching
21
+ // TODO: Handle inheritance and complex types
22
+ let typeName = typeof type === 'string' ? type : (type as any).type || (type as any).name || 'unknown';
23
+
24
+ // Normalize case - constraint types are capitalized, but actual types might be lowercase
25
+ const normalizedTypeName = typeName.charAt(0).toUpperCase() + typeName.slice(1);
26
+
27
+ return constraint.types.includes(normalizedTypeName) || constraint.types.includes(typeName);
28
+ }
29
+
30
+ // Helper to format constraint for error messages
31
+ export function formatConstraint(constraint: TypeConstraint): string {
32
+ if (constraint.kind === 'any') return 'any type';
33
+ if (!constraint.types || constraint.types.length === 0) return 'any type';
34
+ if (constraint.types.length === 1) return constraint.types[0]!;
35
+ return constraint.types.join(' or ');
36
+ }
37
+
38
+ // Helper to resolve type inference rule
39
+ export function resolveTypeInferenceRule(
40
+ rule: TypeInferenceRule,
41
+ input: TypeRef,
42
+ args: TypeRef[],
43
+ analyzer: Analyzer
44
+ ): TypeRef {
45
+ if (typeof rule === 'string') {
46
+ if (rule === 'preserve-input') return input;
47
+ if (rule === 'promote-numeric') {
48
+ // Check if we have numeric types
49
+ const types = [input, ...args].map(t => {
50
+ const rawType = typeof t === 'string' ? t : (t as any).type || (t as any).name || 'unknown';
51
+ // Normalize to uppercase first letter
52
+ return rawType.charAt(0).toUpperCase() + rawType.slice(1);
53
+ });
54
+ if (types.includes('Decimal')) return analyzer.resolveType('Decimal');
55
+ if (types.includes('Integer')) return analyzer.resolveType('Integer');
56
+ return input; // fallback
57
+ }
58
+ return analyzer.resolveType(rule); // Fixed type name - resolve through analyzer
59
+ }
60
+
61
+ if (typeof rule === 'function') {
62
+ // TODO: We need to pass ModelProvider to the rule function
63
+ // For now, just return input
64
+ return input;
65
+ }
66
+
67
+ return input; // fallback
68
+ }
69
+
70
+ // Helper to resolve cardinality inference rule
71
+ export function resolveCardinalityInferenceRule(
72
+ rule: CardinalityInferenceRule,
73
+ inputIsSingleton: boolean,
74
+ argsAreSingleton: boolean[]
75
+ ): boolean {
76
+ if (rule === 'singleton') return true;
77
+ if (rule === 'collection') return false;
78
+ if (rule === 'preserve-input') return inputIsSingleton;
79
+ if (rule === 'all-singleton') return inputIsSingleton && argsAreSingleton.every(s => s);
80
+
81
+ if (typeof rule === 'function') {
82
+ return rule(inputIsSingleton, argsAreSingleton);
83
+ }
84
+
85
+ return inputIsSingleton; // fallback
86
+ }
87
+
88
+ // Default analyzer for operators
89
+ export function defaultOperatorAnalyze(
90
+ this: Operator,
91
+ analyzer: Analyzer,
92
+ input: TypeInfo,
93
+ args: TypeInfo[]
94
+ ): TypeInfo {
95
+ const { signature } = this;
96
+
97
+ // Validate parameter count
98
+ const expectedParams = signature.parameters.length;
99
+ if (args.length !== expectedParams) {
100
+ analyzer.error(`Operator '${this.name}' expects ${expectedParams} parameter(s) but got ${args.length}`);
101
+ }
102
+
103
+ // Validate parameter types and cardinality
104
+ signature.parameters.forEach((param, i) => {
105
+ const arg = args[i];
106
+ if (!arg) return;
107
+
108
+ // Check type constraint
109
+ if (param.types && !matchesConstraint(arg.type, param.types)) {
110
+ const argTypeName = typeof arg.type === 'string'
111
+ ? arg.type
112
+ : (arg.type as any).type || (arg.type as any).name || 'unknown';
113
+ analyzer.error(
114
+ `Operator '${this.name}' parameter '${param.name}' expects ${formatConstraint(param.types)} but got ${argTypeName}`
115
+ );
116
+ }
117
+
118
+ // Check cardinality constraint
119
+ if (param.cardinality) {
120
+ if (param.cardinality === 'singleton' && !arg.isSingleton) {
121
+ analyzer.error(
122
+ `Operator '${this.name}' parameter '${param.name}' requires singleton value but got collection`
123
+ );
124
+ } else if (param.cardinality === 'collection' && arg.isSingleton) {
125
+ analyzer.error(
126
+ `Operator '${this.name}' parameter '${param.name}' requires collection but got singleton`
127
+ );
128
+ }
129
+ // 'any' accepts both
130
+ }
131
+ });
132
+
133
+ // Determine output type
134
+ const outputType = resolveTypeInferenceRule(
135
+ signature.output.type,
136
+ input.type,
137
+ args.map(a => a.type),
138
+ analyzer
139
+ );
140
+
141
+ // Determine output cardinality
142
+ const isSingleton = resolveCardinalityInferenceRule(
143
+ signature.output.cardinality,
144
+ input.isSingleton,
145
+ args.map(a => a.isSingleton)
146
+ );
147
+
148
+ return { type: outputType, isSingleton };
149
+ }
150
+
151
+ // Default analyzer for functions
152
+ export function defaultFunctionAnalyze(
153
+ this: Function,
154
+ analyzer: Analyzer,
155
+ input: TypeInfo,
156
+ args: TypeInfo[]
157
+ ): TypeInfo {
158
+ const { signature } = this;
159
+
160
+ // Check input constraints if specified
161
+ if (signature.input) {
162
+ if (signature.input.types && !matchesConstraint(input.type, signature.input.types)) {
163
+ const inputTypeName = typeof input.type === 'string'
164
+ ? input.type
165
+ : (input.type as any).type || (input.type as any).name || 'unknown';
166
+ analyzer.error(
167
+ `Function '${this.name}' expects input of type ${formatConstraint(signature.input.types)} but got ${inputTypeName}`
168
+ );
169
+ }
170
+
171
+ if (signature.input.cardinality) {
172
+ if (signature.input.cardinality === 'singleton' && !input.isSingleton) {
173
+ analyzer.error(`Function '${this.name}' requires singleton input but got collection`);
174
+ } else if (signature.input.cardinality === 'collection' && input.isSingleton) {
175
+ analyzer.error(`Function '${this.name}' requires collection input but got singleton`);
176
+ }
177
+ }
178
+ }
179
+
180
+ // Validate parameters
181
+ const requiredParams = signature.parameters.filter(p => !p.optional);
182
+ if (args.length < requiredParams.length) {
183
+ analyzer.error(
184
+ `Function '${this.name}' requires at least ${requiredParams.length} parameter(s) but got ${args.length}`
185
+ );
186
+ }
187
+
188
+ if (args.length > signature.parameters.length) {
189
+ analyzer.error(
190
+ `Function '${this.name}' expects at most ${signature.parameters.length} parameter(s) but got ${args.length}`
191
+ );
192
+ }
193
+
194
+ // Check each parameter
195
+ signature.parameters.forEach((param, i) => {
196
+ const arg = args[i];
197
+ if (!arg && !param.optional) {
198
+ analyzer.error(`Function '${this.name}' parameter '${param.name}' is required`);
199
+ return;
200
+ }
201
+ if (!arg) return; // Optional parameter not provided
202
+
203
+ // Check type constraint
204
+ if (param.types && !matchesConstraint(arg.type, param.types)) {
205
+ const argTypeName = typeof arg.type === 'string'
206
+ ? arg.type
207
+ : (arg.type as any).type || (arg.type as any).name || 'unknown';
208
+ analyzer.error(
209
+ `Function '${this.name}' parameter '${param.name}' expects ${formatConstraint(param.types)} but got ${argTypeName}`
210
+ );
211
+ }
212
+
213
+ // Check cardinality constraint
214
+ if (param.cardinality) {
215
+ if (param.cardinality === 'singleton' && !arg.isSingleton) {
216
+ analyzer.error(
217
+ `Function '${this.name}' parameter '${param.name}' requires singleton value but got collection`
218
+ );
219
+ } else if (param.cardinality === 'collection' && arg.isSingleton) {
220
+ analyzer.error(
221
+ `Function '${this.name}' parameter '${param.name}' requires collection but got singleton`
222
+ );
223
+ }
224
+ }
225
+ });
226
+
227
+ // Determine output type
228
+ const outputType = resolveTypeInferenceRule(
229
+ signature.output.type,
230
+ input.type,
231
+ args.map(a => a.type),
232
+ analyzer
233
+ );
234
+
235
+ // Determine output cardinality
236
+ const isSingleton = resolveCardinalityInferenceRule(
237
+ signature.output.cardinality,
238
+ input.isSingleton,
239
+ args.map(a => a.isSingleton)
240
+ );
241
+
242
+ return { type: outputType, isSingleton };
243
+ }
244
+
245
+ // Default analyzer for literals
246
+ export function defaultLiteralAnalyze(
247
+ this: Literal,
248
+ analyzer: Analyzer,
249
+ input: TypeInfo,
250
+ args: TypeInfo[]
251
+ ): TypeInfo {
252
+ // Literals have fixed output type and cardinality
253
+ return {
254
+ type: analyzer.resolveType(this.signature.output.type),
255
+ isSingleton: true
256
+ };
257
+ }
@@ -0,0 +1,31 @@
1
+ import type { Compiler, CompiledExpression } from './types';
2
+
3
+ export function defaultFunctionCompile(
4
+ compiler: Compiler,
5
+ input: CompiledExpression,
6
+ args: CompiledExpression[]
7
+ ): CompiledExpression {
8
+ // Default implementation returns a simple pass-through
9
+ return {
10
+ fn: (ctx) => {
11
+ throw new Error('Function compile not implemented');
12
+ },
13
+ type: compiler.resolveType('Any'),
14
+ isSingleton: false
15
+ };
16
+ }
17
+
18
+ export function defaultOperatorCompile(
19
+ compiler: Compiler,
20
+ left: CompiledExpression,
21
+ right: CompiledExpression[]
22
+ ): CompiledExpression {
23
+ // Default implementation returns a simple pass-through
24
+ return {
25
+ fn: (ctx) => {
26
+ throw new Error('Operator compile not implemented');
27
+ },
28
+ type: compiler.resolveType('Any'),
29
+ isSingleton: false
30
+ };
31
+ }
@@ -0,0 +1,96 @@
1
+ import { Registry } from './registry';
2
+ import { arithmeticOperators } from './operations/arithmetic';
3
+ import { logicalOperators, logicalFunctions } from './operations/logical';
4
+ import { comparisonOperators } from './operations/comparison';
5
+ import { membershipOperators } from './operations/membership';
6
+ import { typeOperators } from './operations/type-operators';
7
+ import { literals } from './operations/literals';
8
+ import { existenceFunctions } from './operations/existence';
9
+ import { navigationOperators } from './operations/navigation';
10
+ import {
11
+ aggregateFunction,
12
+ childrenFunction,
13
+ descendantsFunction,
14
+ iifFunction,
15
+ defineVariableFunction,
16
+ traceFunction,
17
+ checkFunction
18
+ } from './operations/utility';
19
+ import { typeFunction, isFunction, asFunction } from './operations/type-checking';
20
+ import { absFunction, roundFunction, sqrtFunction } from './operations/math';
21
+ import { whereFunction, selectFunction, ofTypeFunction, repeatFunction } from './operations/filtering';
22
+ import {
23
+ containsFunction, lengthFunction, substringFunction, startsWithFunction,
24
+ endsWithFunction, upperFunction, lowerFunction, indexOfFunction,
25
+ replaceFunction, splitFunction, joinFunction, trimFunction, toCharsFunction
26
+ } from './operations/string';
27
+ import {
28
+ toStringFunction, toIntegerFunction, toDecimalFunction,
29
+ toBooleanFunction, toQuantityFunction
30
+ } from './operations/type-conversion';
31
+ import { tailFunction, skipFunction, takeFunction } from './operations/subsetting';
32
+ import { unionFunction, combineFunction, intersectFunction, excludeFunction, collectionOperators } from './operations/collection';
33
+
34
+ // Export types
35
+ export * from './types';
36
+ export { Registry } from './registry';
37
+ export * from './default-analyzers';
38
+
39
+ // Register all operations on module load
40
+ [
41
+ ...arithmeticOperators,
42
+ ...logicalOperators,
43
+ ...logicalFunctions,
44
+ ...comparisonOperators,
45
+ ...membershipOperators,
46
+ ...typeOperators,
47
+ ...collectionOperators,
48
+ ...navigationOperators,
49
+ ...literals,
50
+ ...existenceFunctions,
51
+ aggregateFunction,
52
+ childrenFunction,
53
+ descendantsFunction,
54
+ iifFunction,
55
+ defineVariableFunction,
56
+ traceFunction,
57
+ checkFunction,
58
+ typeFunction,
59
+ isFunction,
60
+ asFunction,
61
+ absFunction,
62
+ roundFunction,
63
+ sqrtFunction,
64
+ whereFunction,
65
+ selectFunction,
66
+ ofTypeFunction,
67
+ repeatFunction,
68
+ containsFunction,
69
+ lengthFunction,
70
+ substringFunction,
71
+ startsWithFunction,
72
+ endsWithFunction,
73
+ upperFunction,
74
+ lowerFunction,
75
+ indexOfFunction,
76
+ replaceFunction,
77
+ splitFunction,
78
+ joinFunction,
79
+ trimFunction,
80
+ toCharsFunction,
81
+ toStringFunction,
82
+ toIntegerFunction,
83
+ toDecimalFunction,
84
+ toBooleanFunction,
85
+ toQuantityFunction,
86
+ tailFunction,
87
+ skipFunction,
88
+ takeFunction,
89
+ unionFunction,
90
+ combineFunction,
91
+ intersectFunction,
92
+ excludeFunction
93
+ ].forEach(op => Registry.register(op));
94
+
95
+ // Export default registry instance
96
+ export default Registry;