@atomic-ehr/fhirpath 0.0.1-canary.69eb286.20250724163205

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