@atomic-ehr/fhirpath 0.0.1-canary.1825db0.20250725140030

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 (59) hide show
  1. package/README.md +400 -0
  2. package/dist/index.d.ts +398 -0
  3. package/dist/index.js +8372 -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 +155 -0
  12. package/src/api/errors.ts +134 -0
  13. package/src/api/expression.ts +156 -0
  14. package/src/api/index.ts +70 -0
  15. package/src/api/inspect.ts +96 -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 +76 -0
  23. package/src/interpreter/README.md +78 -0
  24. package/src/interpreter/interpreter.ts +463 -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/parser/ast.ts +123 -0
  32. package/src/parser/index.ts +3 -0
  33. package/src/parser/parser.ts +701 -0
  34. package/src/parser/pprint.ts +169 -0
  35. package/src/registry/default-analyzers.ts +257 -0
  36. package/src/registry/default-compilers.ts +31 -0
  37. package/src/registry/index.ts +94 -0
  38. package/src/registry/operations/arithmetic.ts +506 -0
  39. package/src/registry/operations/collection.ts +425 -0
  40. package/src/registry/operations/comparison.ts +432 -0
  41. package/src/registry/operations/existence.ts +703 -0
  42. package/src/registry/operations/filtering.ts +358 -0
  43. package/src/registry/operations/literals.ts +341 -0
  44. package/src/registry/operations/logical.ts +439 -0
  45. package/src/registry/operations/math.ts +128 -0
  46. package/src/registry/operations/membership.ts +132 -0
  47. package/src/registry/operations/string.ts +507 -0
  48. package/src/registry/operations/subsetting.ts +174 -0
  49. package/src/registry/operations/type-checking.ts +162 -0
  50. package/src/registry/operations/type-conversion.ts +404 -0
  51. package/src/registry/operations/type-operators.ts +308 -0
  52. package/src/registry/operations/utility.ts +644 -0
  53. package/src/registry/registry.ts +146 -0
  54. package/src/registry/types.ts +161 -0
  55. package/src/registry/utils/evaluation-helpers.ts +93 -0
  56. package/src/registry/utils/index.ts +3 -0
  57. package/src/registry/utils/type-system.ts +173 -0
  58. package/src/runtime/context.ts +158 -0
  59. package/src/runtime/debug-context.ts +135 -0
@@ -0,0 +1,425 @@
1
+ import type { Function, Operator } from '../types';
2
+ import { defaultFunctionAnalyze, defaultOperatorAnalyze } from '../default-analyzers';
3
+ import { defaultFunctionCompile, defaultOperatorCompile } from '../default-compilers';
4
+ import { TokenType } from '../../lexer/token';
5
+ import { RuntimeContextManager } from '../../runtime/context';
6
+
7
+ export const unionFunction: Function = {
8
+ name: 'union',
9
+ kind: 'function',
10
+
11
+ syntax: {
12
+ notation: 'union(other)'
13
+ },
14
+
15
+ signature: {
16
+ input: {
17
+ types: { kind: 'any' },
18
+ cardinality: 'any'
19
+ },
20
+ parameters: [
21
+ {
22
+ name: 'other',
23
+ kind: 'expression',
24
+ types: { kind: 'any' },
25
+ cardinality: 'any',
26
+ optional: false
27
+ }
28
+ ],
29
+ output: {
30
+ type: 'preserve-input',
31
+ cardinality: 'collection'
32
+ },
33
+ propagatesEmpty: false,
34
+ deterministic: true
35
+ },
36
+
37
+ analyze: defaultFunctionAnalyze,
38
+
39
+ evaluate: (interpreter, context, input, otherExpr) => {
40
+ const otherResult = interpreter.evaluate(otherExpr, input, context);
41
+ const other = otherResult.value;
42
+
43
+ const seen = new Set();
44
+ const result: any[] = [];
45
+
46
+ for (const item of input) {
47
+ const key = JSON.stringify(item);
48
+ if (!seen.has(key)) {
49
+ seen.add(key);
50
+ result.push(item);
51
+ }
52
+ }
53
+
54
+ for (const item of other) {
55
+ const key = JSON.stringify(item);
56
+ if (!seen.has(key)) {
57
+ seen.add(key);
58
+ result.push(item);
59
+ }
60
+ }
61
+
62
+ return { value: result, context: otherResult.context };
63
+ },
64
+
65
+ compile: (compiler, input, args) => {
66
+ const otherExpr = args[0];
67
+ if (!otherExpr) {
68
+ throw new Error('union() requires an argument');
69
+ }
70
+
71
+ return {
72
+ fn: (ctx) => {
73
+ const inputVal = input.fn(ctx);
74
+ const otherVal = otherExpr.fn(ctx);
75
+
76
+ const seen = new Set();
77
+ const result: any[] = [];
78
+
79
+ for (const item of inputVal) {
80
+ const key = JSON.stringify(item);
81
+ if (!seen.has(key)) {
82
+ seen.add(key);
83
+ result.push(item);
84
+ }
85
+ }
86
+
87
+ for (const item of otherVal) {
88
+ const key = JSON.stringify(item);
89
+ if (!seen.has(key)) {
90
+ seen.add(key);
91
+ result.push(item);
92
+ }
93
+ }
94
+
95
+ return result;
96
+ },
97
+ type: input.type,
98
+ isSingleton: false,
99
+ source: `${input.source || ''}.union(${otherExpr.source || ''})`
100
+ };
101
+ }
102
+ };
103
+
104
+ export const combineFunction: Function = {
105
+ name: 'combine',
106
+ kind: 'function',
107
+
108
+ syntax: {
109
+ notation: 'combine(other)'
110
+ },
111
+
112
+ signature: {
113
+ input: {
114
+ types: { kind: 'any' },
115
+ cardinality: 'any'
116
+ },
117
+ parameters: [
118
+ {
119
+ name: 'other',
120
+ kind: 'expression',
121
+ types: { kind: 'any' },
122
+ cardinality: 'any',
123
+ optional: false
124
+ }
125
+ ],
126
+ output: {
127
+ type: 'preserve-input',
128
+ cardinality: 'collection'
129
+ },
130
+ propagatesEmpty: false,
131
+ deterministic: true
132
+ },
133
+
134
+ analyze: defaultFunctionAnalyze,
135
+
136
+ evaluate: (interpreter, context, input, otherExpr) => {
137
+ const otherResult = interpreter.evaluate(otherExpr, input, context);
138
+ const other = otherResult.value;
139
+
140
+ return { value: [...input, ...other], context: otherResult.context };
141
+ },
142
+
143
+ compile: (compiler, input, args) => {
144
+ const otherExpr = args[0];
145
+ if (!otherExpr) {
146
+ throw new Error('combine() requires an argument');
147
+ }
148
+
149
+ return {
150
+ fn: (ctx) => {
151
+ const inputVal = input.fn(ctx);
152
+ const otherVal = otherExpr.fn(ctx);
153
+ return [...inputVal, ...otherVal];
154
+ },
155
+ type: input.type,
156
+ isSingleton: false,
157
+ source: `${input.source || ''}.combine(${otherExpr.source || ''})`
158
+ };
159
+ }
160
+ };
161
+
162
+ export const intersectFunction: Function = {
163
+ name: 'intersect',
164
+ kind: 'function',
165
+
166
+ syntax: {
167
+ notation: 'intersect(other)'
168
+ },
169
+
170
+ signature: {
171
+ input: {
172
+ types: { kind: 'any' },
173
+ cardinality: 'any'
174
+ },
175
+ parameters: [
176
+ {
177
+ name: 'other',
178
+ kind: 'expression',
179
+ types: { kind: 'any' },
180
+ cardinality: 'any',
181
+ optional: false
182
+ }
183
+ ],
184
+ output: {
185
+ type: 'preserve-input',
186
+ cardinality: 'collection'
187
+ },
188
+ propagatesEmpty: true,
189
+ deterministic: true
190
+ },
191
+
192
+ analyze: defaultFunctionAnalyze,
193
+
194
+ evaluate: (interpreter, context, input, otherExpr) => {
195
+ const otherResult = interpreter.evaluate(otherExpr, input, context);
196
+ const other = otherResult.value;
197
+
198
+ const otherSet = new Set(other.map((item: any) => JSON.stringify(item)));
199
+ const result: any[] = [];
200
+ const seen = new Set();
201
+
202
+ for (const item of input) {
203
+ const key = JSON.stringify(item);
204
+ if (otherSet.has(key) && !seen.has(key)) {
205
+ seen.add(key);
206
+ result.push(item);
207
+ }
208
+ }
209
+
210
+ return { value: result, context: otherResult.context };
211
+ },
212
+
213
+ compile: (compiler, input, args) => {
214
+ const otherExpr = args[0];
215
+ if (!otherExpr) {
216
+ throw new Error('intersect() requires an argument');
217
+ }
218
+
219
+ return {
220
+ fn: (ctx) => {
221
+ const inputVal = input.fn(ctx);
222
+ const otherVal = otherExpr.fn(ctx);
223
+
224
+ const otherSet = new Set(otherVal.map((item: any) => JSON.stringify(item)));
225
+ const result: any[] = [];
226
+ const seen = new Set();
227
+
228
+ for (const item of inputVal) {
229
+ const key = JSON.stringify(item);
230
+ if (otherSet.has(key) && !seen.has(key)) {
231
+ seen.add(key);
232
+ result.push(item);
233
+ }
234
+ }
235
+
236
+ return result;
237
+ },
238
+ type: input.type,
239
+ isSingleton: false,
240
+ source: `${input.source || ''}.intersect(${otherExpr.source || ''})`
241
+ };
242
+ }
243
+ };
244
+
245
+ export const excludeFunction: Function = {
246
+ name: 'exclude',
247
+ kind: 'function',
248
+
249
+ syntax: {
250
+ notation: 'exclude(other)'
251
+ },
252
+
253
+ signature: {
254
+ input: {
255
+ types: { kind: 'any' },
256
+ cardinality: 'any'
257
+ },
258
+ parameters: [
259
+ {
260
+ name: 'other',
261
+ kind: 'expression',
262
+ types: { kind: 'any' },
263
+ cardinality: 'any',
264
+ optional: false
265
+ }
266
+ ],
267
+ output: {
268
+ type: 'preserve-input',
269
+ cardinality: 'collection'
270
+ },
271
+ propagatesEmpty: true,
272
+ deterministic: true
273
+ },
274
+
275
+ analyze: defaultFunctionAnalyze,
276
+
277
+ evaluate: (interpreter, context, input, otherExpr) => {
278
+ const otherResult = interpreter.evaluate(otherExpr, input, context);
279
+ const other = otherResult.value;
280
+
281
+ const excludeSet = new Set(other.map((item: any) => JSON.stringify(item)));
282
+ const result: any[] = [];
283
+ const seen = new Set();
284
+
285
+ for (const item of input) {
286
+ const key = JSON.stringify(item);
287
+ if (!excludeSet.has(key) && !seen.has(key)) {
288
+ seen.add(key);
289
+ result.push(item);
290
+ }
291
+ }
292
+
293
+ return { value: result, context: otherResult.context };
294
+ },
295
+
296
+ compile: (compiler, input, args) => {
297
+ const otherExpr = args[0];
298
+ if (!otherExpr) {
299
+ throw new Error('exclude() requires an argument');
300
+ }
301
+
302
+ return {
303
+ fn: (ctx) => {
304
+ const inputVal = input.fn(ctx);
305
+ const otherVal = otherExpr.fn(ctx);
306
+
307
+ const excludeSet = new Set(otherVal.map((item: any) => JSON.stringify(item)));
308
+ const result: any[] = [];
309
+ const seen = new Set();
310
+
311
+ for (const item of inputVal) {
312
+ const key = JSON.stringify(item);
313
+ if (!excludeSet.has(key) && !seen.has(key)) {
314
+ seen.add(key);
315
+ result.push(item);
316
+ }
317
+ }
318
+
319
+ return result;
320
+ },
321
+ type: input.type,
322
+ isSingleton: false,
323
+ source: `${input.source || ''}.exclude(${otherExpr.source || ''})`
324
+ };
325
+ }
326
+ };
327
+
328
+ // Union operator (|) - combines collections removing duplicates
329
+ export const unionOperator: Operator = {
330
+ name: '|',
331
+ kind: 'operator',
332
+
333
+ syntax: {
334
+ form: 'infix',
335
+ token: TokenType.PIPE,
336
+ precedence: 13, // Lower precedence than most operators
337
+ associativity: 'left',
338
+ notation: 'a | b'
339
+ },
340
+
341
+ signature: {
342
+ parameters: [
343
+ { name: 'left' },
344
+ { name: 'right' }
345
+ ],
346
+ output: {
347
+ type: 'preserve-left',
348
+ cardinality: 'collection'
349
+ },
350
+ propagatesEmpty: false
351
+ },
352
+
353
+ analyze: defaultOperatorAnalyze,
354
+
355
+ evaluate: (interpreter, context, input, left, right) => {
356
+ // Union removes duplicates
357
+ const seen = new Set();
358
+ const result: any[] = [];
359
+
360
+ for (const item of left) {
361
+ const key = JSON.stringify(item);
362
+ if (!seen.has(key)) {
363
+ seen.add(key);
364
+ result.push(item);
365
+ }
366
+ }
367
+
368
+ for (const item of right) {
369
+ const key = JSON.stringify(item);
370
+ if (!seen.has(key)) {
371
+ seen.add(key);
372
+ result.push(item);
373
+ }
374
+ }
375
+
376
+ return { value: result, context };
377
+ },
378
+
379
+ compile: (compiler, input, args) => {
380
+ const [left, right] = args;
381
+ if (!left || !right) {
382
+ throw new Error('Union operator requires two operands');
383
+ }
384
+ return {
385
+ fn: (ctx) => {
386
+ // Evaluate both sides with SEPARATE context copies
387
+ // This prevents variable definitions from leaking between branches
388
+ const leftCtx = RuntimeContextManager.copy(ctx);
389
+ const rightCtx = RuntimeContextManager.copy(ctx);
390
+ const leftVal = left.fn(leftCtx);
391
+ const rightVal = right.fn(rightCtx);
392
+
393
+ // Union removes duplicates
394
+ const seen = new Set();
395
+ const result: any[] = [];
396
+
397
+ for (const item of leftVal) {
398
+ const key = JSON.stringify(item);
399
+ if (!seen.has(key)) {
400
+ seen.add(key);
401
+ result.push(item);
402
+ }
403
+ }
404
+
405
+ for (const item of rightVal) {
406
+ const key = JSON.stringify(item);
407
+ if (!seen.has(key)) {
408
+ seen.add(key);
409
+ result.push(item);
410
+ }
411
+ }
412
+
413
+ return result;
414
+ },
415
+ type: left.type,
416
+ isSingleton: false,
417
+ source: `${left.source || ''} | ${right.source || ''}`
418
+ };
419
+ }
420
+ };
421
+
422
+ // Export all collection operations
423
+ export const collectionOperators = [
424
+ unionOperator
425
+ ];