@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,506 @@
1
+ import { TokenType } from '../../lexer/token';
2
+ import type { Operator, TypeRef } from '../types';
3
+ import { defaultOperatorAnalyze } from '../default-analyzers';
4
+ import { toSingleton } from '../utils';
5
+
6
+ // Helper to promote numeric types
7
+ function promoteNumericType(left: TypeRef, right: TypeRef): TypeRef {
8
+ const leftName = typeof left === 'string' ? left : (left as any).name;
9
+ const rightName = typeof right === 'string' ? right : (right as any).name;
10
+
11
+ if (leftName === 'Decimal' || rightName === 'Decimal') return 'Decimal';
12
+ if (leftName === 'Integer' && rightName === 'Integer') return 'Integer';
13
+ return 'Decimal'; // Default to Decimal for safety
14
+ }
15
+
16
+ export const plusOperator: Operator = {
17
+ name: '+',
18
+ kind: 'operator',
19
+
20
+ syntax: {
21
+ form: 'infix',
22
+ token: TokenType.PLUS,
23
+ precedence: 9, // ADDITIVE precedence
24
+ associativity: 'left',
25
+ notation: 'a + b'
26
+ },
27
+
28
+ signature: {
29
+ parameters: [
30
+ { name: 'left', types: { kind: 'union', types: ['Integer', 'Decimal', 'String', 'Date', 'DateTime', 'Time'] }, cardinality: 'singleton' },
31
+ { name: 'right', types: { kind: 'union', types: ['Integer', 'Decimal', 'String', 'Quantity'] }, cardinality: 'singleton' }
32
+ ],
33
+ output: {
34
+ type: 'promote-numeric',
35
+ cardinality: 'singleton'
36
+ },
37
+ propagatesEmpty: true
38
+ },
39
+
40
+ analyze: function(analyzer, input, args) {
41
+ // First run default validation
42
+ const result = defaultOperatorAnalyze.call(this, analyzer, input, args);
43
+
44
+ // Additional validation: both operands should be same "category" (numeric vs string)
45
+ const leftType = args[0]?.type;
46
+ const rightType = args[1]?.type;
47
+
48
+ const leftTypeName = typeof leftType === 'string' ? leftType : (leftType as any).type || (leftType as any).name || 'unknown';
49
+ const rightTypeName = typeof rightType === 'string' ? rightType : (rightType as any).type || (rightType as any).name || 'unknown';
50
+
51
+ const isLeftString = leftTypeName === 'String' || leftTypeName === 'string';
52
+ const isRightString = rightTypeName === 'String' || rightTypeName === 'string';
53
+ const isLeftNumeric = ['Integer', 'Decimal', 'integer', 'decimal'].includes(leftTypeName);
54
+ const isRightNumeric = ['Integer', 'Decimal', 'integer', 'decimal'].includes(rightTypeName);
55
+
56
+ // If one is string and other is numeric, that's an error
57
+ if ((isLeftString && isRightNumeric) || (isLeftNumeric && isRightString)) {
58
+ analyzer.error(`Operator '+' cannot be applied to types ${leftTypeName} and ${rightTypeName}`);
59
+ }
60
+
61
+ return result;
62
+ },
63
+
64
+ evaluate: (interpreter, context, input, left, right) => {
65
+ if (left.length === 0 || right.length === 0) return { value: [], context };
66
+
67
+ const l = toSingleton(left);
68
+ const r = toSingleton(right);
69
+
70
+ // String concatenation
71
+ if (typeof l === 'string' || typeof r === 'string') {
72
+ return { value: [String(l) + String(r)], context };
73
+ }
74
+
75
+ // Numeric addition
76
+ return { value: [l + r], context };
77
+ },
78
+
79
+ compile: (compiler, input, args) => ({
80
+ fn: (ctx) => {
81
+ const left = args[0]?.fn(ctx) || [];
82
+ const right = args[1]?.fn(ctx) || [];
83
+ if (left.length === 0 || right.length === 0) return [];
84
+
85
+ const l = toSingleton(left);
86
+ const r = toSingleton(right);
87
+
88
+ if (typeof l === 'string' || typeof r === 'string') {
89
+ return [String(l) + String(r)];
90
+ }
91
+
92
+ return [l + r];
93
+ },
94
+ type: promoteNumericType(args[0]?.type, args[1]?.type),
95
+ isSingleton: true,
96
+ source: `${args[0]?.source || ''} + ${args[1]?.source || ''}`
97
+ })
98
+ };
99
+
100
+ export const minusOperator: Operator = {
101
+ name: '-',
102
+ kind: 'operator',
103
+
104
+ syntax: {
105
+ form: 'infix',
106
+ token: TokenType.MINUS,
107
+ precedence: 9, // ADDITIVE precedence
108
+ associativity: 'left',
109
+ notation: 'a - b'
110
+ },
111
+
112
+ signature: {
113
+ parameters: [
114
+ { name: 'left', types: { kind: 'union', types: ['Integer', 'Decimal', 'Date', 'DateTime', 'Time'] }, cardinality: 'singleton' },
115
+ { name: 'right', types: { kind: 'union', types: ['Integer', 'Decimal', 'Quantity'] }, cardinality: 'singleton' }
116
+ ],
117
+ output: {
118
+ type: 'promote-numeric',
119
+ cardinality: 'singleton'
120
+ },
121
+ propagatesEmpty: true
122
+ },
123
+
124
+ analyze: defaultOperatorAnalyze,
125
+
126
+ evaluate: (interpreter, context, input, left, right) => {
127
+ if (left.length === 0 || right.length === 0) return { value: [], context };
128
+
129
+ const l = toSingleton(left);
130
+ const r = toSingleton(right);
131
+
132
+ return { value: [l - r], context };
133
+ },
134
+
135
+ compile: (compiler, input, args) => ({
136
+ fn: (ctx) => {
137
+ const left = args[0]?.fn(ctx) || [];
138
+ const right = args[1]?.fn(ctx) || [];
139
+ if (left.length === 0 || right.length === 0) return [];
140
+
141
+ return [toSingleton(left) - toSingleton(right)];
142
+ },
143
+ type: promoteNumericType(args[0]?.type, args[1]?.type),
144
+ isSingleton: true,
145
+ source: `${args[0]?.source || ''} - ${args[1]?.source || ''}`
146
+ })
147
+ };
148
+
149
+ export const multiplyOperator: Operator = {
150
+ name: '*',
151
+ kind: 'operator',
152
+
153
+ syntax: {
154
+ form: 'infix',
155
+ token: TokenType.STAR,
156
+ precedence: 10, // MULTIPLICATIVE precedence
157
+ associativity: 'left',
158
+ notation: 'a * b'
159
+ },
160
+
161
+ signature: {
162
+ parameters: [
163
+ { name: 'left', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' },
164
+ { name: 'right', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' }
165
+ ],
166
+ output: {
167
+ type: 'promote-numeric',
168
+ cardinality: 'singleton'
169
+ },
170
+ propagatesEmpty: true
171
+ },
172
+
173
+ analyze: defaultOperatorAnalyze,
174
+
175
+ evaluate: (interpreter, context, input, left, right) => {
176
+ if (left.length === 0 || right.length === 0) return { value: [], context };
177
+
178
+ const l = toSingleton(left);
179
+ const r = toSingleton(right);
180
+
181
+ return { value: [l * r], context };
182
+ },
183
+
184
+ compile: (compiler, input, args) => ({
185
+ fn: (ctx) => {
186
+ const left = args[0]?.fn(ctx) || [];
187
+ const right = args[1]?.fn(ctx) || [];
188
+ if (left.length === 0 || right.length === 0) return [];
189
+
190
+ return [toSingleton(left) * toSingleton(right)];
191
+ },
192
+ type: promoteNumericType(args[0]?.type, args[1]?.type),
193
+ isSingleton: true,
194
+ source: `${args[0]?.source || ''} * ${args[1]?.source || ''}`
195
+ })
196
+ };
197
+
198
+ export const divideOperator: Operator = {
199
+ name: '/',
200
+ kind: 'operator',
201
+
202
+ syntax: {
203
+ form: 'infix',
204
+ token: TokenType.SLASH,
205
+ precedence: 10, // MULTIPLICATIVE precedence
206
+ associativity: 'left',
207
+ notation: 'a / b'
208
+ },
209
+
210
+ signature: {
211
+ parameters: [
212
+ { name: 'left', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' },
213
+ { name: 'right', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' }
214
+ ],
215
+ output: {
216
+ type: 'Decimal', // Division always returns Decimal
217
+ cardinality: 'singleton'
218
+ },
219
+ propagatesEmpty: true
220
+ },
221
+
222
+ analyze: defaultOperatorAnalyze,
223
+
224
+ evaluate: (interpreter, context, input, left, right) => {
225
+ if (left.length === 0 || right.length === 0) return { value: [], context };
226
+
227
+ const l = toSingleton(left);
228
+ const r = toSingleton(right);
229
+
230
+ if (r === 0) return { value: [], context }; // Division by zero returns empty
231
+
232
+ return { value: [l / r], context };
233
+ },
234
+
235
+ compile: (compiler, input, args) => ({
236
+ fn: (ctx) => {
237
+ const left = args[0]?.fn(ctx) || [];
238
+ const right = args[1]?.fn(ctx) || [];
239
+ if (left.length === 0 || right.length === 0) return [];
240
+
241
+ const r = toSingleton(right);
242
+ if (r === 0) return [];
243
+
244
+ return [toSingleton(left) / r];
245
+ },
246
+ type: compiler.resolveType('Decimal'),
247
+ isSingleton: true,
248
+ source: `${args[0]?.source || ''} / ${args[1]?.source || ''}`
249
+ })
250
+ };
251
+
252
+ export const modOperator: Operator = {
253
+ name: 'mod',
254
+ kind: 'operator',
255
+
256
+ syntax: {
257
+ form: 'infix',
258
+ token: TokenType.MOD,
259
+ precedence: 10, // MULTIPLICATIVE precedence
260
+ associativity: 'left',
261
+ notation: 'a mod b'
262
+ },
263
+
264
+ signature: {
265
+ parameters: [
266
+ { name: 'left', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' },
267
+ { name: 'right', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' }
268
+ ],
269
+ output: {
270
+ type: 'promote-numeric',
271
+ cardinality: 'singleton'
272
+ },
273
+ propagatesEmpty: true
274
+ },
275
+
276
+ analyze: defaultOperatorAnalyze,
277
+
278
+ evaluate: (interpreter, context, input, left, right) => {
279
+ if (left.length === 0 || right.length === 0) return { value: [], context };
280
+
281
+ const l = toSingleton(left);
282
+ const r = toSingleton(right);
283
+
284
+ if (r === 0) return { value: [], context }; // Modulo by zero returns empty
285
+
286
+ return { value: [l % r], context };
287
+ },
288
+
289
+ compile: (compiler, input, args) => ({
290
+ fn: (ctx) => {
291
+ const left = args[0]?.fn(ctx) || [];
292
+ const right = args[1]?.fn(ctx) || [];
293
+ if (left.length === 0 || right.length === 0) return [];
294
+
295
+ const r = toSingleton(right);
296
+ if (r === 0) return [];
297
+
298
+ return [toSingleton(left) % r];
299
+ },
300
+ type: promoteNumericType(args[0]?.type, args[1]?.type),
301
+ isSingleton: true,
302
+ source: `${args[0]?.source || ''} mod ${args[1]?.source || ''}`
303
+ })
304
+ };
305
+
306
+ export const divOperator: Operator = {
307
+ name: 'div',
308
+ kind: 'operator',
309
+
310
+ syntax: {
311
+ form: 'infix',
312
+ token: TokenType.DIV,
313
+ precedence: 10, // MULTIPLICATIVE precedence
314
+ associativity: 'left',
315
+ notation: 'a div b'
316
+ },
317
+
318
+ signature: {
319
+ parameters: [
320
+ { name: 'left', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' },
321
+ { name: 'right', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' }
322
+ ],
323
+ output: {
324
+ type: 'Integer', // Integer division
325
+ cardinality: 'singleton'
326
+ },
327
+ propagatesEmpty: true
328
+ },
329
+
330
+ analyze: defaultOperatorAnalyze,
331
+
332
+ evaluate: (interpreter, context, input, left, right) => {
333
+ if (left.length === 0 || right.length === 0) return { value: [], context };
334
+
335
+ const l = toSingleton(left);
336
+ const r = toSingleton(right);
337
+
338
+ if (r === 0) return { value: [], context }; // Division by zero returns empty
339
+
340
+ return { value: [Math.floor(l / r)], context };
341
+ },
342
+
343
+ compile: (compiler, input, args) => ({
344
+ fn: (ctx) => {
345
+ const left = args[0]?.fn(ctx) || [];
346
+ const right = args[1]?.fn(ctx) || [];
347
+ if (left.length === 0 || right.length === 0) return [];
348
+
349
+ const r = toSingleton(right);
350
+ if (r === 0) return [];
351
+
352
+ return [Math.floor(toSingleton(left) / r)];
353
+ },
354
+ type: compiler.resolveType('Integer'),
355
+ isSingleton: true,
356
+ source: `${args[0]?.source || ''} div ${args[1]?.source || ''}`
357
+ })
358
+ };
359
+
360
+ export const unaryPlusOperator: Operator = {
361
+ name: 'unary+',
362
+ kind: 'operator',
363
+
364
+ syntax: {
365
+ form: 'prefix',
366
+ token: TokenType.PLUS,
367
+ precedence: 11, // UNARY precedence
368
+ notation: '+a'
369
+ },
370
+
371
+ signature: {
372
+ parameters: [
373
+ { name: 'operand', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' }
374
+ ],
375
+ output: {
376
+ type: 'preserve-input',
377
+ cardinality: 'singleton'
378
+ },
379
+ propagatesEmpty: true
380
+ },
381
+
382
+ analyze: defaultOperatorAnalyze,
383
+
384
+ evaluate: (interpreter, context, input, operand) => {
385
+ if (operand.length === 0) return { value: [], context };
386
+ return { value: operand, context }; // Unary plus is identity
387
+ },
388
+
389
+ compile: (compiler, input, args) => ({
390
+ fn: (ctx) => args[0]?.fn(ctx) || [],
391
+ type: args[0]?.type,
392
+ isSingleton: true,
393
+ source: `+${args[0]?.source || ''}`
394
+ })
395
+ };
396
+
397
+ export const unaryMinusOperator: Operator = {
398
+ name: 'unary-',
399
+ kind: 'operator',
400
+
401
+ syntax: {
402
+ form: 'prefix',
403
+ token: TokenType.MINUS,
404
+ precedence: 11, // UNARY precedence
405
+ notation: '-a'
406
+ },
407
+
408
+ signature: {
409
+ parameters: [
410
+ { name: 'operand', types: { kind: 'union', types: ['Integer', 'Decimal'] }, cardinality: 'singleton' }
411
+ ],
412
+ output: {
413
+ type: 'preserve-input',
414
+ cardinality: 'singleton'
415
+ },
416
+ propagatesEmpty: true
417
+ },
418
+
419
+ analyze: defaultOperatorAnalyze,
420
+
421
+ evaluate: (interpreter, context, input, operand) => {
422
+ if (operand.length === 0) return { value: [], context };
423
+
424
+ const val = toSingleton(operand);
425
+ return { value: [-val], context };
426
+ },
427
+
428
+ compile: (compiler, input, args) => ({
429
+ fn: (ctx) => {
430
+ const operand = args[0]?.fn(ctx) || [];
431
+ if (operand.length === 0) return [];
432
+ return [-toSingleton(operand)];
433
+ },
434
+ type: args[0]?.type,
435
+ isSingleton: true,
436
+ source: `-${args[0]?.source || ''}`
437
+ })
438
+ };
439
+
440
+ export const concatOperator: Operator = {
441
+ name: '&',
442
+ kind: 'operator',
443
+
444
+ syntax: {
445
+ form: 'infix',
446
+ token: TokenType.CONCAT,
447
+ precedence: 9, // ADDITIVE precedence
448
+ associativity: 'left',
449
+ notation: 'a & b'
450
+ },
451
+
452
+ signature: {
453
+ parameters: [
454
+ { name: 'left', types: { kind: 'primitive', types: ['String'] }, cardinality: 'singleton' },
455
+ { name: 'right', types: { kind: 'primitive', types: ['String'] }, cardinality: 'singleton' }
456
+ ],
457
+ output: {
458
+ type: 'String',
459
+ cardinality: 'singleton'
460
+ },
461
+ propagatesEmpty: true
462
+ },
463
+
464
+ analyze: defaultOperatorAnalyze,
465
+
466
+ evaluate: (interpreter, context, input, left, right) => {
467
+ if (left.length === 0 || right.length === 0) return { value: [], context };
468
+
469
+ // String concatenation requires both operands to be singletons
470
+ if (left.length > 1 || right.length > 1) return { value: [], context };
471
+
472
+ const l = left[0];
473
+ const r = right[0];
474
+
475
+ return { value: [String(l) + String(r)], context };
476
+ },
477
+
478
+ compile: (compiler, input, args) => ({
479
+ fn: (ctx) => {
480
+ const left = args[0]?.fn(ctx) || [];
481
+ const right = args[1]?.fn(ctx) || [];
482
+ if (left.length === 0 || right.length === 0) return [];
483
+
484
+ // String concatenation requires both operands to be singletons
485
+ if (left.length > 1 || right.length > 1) return [];
486
+
487
+ return [String(left[0]) + String(right[0])];
488
+ },
489
+ type: compiler.resolveType('String'),
490
+ isSingleton: true,
491
+ source: `${args[0]?.source || ''} & ${args[1]?.source || ''}`
492
+ })
493
+ };
494
+
495
+ // Export all arithmetic operators
496
+ export const arithmeticOperators = [
497
+ plusOperator,
498
+ minusOperator,
499
+ multiplyOperator,
500
+ divideOperator,
501
+ modOperator,
502
+ divOperator,
503
+ concatOperator,
504
+ unaryPlusOperator,
505
+ unaryMinusOperator
506
+ ];