@atomic-ehr/fhirpath 0.0.1-canary.35b105d.20250724165800

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 (57) hide show
  1. package/README.md +307 -0
  2. package/dist/index.d.ts +225 -0
  3. package/dist/index.js +8185 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +51 -0
  6. package/src/analyzer/analyzer.ts +486 -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 +148 -0
  12. package/src/api/errors.ts +134 -0
  13. package/src/api/expression.ts +152 -0
  14. package/src/api/index.ts +57 -0
  15. package/src/api/registry.ts +128 -0
  16. package/src/api/types.ts +154 -0
  17. package/src/compiler/compiler.ts +579 -0
  18. package/src/compiler/index.ts +2 -0
  19. package/src/compiler/prototype-context-adapter.ts +99 -0
  20. package/src/compiler/types.ts +23 -0
  21. package/src/index.ts +52 -0
  22. package/src/interpreter/README.md +78 -0
  23. package/src/interpreter/interpreter.ts +485 -0
  24. package/src/interpreter/types.ts +110 -0
  25. package/src/lexer/char-tables.ts +37 -0
  26. package/src/lexer/errors.ts +31 -0
  27. package/src/lexer/index.ts +5 -0
  28. package/src/lexer/lexer.ts +745 -0
  29. package/src/lexer/token.ts +104 -0
  30. package/src/parser/ast.ts +123 -0
  31. package/src/parser/index.ts +3 -0
  32. package/src/parser/parser.ts +701 -0
  33. package/src/parser/pprint.ts +169 -0
  34. package/src/registry/default-analyzers.ts +257 -0
  35. package/src/registry/default-compilers.ts +31 -0
  36. package/src/registry/index.ts +93 -0
  37. package/src/registry/operations/arithmetic.ts +506 -0
  38. package/src/registry/operations/collection.ts +425 -0
  39. package/src/registry/operations/comparison.ts +432 -0
  40. package/src/registry/operations/existence.ts +703 -0
  41. package/src/registry/operations/filtering.ts +358 -0
  42. package/src/registry/operations/literals.ts +341 -0
  43. package/src/registry/operations/logical.ts +402 -0
  44. package/src/registry/operations/math.ts +128 -0
  45. package/src/registry/operations/membership.ts +132 -0
  46. package/src/registry/operations/string.ts +507 -0
  47. package/src/registry/operations/subsetting.ts +174 -0
  48. package/src/registry/operations/type-checking.ts +162 -0
  49. package/src/registry/operations/type-conversion.ts +404 -0
  50. package/src/registry/operations/type-operators.ts +307 -0
  51. package/src/registry/operations/utility.ts +542 -0
  52. package/src/registry/registry.ts +146 -0
  53. package/src/registry/types.ts +161 -0
  54. package/src/registry/utils/evaluation-helpers.ts +93 -0
  55. package/src/registry/utils/index.ts +3 -0
  56. package/src/registry/utils/type-system.ts +173 -0
  57. package/src/runtime/context.ts +179 -0
@@ -0,0 +1,703 @@
1
+ import type { Function } from '../types';
2
+ import { defaultFunctionAnalyze } from '../default-analyzers';
3
+ import { isTruthy, toSingleton } from '../utils';
4
+ import { RuntimeContextManager } from '../../runtime/context';
5
+
6
+ export const existsFunction: Function = {
7
+ name: 'exists',
8
+ kind: 'function',
9
+
10
+ syntax: {
11
+ notation: 'exists([criteria])'
12
+ },
13
+
14
+ signature: {
15
+ input: {
16
+ types: { kind: 'any' },
17
+ cardinality: 'any'
18
+ },
19
+ parameters: [
20
+ {
21
+ name: 'criteria',
22
+ kind: 'expression',
23
+ types: { kind: 'any' },
24
+ cardinality: 'any',
25
+ optional: true
26
+ }
27
+ ],
28
+ output: {
29
+ type: 'Boolean',
30
+ cardinality: 'singleton'
31
+ },
32
+ propagatesEmpty: false, // exists() on empty returns false
33
+ deterministic: true
34
+ },
35
+
36
+ analyze: defaultFunctionAnalyze,
37
+
38
+ evaluate: (interpreter, context, input, ...args) => {
39
+ const criteria = args[0];
40
+ if (!criteria) {
41
+ // No criteria: check if input is non-empty
42
+ return { value: [input.length > 0], context };
43
+ }
44
+
45
+ // With criteria: check if any element matches
46
+ for (let i = 0; i < input.length; i++) {
47
+ const item = input[i];
48
+ const iterContext = RuntimeContextManager.withIterator(context, item, i);
49
+ const result = interpreter.evaluate(criteria, [item], iterContext);
50
+ if (result.value.length > 0 && toSingleton(result.value)) {
51
+ return { value: [true], context: result.context };
52
+ }
53
+ }
54
+
55
+ return { value: [false], context };
56
+ },
57
+
58
+ compile: (compiler, input, args) => {
59
+ if (args.length === 0) {
60
+ // No criteria
61
+ return {
62
+ fn: (ctx) => {
63
+ const inputVal = input.fn(ctx);
64
+ return [inputVal.length > 0];
65
+ },
66
+ type: compiler.resolveType('Boolean'),
67
+ isSingleton: true,
68
+ source: `${input.source || ''}.exists()`
69
+ };
70
+ }
71
+
72
+ // With criteria
73
+ const criteria = args[0];
74
+ return {
75
+ fn: (ctx) => {
76
+ const inputVal = input.fn(ctx);
77
+ for (let i = 0; i < inputVal.length; i++) {
78
+ const item = inputVal[i];
79
+ const newCtx = RuntimeContextManager.withIterator(ctx, item, i);
80
+ const result = criteria?.fn(newCtx) || [];
81
+ if (result.length > 0 && toSingleton(result)) {
82
+ return [true];
83
+ }
84
+ }
85
+ return [false];
86
+ },
87
+ type: compiler.resolveType('Boolean'),
88
+ isSingleton: true,
89
+ source: `${input.source || ''}.exists(${criteria?.source || ''})`
90
+ };
91
+ }
92
+ };
93
+
94
+ export const emptyFunction: Function = {
95
+ name: 'empty',
96
+ kind: 'function',
97
+
98
+ syntax: {
99
+ notation: 'empty()'
100
+ },
101
+
102
+ signature: {
103
+ input: {
104
+ types: { kind: 'any' },
105
+ cardinality: 'any'
106
+ },
107
+ parameters: [],
108
+ output: {
109
+ type: 'Boolean',
110
+ cardinality: 'singleton'
111
+ },
112
+ propagatesEmpty: false, // empty() on empty returns true
113
+ deterministic: true
114
+ },
115
+
116
+ analyze: defaultFunctionAnalyze,
117
+
118
+ evaluate: (interpreter, context, input) => {
119
+ return { value: [input.length === 0], context };
120
+ },
121
+
122
+ compile: (compiler, input, args) => ({
123
+ fn: (ctx) => {
124
+ const inputVal = input.fn(ctx);
125
+ return [inputVal.length === 0];
126
+ },
127
+ type: compiler.resolveType('Boolean'),
128
+ isSingleton: true,
129
+ source: `${input.source || ''}.empty()`
130
+ })
131
+ };
132
+
133
+ export const countFunction: Function = {
134
+ name: 'count',
135
+ kind: 'function',
136
+
137
+ syntax: {
138
+ notation: 'count()'
139
+ },
140
+
141
+ signature: {
142
+ input: {
143
+ types: { kind: 'any' },
144
+ cardinality: 'any'
145
+ },
146
+ parameters: [],
147
+ output: {
148
+ type: 'Integer',
149
+ cardinality: 'singleton'
150
+ },
151
+ propagatesEmpty: false, // count() on empty returns 0
152
+ deterministic: true
153
+ },
154
+
155
+ analyze: defaultFunctionAnalyze,
156
+
157
+ evaluate: (interpreter, context, input) => {
158
+ return { value: [input.length], context };
159
+ },
160
+
161
+ compile: (compiler, input, args) => ({
162
+ fn: (ctx) => {
163
+ const inputVal = input.fn(ctx);
164
+ return [inputVal.length];
165
+ },
166
+ type: compiler.resolveType('Integer'),
167
+ isSingleton: true,
168
+ source: `${input.source || ''}.count()`
169
+ })
170
+ };
171
+
172
+ export const firstFunction: Function = {
173
+ name: 'first',
174
+ kind: 'function',
175
+
176
+ syntax: {
177
+ notation: 'first()'
178
+ },
179
+
180
+ signature: {
181
+ input: {
182
+ types: { kind: 'any' },
183
+ cardinality: 'any'
184
+ },
185
+ parameters: [],
186
+ output: {
187
+ type: 'preserve-input',
188
+ cardinality: 'singleton'
189
+ },
190
+ propagatesEmpty: true,
191
+ deterministic: true
192
+ },
193
+
194
+ analyze: defaultFunctionAnalyze,
195
+
196
+ evaluate: (interpreter, context, input) => {
197
+ if (input.length === 0) return { value: [], context };
198
+ return { value: [input[0]], context };
199
+ },
200
+
201
+ compile: (compiler, input, args) => ({
202
+ fn: (ctx) => {
203
+ const inputVal = input.fn(ctx);
204
+ if (inputVal.length === 0) return [];
205
+ return [inputVal[0]];
206
+ },
207
+ type: input.type,
208
+ isSingleton: true,
209
+ source: `${input.source || ''}.first()`
210
+ })
211
+ };
212
+
213
+ export const lastFunction: Function = {
214
+ name: 'last',
215
+ kind: 'function',
216
+
217
+ syntax: {
218
+ notation: 'last()'
219
+ },
220
+
221
+ signature: {
222
+ input: {
223
+ types: { kind: 'any' },
224
+ cardinality: 'any'
225
+ },
226
+ parameters: [],
227
+ output: {
228
+ type: 'preserve-input',
229
+ cardinality: 'singleton'
230
+ },
231
+ propagatesEmpty: true,
232
+ deterministic: true
233
+ },
234
+
235
+ analyze: defaultFunctionAnalyze,
236
+
237
+ evaluate: (interpreter, context, input) => {
238
+ if (input.length === 0) return { value: [], context };
239
+ return { value: [input[input.length - 1]], context };
240
+ },
241
+
242
+ compile: (compiler, input, args) => ({
243
+ fn: (ctx) => {
244
+ const inputVal = input.fn(ctx);
245
+ if (inputVal.length === 0) return [];
246
+ return [inputVal[inputVal.length - 1]];
247
+ },
248
+ type: input.type,
249
+ isSingleton: true,
250
+ source: `${input.source || ''}.last()`
251
+ })
252
+ };
253
+
254
+ export const singleFunction: Function = {
255
+ name: 'single',
256
+ kind: 'function',
257
+
258
+ syntax: {
259
+ notation: 'single()'
260
+ },
261
+
262
+ signature: {
263
+ input: {
264
+ types: { kind: 'any' },
265
+ cardinality: 'any'
266
+ },
267
+ parameters: [],
268
+ output: {
269
+ type: 'preserve-input',
270
+ cardinality: 'singleton'
271
+ },
272
+ propagatesEmpty: true,
273
+ deterministic: true
274
+ },
275
+
276
+ analyze: defaultFunctionAnalyze,
277
+
278
+ evaluate: (interpreter, context, input) => {
279
+ if (input.length === 0) return { value: [], context };
280
+ if (input.length !== 1) {
281
+ throw new Error('single() requires collection to have exactly one item');
282
+ }
283
+ return { value: input, context };
284
+ },
285
+
286
+ compile: (compiler, input, args) => ({
287
+ fn: (ctx) => {
288
+ const inputVal = input.fn(ctx);
289
+ if (inputVal.length === 0) return [];
290
+ if (inputVal.length !== 1) {
291
+ throw new Error('single() requires collection to have exactly one item');
292
+ }
293
+ return inputVal;
294
+ },
295
+ type: input.type,
296
+ isSingleton: true,
297
+ source: `${input.source || ''}.single()`
298
+ })
299
+ };
300
+
301
+ export const allFunction: Function = {
302
+ name: 'all',
303
+ kind: 'function',
304
+
305
+ syntax: {
306
+ notation: 'all([criteria])'
307
+ },
308
+
309
+ signature: {
310
+ input: {
311
+ types: { kind: 'any' },
312
+ cardinality: 'any'
313
+ },
314
+ parameters: [
315
+ {
316
+ name: 'criteria',
317
+ kind: 'expression',
318
+ types: { kind: 'any' },
319
+ cardinality: 'any',
320
+ optional: true
321
+ }
322
+ ],
323
+ output: {
324
+ type: 'Boolean',
325
+ cardinality: 'singleton'
326
+ },
327
+ propagatesEmpty: false,
328
+ deterministic: true
329
+ },
330
+
331
+ analyze: defaultFunctionAnalyze,
332
+
333
+ evaluate: (interpreter, context, input, criteria) => {
334
+ if (input.length === 0) {
335
+ return { value: [true], context };
336
+ }
337
+
338
+ if (!criteria) {
339
+ return { value: [input.every(item => item === true)], context };
340
+ }
341
+
342
+ for (let i = 0; i < input.length; i++) {
343
+ const item = input[i];
344
+ const iterContext = RuntimeContextManager.withIterator(context, item, i);
345
+ const result = interpreter.evaluate(criteria, [item], iterContext);
346
+
347
+ if (!isTruthy(result.value)) {
348
+ return { value: [false], context };
349
+ }
350
+ }
351
+
352
+ return { value: [true], context };
353
+ },
354
+
355
+ compile: (compiler, input, args) => {
356
+ if (args.length === 0 || !args[0]) {
357
+ // No criteria - check if all items are true
358
+ return {
359
+ fn: (ctx) => {
360
+ const inputVal = input.fn(ctx);
361
+ if (inputVal.length === 0) {
362
+ return [true];
363
+ }
364
+ return [inputVal.every(item => item === true)];
365
+ },
366
+ type: compiler.resolveType('Boolean'),
367
+ isSingleton: true,
368
+ source: `${input.source || ''}.all()`
369
+ };
370
+ }
371
+
372
+ // With criteria
373
+ const criteria = args[0];
374
+ return {
375
+ fn: (ctx) => {
376
+ const inputVal = input.fn(ctx);
377
+ if (inputVal.length === 0) {
378
+ return [true];
379
+ }
380
+
381
+ for (let i = 0; i < inputVal.length; i++) {
382
+ const item = inputVal[i];
383
+ const newCtx = RuntimeContextManager.withIterator(ctx, item, i);
384
+ const result = criteria?.fn(newCtx) || [];
385
+
386
+ if (!isTruthy(result)) {
387
+ return [false];
388
+ }
389
+ }
390
+
391
+ return [true];
392
+ },
393
+ type: compiler.resolveType('Boolean'),
394
+ isSingleton: true,
395
+ source: `${input.source || ''}.all(${criteria.source || ''})`
396
+ };
397
+ }
398
+ };
399
+
400
+ export const allTrueFunction: Function = {
401
+ name: 'allTrue',
402
+ kind: 'function',
403
+
404
+ syntax: {
405
+ notation: 'allTrue()'
406
+ },
407
+
408
+ signature: {
409
+ input: {
410
+ types: { kind: 'primitive', types: ['Boolean'] },
411
+ cardinality: 'any'
412
+ },
413
+ parameters: [],
414
+ output: {
415
+ type: 'Boolean',
416
+ cardinality: 'singleton'
417
+ },
418
+ propagatesEmpty: false,
419
+ deterministic: true
420
+ },
421
+
422
+ analyze: defaultFunctionAnalyze,
423
+
424
+ evaluate: (interpreter, context, input) => {
425
+ return { value: [input.every(item => item === true)], context };
426
+ },
427
+
428
+ compile: (compiler, input, args) => {
429
+ return {
430
+ fn: (ctx) => {
431
+ const inputVal = input.fn(ctx);
432
+ return [inputVal.every(item => item === true)];
433
+ },
434
+ type: compiler.resolveType('Boolean'),
435
+ isSingleton: true,
436
+ source: `${input.source || ''}.allTrue()`
437
+ };
438
+ }
439
+ };
440
+
441
+ export const anyTrueFunction: Function = {
442
+ name: 'anyTrue',
443
+ kind: 'function',
444
+
445
+ syntax: {
446
+ notation: 'anyTrue()'
447
+ },
448
+
449
+ signature: {
450
+ input: {
451
+ types: { kind: 'primitive', types: ['Boolean'] },
452
+ cardinality: 'any'
453
+ },
454
+ parameters: [],
455
+ output: {
456
+ type: 'Boolean',
457
+ cardinality: 'singleton'
458
+ },
459
+ propagatesEmpty: false,
460
+ deterministic: true
461
+ },
462
+
463
+ analyze: defaultFunctionAnalyze,
464
+
465
+ evaluate: (interpreter, context, input) => {
466
+ return { value: [input.some(item => item === true)], context };
467
+ },
468
+
469
+ compile: (compiler, input, args) => {
470
+ return {
471
+ fn: (ctx) => {
472
+ const inputVal = input.fn(ctx);
473
+ return [inputVal.some(item => item === true)];
474
+ },
475
+ type: compiler.resolveType('Boolean'),
476
+ isSingleton: true,
477
+ source: `${input.source || ''}.anyTrue()`
478
+ };
479
+ }
480
+ };
481
+
482
+ export const allFalseFunction: Function = {
483
+ name: 'allFalse',
484
+ kind: 'function',
485
+
486
+ syntax: {
487
+ notation: 'allFalse()'
488
+ },
489
+
490
+ signature: {
491
+ input: {
492
+ types: { kind: 'primitive', types: ['Boolean'] },
493
+ cardinality: 'any'
494
+ },
495
+ parameters: [],
496
+ output: {
497
+ type: 'Boolean',
498
+ cardinality: 'singleton'
499
+ },
500
+ propagatesEmpty: false,
501
+ deterministic: true
502
+ },
503
+
504
+ analyze: defaultFunctionAnalyze,
505
+
506
+ evaluate: (interpreter, context, input) => {
507
+ return { value: [input.every(item => item === false)], context };
508
+ },
509
+
510
+ compile: (compiler, input, args) => {
511
+ return {
512
+ fn: (ctx) => {
513
+ const inputVal = input.fn(ctx);
514
+ return [inputVal.every(item => item === false)];
515
+ },
516
+ type: compiler.resolveType('Boolean'),
517
+ isSingleton: true,
518
+ source: `${input.source || ''}.allFalse()`
519
+ };
520
+ }
521
+ };
522
+
523
+ export const anyFalseFunction: Function = {
524
+ name: 'anyFalse',
525
+ kind: 'function',
526
+
527
+ syntax: {
528
+ notation: 'anyFalse()'
529
+ },
530
+
531
+ signature: {
532
+ input: {
533
+ types: { kind: 'primitive', types: ['Boolean'] },
534
+ cardinality: 'any'
535
+ },
536
+ parameters: [],
537
+ output: {
538
+ type: 'Boolean',
539
+ cardinality: 'singleton'
540
+ },
541
+ propagatesEmpty: false,
542
+ deterministic: true
543
+ },
544
+
545
+ analyze: defaultFunctionAnalyze,
546
+
547
+ evaluate: (interpreter, context, input) => {
548
+ return { value: [input.some(item => item === false)], context };
549
+ },
550
+
551
+ compile: (compiler, input, args) => {
552
+ return {
553
+ fn: (ctx) => {
554
+ const inputVal = input.fn(ctx);
555
+ return [inputVal.some(item => item === false)];
556
+ },
557
+ type: compiler.resolveType('Boolean'),
558
+ isSingleton: true,
559
+ source: `${input.source || ''}.anyFalse()`
560
+ };
561
+ }
562
+ };
563
+
564
+ export const distinctFunction: Function = {
565
+ name: 'distinct',
566
+ kind: 'function',
567
+
568
+ syntax: {
569
+ notation: 'distinct()'
570
+ },
571
+
572
+ signature: {
573
+ input: {
574
+ types: { kind: 'any' },
575
+ cardinality: 'any'
576
+ },
577
+ parameters: [],
578
+ output: {
579
+ type: 'preserve-input',
580
+ cardinality: 'collection'
581
+ },
582
+ propagatesEmpty: true,
583
+ deterministic: true
584
+ },
585
+
586
+ analyze: defaultFunctionAnalyze,
587
+
588
+ evaluate: (interpreter, context, input) => {
589
+ const seen = new Set();
590
+ const result: any[] = [];
591
+
592
+ for (const item of input) {
593
+ const key = JSON.stringify(item);
594
+ if (!seen.has(key)) {
595
+ seen.add(key);
596
+ result.push(item);
597
+ }
598
+ }
599
+
600
+ return { value: result, context };
601
+ },
602
+
603
+ compile: (compiler, input, args) => {
604
+ return {
605
+ fn: (ctx) => {
606
+ const inputVal = input.fn(ctx);
607
+ const seen = new Set();
608
+ const result: any[] = [];
609
+
610
+ for (const item of inputVal) {
611
+ const key = JSON.stringify(item);
612
+ if (!seen.has(key)) {
613
+ seen.add(key);
614
+ result.push(item);
615
+ }
616
+ }
617
+
618
+ return result;
619
+ },
620
+ type: input.type,
621
+ isSingleton: false,
622
+ source: `${input.source || ''}.distinct()`
623
+ };
624
+ }
625
+ };
626
+
627
+ export const isDistinctFunction: Function = {
628
+ name: 'isDistinct',
629
+ kind: 'function',
630
+
631
+ syntax: {
632
+ notation: 'isDistinct()'
633
+ },
634
+
635
+ signature: {
636
+ input: {
637
+ types: { kind: 'any' },
638
+ cardinality: 'any'
639
+ },
640
+ parameters: [],
641
+ output: {
642
+ type: 'Boolean',
643
+ cardinality: 'singleton'
644
+ },
645
+ propagatesEmpty: false,
646
+ deterministic: true
647
+ },
648
+
649
+ analyze: defaultFunctionAnalyze,
650
+
651
+ evaluate: (interpreter, context, input) => {
652
+ const seen = new Set();
653
+
654
+ for (const item of input) {
655
+ const key = JSON.stringify(item);
656
+ if (seen.has(key)) {
657
+ return { value: [false], context };
658
+ }
659
+ seen.add(key);
660
+ }
661
+
662
+ return { value: [true], context };
663
+ },
664
+
665
+ compile: (compiler, input, args) => {
666
+ return {
667
+ fn: (ctx) => {
668
+ const inputVal = input.fn(ctx);
669
+ const seen = new Set();
670
+
671
+ for (const item of inputVal) {
672
+ const key = JSON.stringify(item);
673
+ if (seen.has(key)) {
674
+ return [false];
675
+ }
676
+ seen.add(key);
677
+ }
678
+
679
+ return [true];
680
+ },
681
+ type: compiler.resolveType('Boolean'),
682
+ isSingleton: true,
683
+ source: `${input.source || ''}.isDistinct()`
684
+ };
685
+ }
686
+ };
687
+
688
+ // Export all existence functions
689
+ export const existenceFunctions = [
690
+ existsFunction,
691
+ emptyFunction,
692
+ countFunction,
693
+ firstFunction,
694
+ lastFunction,
695
+ singleFunction,
696
+ allFunction,
697
+ allTrueFunction,
698
+ anyTrueFunction,
699
+ allFalseFunction,
700
+ anyFalseFunction,
701
+ distinctFunction,
702
+ isDistinctFunction
703
+ ];