@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,307 @@
1
+ import { TokenType } from '../../lexer/token';
2
+ import type { Operator } from '../types';
3
+ import type { Analyzer, TypeInfo } from '../types';
4
+ import { EvaluationError } from '../../interpreter/types';
5
+ import type { Context, EvaluationResult } from '../../interpreter/types';
6
+ import type { CompiledExpression } from '../../compiler/types';
7
+
8
+ // Type operators (is, as) need special handling in the parser
9
+ // They are included here for precedence lookup and keyword registration
10
+
11
+ export const isOperator: Operator = {
12
+ name: 'is',
13
+ kind: 'operator',
14
+ syntax: {
15
+ form: 'infix',
16
+ token: TokenType.IS,
17
+ precedence: 6,
18
+ associativity: 'left',
19
+ notation: 'a is Type',
20
+ special: true // Requires special parsing
21
+ },
22
+ signature: {
23
+ parameters: [{ name: 'left' }, { name: 'right' }],
24
+ output: {
25
+ type: 'Boolean',
26
+ cardinality: 'singleton'
27
+ },
28
+ propagatesEmpty: false
29
+ },
30
+ analyze: (analyzer: Analyzer, input: TypeInfo, args: TypeInfo[]): TypeInfo => {
31
+ // Always returns boolean
32
+ return {
33
+ type: analyzer.resolveType('Boolean'),
34
+ isSingleton: input.isSingleton
35
+ };
36
+ },
37
+ evaluate: (interpreter, context, input, leftValue, rightNode) => {
38
+ // For 'is' operator, the right side should be a type name
39
+ // It might come as a TypeOrIdentifierNode or as an array with the type name
40
+ let typeName: string;
41
+
42
+ if (typeof rightNode === 'string') {
43
+ typeName = rightNode;
44
+ } else if (Array.isArray(rightNode) && rightNode.length === 1) {
45
+ typeName = rightNode[0];
46
+ } else if (rightNode && typeof rightNode === 'object' && 'name' in rightNode) {
47
+ // TypeOrIdentifier node
48
+ typeName = rightNode.name;
49
+ } else {
50
+ throw new Error('is operator requires a type name');
51
+ }
52
+
53
+ // Import TypeSystem locally to avoid circular dependency
54
+ const { TypeSystem } = require('../utils/type-system');
55
+
56
+ if (leftValue.length === 0) {
57
+ return { value: [], context };
58
+ }
59
+
60
+ // Check if all values in the collection match the type
61
+ for (const item of leftValue) {
62
+ if (!TypeSystem.isType(item, typeName)) {
63
+ return { value: [false], context };
64
+ }
65
+ }
66
+
67
+ return { value: [true], context };
68
+ },
69
+ compile: (compiler, input, args) => {
70
+ // For operators, input is the left expression and args contains [left, right]
71
+ const leftExpr = input;
72
+ const rightExpr = args?.[1];
73
+
74
+ if (!leftExpr) {
75
+ throw new Error('is operator requires left operand');
76
+ }
77
+
78
+ if (!rightExpr) {
79
+ throw new Error('is operator requires right operand (type name)');
80
+ }
81
+
82
+ // Import TypeSystem locally to avoid circular dependency
83
+ const { TypeSystem } = require('../utils/type-system');
84
+
85
+ // The right side of 'is' should be a type identifier
86
+ // Since type identifiers don't evaluate to values, we need to handle this specially
87
+
88
+ // Try to determine the type name at compile time
89
+ let staticTypeName: string | undefined;
90
+
91
+ // Check various possible structures for the type name
92
+ // Check if it has source that looks like a type name
93
+ if ((rightExpr as any).source && /^[A-Z][a-zA-Z]*$/.test((rightExpr as any).source)) {
94
+ staticTypeName = (rightExpr as any).source;
95
+ }
96
+ // If it evaluates to a constant type name
97
+ else {
98
+ // Try to execute it with empty context to see if it returns a type name
99
+ try {
100
+ const result = rightExpr.fn({ input: [], focus: [], env: {} });
101
+ if (result.length === 1 && typeof result[0] === 'string' && /^[A-Z]/.test(result[0])) {
102
+ staticTypeName = result[0];
103
+ }
104
+ } catch (e) {
105
+ // Ignore errors, fall through to runtime handling
106
+ }
107
+ }
108
+
109
+ if (staticTypeName) {
110
+ // We know the type name at compile time
111
+ return {
112
+ fn: (ctx) => {
113
+ const left = leftExpr.fn(ctx);
114
+
115
+ if (left.length === 0) return [];
116
+
117
+ // Check if all values in the collection match the type
118
+ for (const item of left) {
119
+ if (!TypeSystem.isType(item, staticTypeName)) {
120
+ return [false];
121
+ }
122
+ }
123
+
124
+ return [true];
125
+ },
126
+ type: compiler.resolveType('Boolean'),
127
+ isSingleton: true
128
+ };
129
+ }
130
+
131
+ // Fallback: evaluate type name at runtime
132
+ return {
133
+ fn: (ctx) => {
134
+ const left = leftExpr.fn(ctx);
135
+
136
+ if (left.length === 0) return [];
137
+
138
+ // Try to evaluate the right expression to get the type name
139
+ let typeName: string;
140
+ try {
141
+ const rightResult = rightExpr.fn(ctx);
142
+ if (rightResult.length === 1 && typeof rightResult[0] === 'string') {
143
+ typeName = rightResult[0];
144
+ } else {
145
+ throw new Error('Type name must be a string');
146
+ }
147
+ } catch (e) {
148
+ // If evaluation fails, it might be because TypeOrIdentifier
149
+ // is trying to access a non-existent property
150
+ // In that case, assume the identifier name is the type name
151
+ const source = (rightExpr as any).source;
152
+ if (source && /^[A-Z]/.test(source)) {
153
+ typeName = source;
154
+ } else {
155
+ throw new Error(`Cannot determine type name: ${(e as Error).message}`);
156
+ }
157
+ }
158
+
159
+ // Check if all values in the collection match the type
160
+ for (const item of left) {
161
+ if (!TypeSystem.isType(item, typeName)) {
162
+ return [false];
163
+ }
164
+ }
165
+
166
+ return [true];
167
+ },
168
+ type: compiler.resolveType('Boolean'),
169
+ isSingleton: true
170
+ };
171
+ }
172
+ };
173
+
174
+ export const asOperator: Operator = {
175
+ name: 'as',
176
+ kind: 'operator',
177
+ syntax: {
178
+ form: 'infix',
179
+ token: TokenType.AS,
180
+ precedence: 6,
181
+ associativity: 'left',
182
+ notation: 'a as Type',
183
+ special: true // Requires special parsing
184
+ },
185
+ signature: {
186
+ parameters: [{ name: 'left' }, { name: 'right' }],
187
+ output: {
188
+ type: 'preserve-input',
189
+ cardinality: 'preserve-input'
190
+ },
191
+ propagatesEmpty: true
192
+ },
193
+ analyze: (analyzer: Analyzer, input: TypeInfo, args: TypeInfo[]): TypeInfo => {
194
+ // Returns the target type (determined by parser)
195
+ // This is a placeholder - actual type is set by parser
196
+ return {
197
+ type: analyzer.resolveType('Any'),
198
+ isSingleton: input.isSingleton
199
+ };
200
+ },
201
+ evaluate: (interpreter, context, input, leftValue, rightNode) => {
202
+ // For 'as' operator, the right side should be a type name
203
+ let typeName: string;
204
+
205
+ if (typeof rightNode === 'string') {
206
+ typeName = rightNode;
207
+ } else if (Array.isArray(rightNode) && rightNode.length === 1) {
208
+ typeName = rightNode[0];
209
+ } else if (rightNode && typeof rightNode === 'object' && 'name' in rightNode) {
210
+ // TypeOrIdentifier node
211
+ typeName = rightNode.name;
212
+ } else {
213
+ throw new Error('as operator requires a type name');
214
+ }
215
+
216
+ // Import TypeSystem locally to avoid circular dependency
217
+ const { TypeSystem } = require('../utils/type-system');
218
+
219
+ // Filter values that match the type
220
+ const results: any[] = [];
221
+ for (const item of leftValue) {
222
+ if (TypeSystem.isType(item, typeName)) {
223
+ results.push(item);
224
+ }
225
+ }
226
+
227
+ return { value: results, context };
228
+ },
229
+ compile: (compiler, input, args) => {
230
+ // For operators, input is the left expression and args contains [left, right]
231
+ const leftExpr = input;
232
+ const rightExpr = args?.[1];
233
+
234
+ if (!leftExpr || !rightExpr) {
235
+ throw new Error('as operator requires two arguments');
236
+ }
237
+
238
+ // Similar to 'is', extract type name
239
+ let typeName: string | undefined;
240
+
241
+ if ((rightExpr as any).typeName) {
242
+ typeName = (rightExpr as any).typeName;
243
+ } else if ((rightExpr as any).source && (rightExpr as any).source.match(/^[A-Z]\w*$/)) {
244
+ typeName = (rightExpr as any).source;
245
+ }
246
+
247
+ if (typeName) {
248
+ // Import TypeSystem locally to avoid circular dependency
249
+ const { TypeSystem } = require('../utils/type-system');
250
+
251
+ return {
252
+ fn: (ctx) => {
253
+ const left = leftExpr.fn(ctx);
254
+
255
+ // Filter values that match the type
256
+ const results: any[] = [];
257
+ for (const item of left) {
258
+ if (TypeSystem.isType(item, typeName)) {
259
+ results.push(item);
260
+ }
261
+ }
262
+
263
+ return results;
264
+ },
265
+ type: compiler.resolveType(typeName),
266
+ isSingleton: false
267
+ };
268
+ }
269
+
270
+ // Fallback: dynamic type checking
271
+ return {
272
+ fn: (ctx) => {
273
+ const left = leftExpr.fn(ctx);
274
+ const right = rightExpr.fn(ctx);
275
+
276
+ // Extract type name from right result
277
+ let typeNameValue: string;
278
+ if (right.length === 1 && typeof right[0] === 'string') {
279
+ typeNameValue = right[0];
280
+ } else {
281
+ throw new Error('as operator requires a type name');
282
+ }
283
+
284
+ // Import TypeSystem locally to avoid circular dependency
285
+ const { TypeSystem } = require('../utils/type-system');
286
+
287
+ // Filter values that match the type
288
+ const results: any[] = [];
289
+ for (const item of left) {
290
+ if (TypeSystem.isType(item, typeNameValue)) {
291
+ results.push(item);
292
+ }
293
+ }
294
+
295
+ return results;
296
+ },
297
+ type: leftExpr.type,
298
+ isSingleton: false
299
+ };
300
+ }
301
+ };
302
+
303
+ // Export type operators
304
+ export const typeOperators = [
305
+ isOperator,
306
+ asOperator
307
+ ];