@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.
- package/README.md +307 -0
- package/dist/index.d.ts +225 -0
- package/dist/index.js +8185 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
- package/src/analyzer/analyzer.ts +486 -0
- package/src/analyzer/model-provider.ts +244 -0
- package/src/analyzer/schemas/index.ts +2 -0
- package/src/analyzer/schemas/types.ts +40 -0
- package/src/analyzer/types.ts +142 -0
- package/src/api/builder.ts +148 -0
- package/src/api/errors.ts +134 -0
- package/src/api/expression.ts +152 -0
- package/src/api/index.ts +57 -0
- package/src/api/registry.ts +128 -0
- package/src/api/types.ts +154 -0
- package/src/compiler/compiler.ts +579 -0
- package/src/compiler/index.ts +2 -0
- package/src/compiler/prototype-context-adapter.ts +99 -0
- package/src/compiler/types.ts +23 -0
- package/src/index.ts +52 -0
- package/src/interpreter/README.md +78 -0
- package/src/interpreter/interpreter.ts +485 -0
- package/src/interpreter/types.ts +110 -0
- package/src/lexer/char-tables.ts +37 -0
- package/src/lexer/errors.ts +31 -0
- package/src/lexer/index.ts +5 -0
- package/src/lexer/lexer.ts +745 -0
- package/src/lexer/token.ts +104 -0
- package/src/parser/ast.ts +123 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/parser.ts +701 -0
- package/src/parser/pprint.ts +169 -0
- package/src/registry/default-analyzers.ts +257 -0
- package/src/registry/default-compilers.ts +31 -0
- package/src/registry/index.ts +93 -0
- package/src/registry/operations/arithmetic.ts +506 -0
- package/src/registry/operations/collection.ts +425 -0
- package/src/registry/operations/comparison.ts +432 -0
- package/src/registry/operations/existence.ts +703 -0
- package/src/registry/operations/filtering.ts +358 -0
- package/src/registry/operations/literals.ts +341 -0
- package/src/registry/operations/logical.ts +402 -0
- package/src/registry/operations/math.ts +128 -0
- package/src/registry/operations/membership.ts +132 -0
- package/src/registry/operations/string.ts +507 -0
- package/src/registry/operations/subsetting.ts +174 -0
- package/src/registry/operations/type-checking.ts +162 -0
- package/src/registry/operations/type-conversion.ts +404 -0
- package/src/registry/operations/type-operators.ts +307 -0
- package/src/registry/operations/utility.ts +542 -0
- package/src/registry/registry.ts +146 -0
- package/src/registry/types.ts +161 -0
- package/src/registry/utils/evaluation-helpers.ts +93 -0
- package/src/registry/utils/index.ts +3 -0
- package/src/registry/utils/type-system.ts +173 -0
- 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
|
+
];
|