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