@atomic-ehr/fhirpath 0.0.2 → 0.0.3-canary.2be66fb.20250905161900
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 +716 -238
- package/dist/index.d.ts +226 -120
- package/dist/index.js +11552 -5580
- package/dist/index.js.map +1 -1
- package/package.json +12 -5
- package/src/analyzer/augmentor.ts +242 -0
- package/src/analyzer/cursor-services.ts +75 -0
- package/src/analyzer/scope-manager.ts +57 -0
- package/src/analyzer/trivia-indexer.ts +58 -0
- package/src/analyzer/type-compat.ts +157 -0
- package/src/analyzer/utils.ts +132 -0
- package/src/analyzer.ts +939 -1204
- package/src/completion-provider.ts +209 -191
- package/src/complex-types/quantity-value.ts +410 -0
- package/src/complex-types/temporal.ts +1776 -0
- package/src/errors.ts +25 -3
- package/src/index.ts +17 -104
- package/src/inspect.ts +4 -4
- package/src/{boxing.ts → interpreter/boxing.ts} +1 -1
- package/src/interpreter/navigator.ts +94 -0
- package/src/interpreter/runtime-context.ts +273 -0
- package/src/interpreter.ts +506 -468
- package/src/lexer.ts +192 -211
- package/src/model-provider.ts +71 -43
- package/src/operations/abs-function.ts +1 -1
- package/src/operations/aggregate-function.ts +84 -5
- package/src/operations/all-function.ts +4 -3
- package/src/operations/allFalse-function.ts +2 -1
- package/src/operations/allTrue-function.ts +2 -1
- package/src/operations/and-operator.ts +2 -1
- package/src/operations/anyFalse-function.ts +2 -1
- package/src/operations/anyTrue-function.ts +2 -1
- package/src/operations/as-function.ts +99 -0
- package/src/operations/as-operator.ts +57 -19
- package/src/operations/ceiling-function.ts +1 -1
- package/src/operations/children-function.ts +14 -5
- package/src/operations/combine-function.ts +6 -3
- package/src/operations/combine-operator.ts +6 -7
- package/src/operations/comparison.ts +744 -0
- package/src/operations/contains-function.ts +1 -1
- package/src/operations/contains-operator.ts +2 -1
- package/src/operations/convertsToBoolean-function.ts +78 -0
- package/src/operations/convertsToDecimal-function.ts +82 -0
- package/src/operations/convertsToInteger-function.ts +71 -0
- package/src/operations/convertsToLong-function.ts +89 -0
- package/src/operations/convertsToQuantity-function.ts +132 -0
- package/src/operations/convertsToString-function.ts +88 -0
- package/src/operations/count-function.ts +2 -1
- package/src/operations/dateOf-function.ts +69 -0
- package/src/operations/dayOf-function.ts +66 -0
- package/src/operations/decimal-boundaries.ts +133 -0
- package/src/operations/defineVariable-function.ts +130 -17
- package/src/operations/distinct-function.ts +1 -1
- package/src/operations/div-operator.ts +1 -1
- package/src/operations/divide-operator.ts +12 -7
- package/src/operations/dot-operator.ts +1 -1
- package/src/operations/empty-function.ts +30 -21
- package/src/operations/endsWith-function.ts +6 -1
- package/src/operations/equal-operator.ts +23 -32
- package/src/operations/equivalent-operator.ts +13 -53
- package/src/operations/exclude-function.ts +2 -1
- package/src/operations/exists-function.ts +4 -3
- package/src/operations/extension-function.ts +84 -0
- package/src/operations/first-function.ts +1 -1
- package/src/operations/floor-function.ts +1 -1
- package/src/operations/greater-operator.ts +7 -9
- package/src/operations/greater-or-equal-operator.ts +7 -9
- package/src/operations/highBoundary-function.ts +120 -0
- package/src/operations/hourOf-function.ts +66 -0
- package/src/operations/iif-function.ts +193 -8
- package/src/operations/implies-operator.ts +2 -1
- package/src/operations/in-operator.ts +2 -1
- package/src/operations/index.ts +43 -0
- package/src/operations/indexOf-function.ts +1 -1
- package/src/operations/intersect-function.ts +1 -1
- package/src/operations/is-function.ts +70 -0
- package/src/operations/is-operator.ts +176 -13
- package/src/operations/isDistinct-function.ts +2 -1
- package/src/operations/join-function.ts +1 -1
- package/src/operations/last-function.ts +1 -1
- package/src/operations/lastIndexOf-function.ts +85 -0
- package/src/operations/length-function.ts +1 -1
- package/src/operations/less-operator.ts +8 -9
- package/src/operations/less-or-equal-operator.ts +7 -9
- package/src/operations/less-than.ts +8 -13
- package/src/operations/lowBoundary-function.ts +120 -0
- package/src/operations/lower-function.ts +1 -1
- package/src/operations/matches-function.ts +86 -0
- package/src/operations/matchesFull-function.ts +96 -0
- package/src/operations/millisecondOf-function.ts +66 -0
- package/src/operations/minus-operator.ts +76 -4
- package/src/operations/minuteOf-function.ts +66 -0
- package/src/operations/mod-operator.ts +8 -2
- package/src/operations/monthOf-function.ts +66 -0
- package/src/operations/multiply-operator.ts +27 -3
- package/src/operations/not-equal-operator.ts +24 -30
- package/src/operations/not-equivalent-operator.ts +13 -53
- package/src/operations/not-function.ts +10 -3
- package/src/operations/ofType-function.ts +43 -12
- package/src/operations/or-operator.ts +2 -1
- package/src/operations/plus-operator.ts +71 -7
- package/src/operations/power-function.ts +35 -10
- package/src/operations/precision-function.ts +146 -0
- package/src/operations/repeat-function.ts +169 -0
- package/src/operations/replace-function.ts +1 -1
- package/src/operations/replaceMatches-function.ts +125 -0
- package/src/operations/round-function.ts +1 -1
- package/src/operations/secondOf-function.ts +66 -0
- package/src/operations/select-function.ts +66 -5
- package/src/operations/single-function.ts +1 -1
- package/src/operations/skip-function.ts +1 -1
- package/src/operations/split-function.ts +1 -1
- package/src/operations/sqrt-function.ts +15 -8
- package/src/operations/startsWith-function.ts +1 -1
- package/src/operations/subsetOf-function.ts +6 -2
- package/src/operations/substring-function.ts +1 -1
- package/src/operations/supersetOf-function.ts +6 -2
- package/src/operations/tail-function.ts +1 -1
- package/src/operations/take-function.ts +1 -1
- package/src/operations/temporal-functions.ts +555 -0
- package/src/operations/timeOf-function.ts +67 -0
- package/src/operations/timezoneOffsetOf-function.ts +69 -0
- package/src/operations/toBoolean-function.ts +27 -8
- package/src/operations/toChars-function.ts +56 -0
- package/src/operations/toDecimal-function.ts +27 -8
- package/src/operations/toInteger-function.ts +15 -3
- package/src/operations/toLong-function.ts +98 -0
- package/src/operations/toQuantity-function.ts +181 -0
- package/src/operations/toString-function.ts +78 -15
- package/src/operations/trace-function.ts +1 -1
- package/src/operations/trim-function.ts +1 -1
- package/src/operations/truncate-function.ts +1 -1
- package/src/operations/unary-minus-operator.ts +2 -2
- package/src/operations/unary-plus-operator.ts +1 -1
- package/src/operations/union-function.ts +1 -1
- package/src/operations/union-operator.ts +16 -26
- package/src/operations/upper-function.ts +1 -1
- package/src/operations/where-function.ts +3 -3
- package/src/operations/xor-operator.ts +1 -1
- package/src/operations/yearOf-function.ts +66 -0
- package/src/{cursor-nodes.ts → parser/cursor-nodes.ts} +10 -7
- package/src/parser.ts +262 -503
- package/src/registry.ts +53 -42
- package/src/types.ts +129 -17
- package/src/utils/decimal.ts +76 -0
- package/src/utils/pprint.ts +151 -0
- package/src/quantity-value.ts +0 -198
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type { FunctionDefinition } from '../types';
|
|
2
|
-
import { Errors } from '../errors';
|
|
1
|
+
import type { FunctionDefinition, AnalysisContext, InternalAnalysisResult } from '../types';
|
|
2
|
+
import { Errors, ErrorCodes } from '../errors';
|
|
3
3
|
import type { FunctionEvaluator } from '../types';
|
|
4
|
-
import { box, unbox } from '../boxing';
|
|
5
|
-
import { RuntimeContextManager } from '../interpreter';
|
|
4
|
+
import { box, unbox } from '../interpreter/boxing';
|
|
5
|
+
import { RuntimeContextManager } from '../interpreter/runtime-context';
|
|
6
|
+
import { NodeType } from '../parser';
|
|
7
|
+
import { DiagnosticSeverity } from '../types';
|
|
6
8
|
|
|
7
9
|
export const evaluate: FunctionEvaluator = async (input, context, args, evaluator) => {
|
|
8
10
|
if (args.length < 2) {
|
|
@@ -15,7 +17,7 @@ export const evaluate: FunctionEvaluator = async (input, context, args, evaluato
|
|
|
15
17
|
|
|
16
18
|
// Check for multiple items in input collection
|
|
17
19
|
if (input.length > 1) {
|
|
18
|
-
throw Errors.
|
|
20
|
+
throw Errors.emptyNotAllowed('iif');
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
// Always evaluate condition
|
|
@@ -35,6 +37,12 @@ export const evaluate: FunctionEvaluator = async (input, context, args, evaluato
|
|
|
35
37
|
|
|
36
38
|
const condResult = await evaluator(condExpr, input, evalContext);
|
|
37
39
|
|
|
40
|
+
// Check if condition is a singleton boolean
|
|
41
|
+
if (condResult.value.length > 1) {
|
|
42
|
+
// Multiple items in condition - should error per spec
|
|
43
|
+
throw Errors.invalidOperation('iif condition must be a single boolean value');
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
// Empty condition is treated as false
|
|
39
47
|
if (condResult.value.length === 0) {
|
|
40
48
|
// If no else expression provided, return empty
|
|
@@ -54,7 +62,7 @@ export const evaluate: FunctionEvaluator = async (input, context, args, evaluato
|
|
|
54
62
|
|
|
55
63
|
// Check if condition is a boolean
|
|
56
64
|
if (typeof condition !== 'boolean') {
|
|
57
|
-
// Non-boolean
|
|
65
|
+
// Non-boolean singleton returns empty per FHIRPath spec behavior
|
|
58
66
|
return { value: [], context };
|
|
59
67
|
}
|
|
60
68
|
|
|
@@ -72,6 +80,7 @@ export const evaluate: FunctionEvaluator = async (input, context, args, evaluato
|
|
|
72
80
|
|
|
73
81
|
export const iifFunction: FunctionDefinition & { evaluate: FunctionEvaluator } = {
|
|
74
82
|
name: 'iif',
|
|
83
|
+
doesNotPropagateEmpty: true, // iif evaluates even with empty input
|
|
75
84
|
category: ['control'],
|
|
76
85
|
description: 'If-then-else expression (immediate if)',
|
|
77
86
|
examples: ['iif(gender = "male", "Mr.", "Ms.")'],
|
|
@@ -86,5 +95,181 @@ export const iifFunction: FunctionDefinition & { evaluate: FunctionEvaluator } =
|
|
|
86
95
|
],
|
|
87
96
|
result: { type: 'Any', singleton: false },
|
|
88
97
|
}],
|
|
89
|
-
evaluate
|
|
90
|
-
|
|
98
|
+
evaluate,
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Analysis-time behavior for iif with lazy evaluation.
|
|
102
|
+
* Only analyzes reachable branches when condition is a literal.
|
|
103
|
+
*/
|
|
104
|
+
async analyze(context: AnalysisContext, args): Promise<InternalAnalysisResult> {
|
|
105
|
+
const diagnostics: any[] = [];
|
|
106
|
+
|
|
107
|
+
// Validate argument count
|
|
108
|
+
if (args.length < 2) {
|
|
109
|
+
return {
|
|
110
|
+
type: { type: 'Any', singleton: false },
|
|
111
|
+
diagnostics: [{
|
|
112
|
+
message: 'iif requires at least 2 arguments',
|
|
113
|
+
severity: DiagnosticSeverity.Error,
|
|
114
|
+
code: ErrorCodes.WRONG_ARGUMENT_COUNT,
|
|
115
|
+
source: 'fhirpath',
|
|
116
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }
|
|
117
|
+
}]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (args.length > 3) {
|
|
122
|
+
return {
|
|
123
|
+
type: { type: 'Any', singleton: false },
|
|
124
|
+
diagnostics: [{
|
|
125
|
+
message: 'iif takes at most 3 arguments',
|
|
126
|
+
severity: DiagnosticSeverity.Error,
|
|
127
|
+
code: ErrorCodes.WRONG_ARGUMENT_COUNT,
|
|
128
|
+
source: 'fhirpath',
|
|
129
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }
|
|
130
|
+
}]
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Analyze condition
|
|
135
|
+
const conditionArg = args[0];
|
|
136
|
+
if (!conditionArg) {
|
|
137
|
+
return {
|
|
138
|
+
type: { type: 'Any', singleton: false },
|
|
139
|
+
diagnostics: [{
|
|
140
|
+
message: 'iif requires a condition argument',
|
|
141
|
+
severity: DiagnosticSeverity.Error,
|
|
142
|
+
code: ErrorCodes.WRONG_ARGUMENT_COUNT,
|
|
143
|
+
source: 'fhirpath',
|
|
144
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }
|
|
145
|
+
}]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const condResult = await context.analyzeNode(conditionArg);
|
|
149
|
+
diagnostics.push(...condResult.diagnostics);
|
|
150
|
+
|
|
151
|
+
// Check if condition is a literal boolean
|
|
152
|
+
let isLiteralTrue = false;
|
|
153
|
+
let isLiteralFalse = false;
|
|
154
|
+
|
|
155
|
+
if (conditionArg.type === NodeType.Literal) {
|
|
156
|
+
const literalValue = (conditionArg as any).value;
|
|
157
|
+
if (literalValue === true) {
|
|
158
|
+
isLiteralTrue = true;
|
|
159
|
+
} else if (literalValue === false) {
|
|
160
|
+
isLiteralFalse = true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let trueBranchType = { type: 'Any', singleton: false } as any;
|
|
165
|
+
let falseBranchType = { type: 'Any', singleton: false } as any;
|
|
166
|
+
|
|
167
|
+
// Lazy evaluation: only analyze reachable branches
|
|
168
|
+
if (isLiteralTrue) {
|
|
169
|
+
// Only true branch is reachable
|
|
170
|
+
const trueBranch = args[1];
|
|
171
|
+
if (!trueBranch) {
|
|
172
|
+
return {
|
|
173
|
+
type: { type: 'Any', singleton: false },
|
|
174
|
+
diagnostics
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const trueBranchResult = await context.analyzeNode(trueBranch);
|
|
178
|
+
diagnostics.push(...trueBranchResult.diagnostics);
|
|
179
|
+
trueBranchType = trueBranchResult.type;
|
|
180
|
+
|
|
181
|
+
// Warn about unreachable false branch
|
|
182
|
+
if (args.length === 3 && args[2]) {
|
|
183
|
+
diagnostics.push({
|
|
184
|
+
message: 'Unreachable code: false branch will never execute',
|
|
185
|
+
severity: DiagnosticSeverity.Warning,
|
|
186
|
+
code: ErrorCodes.UNREACHABLE_CODE,
|
|
187
|
+
source: 'fhirpath',
|
|
188
|
+
range: args[2].range || { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { type: trueBranchType, diagnostics };
|
|
193
|
+
} else if (isLiteralFalse) {
|
|
194
|
+
// Only false branch is reachable (if it exists)
|
|
195
|
+
// Warn about unreachable true branch
|
|
196
|
+
const trueBranch = args[1];
|
|
197
|
+
if (trueBranch) {
|
|
198
|
+
diagnostics.push({
|
|
199
|
+
message: 'Unreachable code: true branch will never execute',
|
|
200
|
+
severity: DiagnosticSeverity.Warning,
|
|
201
|
+
code: ErrorCodes.UNREACHABLE_CODE,
|
|
202
|
+
source: 'fhirpath',
|
|
203
|
+
range: trueBranch.range || { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (args.length === 3 && args[2]) {
|
|
208
|
+
const falseBranchResult = await context.analyzeNode(args[2]);
|
|
209
|
+
diagnostics.push(...falseBranchResult.diagnostics);
|
|
210
|
+
falseBranchType = falseBranchResult.type;
|
|
211
|
+
return { type: falseBranchType, diagnostics };
|
|
212
|
+
} else {
|
|
213
|
+
// No else branch, return empty
|
|
214
|
+
return { type: { type: 'Any', singleton: false }, diagnostics };
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
// Dynamic condition: analyze both branches
|
|
218
|
+
const trueBranch = args[1];
|
|
219
|
+
if (trueBranch) {
|
|
220
|
+
const trueBranchResult = await context.analyzeNode(trueBranch);
|
|
221
|
+
diagnostics.push(...trueBranchResult.diagnostics);
|
|
222
|
+
trueBranchType = trueBranchResult.type;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (args.length === 3 && args[2]) {
|
|
226
|
+
const falseBranchResult = await context.analyzeNode(args[2]);
|
|
227
|
+
diagnostics.push(...falseBranchResult.diagnostics);
|
|
228
|
+
falseBranchType = falseBranchResult.type;
|
|
229
|
+
|
|
230
|
+
// Result type is the union of both branches
|
|
231
|
+
// For now, if they differ, return Any
|
|
232
|
+
if (trueBranchType.type === falseBranchType.type &&
|
|
233
|
+
trueBranchType.singleton === falseBranchType.singleton) {
|
|
234
|
+
return { type: trueBranchType, diagnostics };
|
|
235
|
+
} else if (trueBranchType.type === falseBranchType.type) {
|
|
236
|
+
// Same type but different singleton - result is collection
|
|
237
|
+
return { type: { type: trueBranchType.type, singleton: false }, diagnostics };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { type: { type: 'Any', singleton: false }, diagnostics };
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
async inferResultType(analyzer, node, inputType) {
|
|
246
|
+
// iif returns the common type of the true and false branches
|
|
247
|
+
if (node.arguments.length >= 2) {
|
|
248
|
+
const trueBranchType = await (analyzer as any).inferType(node.arguments[1]!, inputType);
|
|
249
|
+
if (node.arguments.length >= 3) {
|
|
250
|
+
const falseBranchType = await (analyzer as any).inferType(node.arguments[2]!, inputType);
|
|
251
|
+
// If both branches have the same type, use that
|
|
252
|
+
if (trueBranchType.type === falseBranchType.type &&
|
|
253
|
+
trueBranchType.singleton === falseBranchType.singleton) {
|
|
254
|
+
return trueBranchType;
|
|
255
|
+
}
|
|
256
|
+
// If types are the same but singleton differs, return as collection
|
|
257
|
+
if (trueBranchType.type === falseBranchType.type) {
|
|
258
|
+
// One is singleton, one is collection - result must be collection
|
|
259
|
+
return { type: trueBranchType.type, singleton: false };
|
|
260
|
+
}
|
|
261
|
+
// Otherwise, check if one is a subtype of the other
|
|
262
|
+
if ((analyzer as any).isTypeCompatible(trueBranchType, falseBranchType)) {
|
|
263
|
+
return falseBranchType;
|
|
264
|
+
}
|
|
265
|
+
if ((analyzer as any).isTypeCompatible(falseBranchType, trueBranchType)) {
|
|
266
|
+
return trueBranchType;
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
// Only true branch, result can be that type or empty
|
|
270
|
+
return { ...trueBranchType, singleton: false };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return { type: 'Any', singleton: false };
|
|
274
|
+
}
|
|
275
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { OperatorDefinition } from '../types';
|
|
2
2
|
import { PRECEDENCE } from '../types';
|
|
3
3
|
import type { OperationEvaluator } from '../types';
|
|
4
|
-
import { box, unbox } from '../boxing';
|
|
4
|
+
import { box, unbox } from '../interpreter/boxing';
|
|
5
5
|
|
|
6
6
|
export const evaluate: OperationEvaluator = async (input, context, left, right) => {
|
|
7
7
|
// Three-valued logic for implies per spec truth table
|
|
@@ -50,6 +50,7 @@ export const impliesOperator: OperatorDefinition & { evaluate: OperationEvaluato
|
|
|
50
50
|
category: ['logical'],
|
|
51
51
|
precedence: PRECEDENCE.IMPLIES,
|
|
52
52
|
associativity: 'right',
|
|
53
|
+
doesNotPropagateEmpty: true, // Implies has special empty handling rules
|
|
53
54
|
description: 'If the left operand evaluates to true, returns the boolean evaluation of the right operand. If the left operand evaluates to false, returns true. Otherwise, returns true if the right operand evaluates to true, and empty ({ }) otherwise',
|
|
54
55
|
examples: [
|
|
55
56
|
'Patient.name.given.exists() implies Patient.name.family.exists()',
|
|
@@ -2,7 +2,7 @@ import type { OperatorDefinition } from '../types';
|
|
|
2
2
|
import { Errors } from '../errors';
|
|
3
3
|
import { PRECEDENCE } from '../types';
|
|
4
4
|
import type { OperationEvaluator } from '../types';
|
|
5
|
-
import { box, unbox } from '../boxing';
|
|
5
|
+
import { box, unbox } from '../interpreter/boxing';
|
|
6
6
|
|
|
7
7
|
export const evaluate: OperationEvaluator = async (input, context, left, right) => {
|
|
8
8
|
// If left is empty, result is empty
|
|
@@ -45,6 +45,7 @@ export const inOperator: OperatorDefinition & { evaluate: OperationEvaluator } =
|
|
|
45
45
|
category: ['membership'],
|
|
46
46
|
precedence: PRECEDENCE.IN_CONTAINS,
|
|
47
47
|
associativity: 'left',
|
|
48
|
+
doesNotPropagateEmpty: true, // Has custom empty handling per spec
|
|
48
49
|
description: 'If the left operand is a collection with a single item, returns true if the item is in the right operand using equality semantics. If the left is empty, the result is empty. If the right is empty, the result is false.',
|
|
49
50
|
examples: ['\'Joe\' in Patient.name.given', '5 in (1 | 2 | 3 | 4 | 5)', 'code in terminologyServer.valueset(\'my-valueset\').code'],
|
|
50
51
|
signatures: [
|
package/src/operations/index.ts
CHANGED
|
@@ -34,6 +34,8 @@ export { containsOperator } from './contains-operator';
|
|
|
34
34
|
export { isOperator } from './is-operator';
|
|
35
35
|
export { asOperator } from './as-operator';
|
|
36
36
|
export { ofTypeFunction } from './ofType-function';
|
|
37
|
+
export { isFunction } from './is-function';
|
|
38
|
+
export { asFunction } from './as-function';
|
|
37
39
|
|
|
38
40
|
// Other operators
|
|
39
41
|
export { unionOperator } from './union-operator';
|
|
@@ -43,10 +45,12 @@ export { dotOperator } from './dot-operator';
|
|
|
43
45
|
// Functions
|
|
44
46
|
export { whereFunction } from './where-function';
|
|
45
47
|
export { selectFunction } from './select-function';
|
|
48
|
+
export { repeatFunction } from './repeat-function';
|
|
46
49
|
export { firstFunction } from './first-function';
|
|
47
50
|
export { lastFunction } from './last-function';
|
|
48
51
|
export { childrenFunction } from './children-function';
|
|
49
52
|
export { descendantsFunction } from './descendants-function';
|
|
53
|
+
export { extensionFunction } from './extension-function';
|
|
50
54
|
export { skipFunction } from './skip-function';
|
|
51
55
|
export { takeFunction } from './take-function';
|
|
52
56
|
export { tailFunction } from './tail-function';
|
|
@@ -60,6 +64,19 @@ export { distinctFunction } from './distinct-function';
|
|
|
60
64
|
export { isDistinctFunction } from './isDistinct-function';
|
|
61
65
|
export { iifFunction } from './iif-function';
|
|
62
66
|
export { defineVariableFunction } from './defineVariable-function';
|
|
67
|
+
|
|
68
|
+
// Temporal functions
|
|
69
|
+
export {
|
|
70
|
+
nowFunction,
|
|
71
|
+
todayFunction,
|
|
72
|
+
timeOfDayFunction,
|
|
73
|
+
toDateFunction,
|
|
74
|
+
toDateTimeFunction,
|
|
75
|
+
toTimeFunction,
|
|
76
|
+
convertsToDateFunction,
|
|
77
|
+
convertsToDateTimeFunction,
|
|
78
|
+
convertsToTimeFunction
|
|
79
|
+
} from './temporal-functions';
|
|
63
80
|
export { joinFunction } from './join-function';
|
|
64
81
|
export { replaceFunction } from './replace-function';
|
|
65
82
|
export { unionFunction } from './union-function';
|
|
@@ -67,7 +84,12 @@ export { combineFunction } from './combine-function';
|
|
|
67
84
|
export { intersectFunction } from './intersect-function';
|
|
68
85
|
export { excludeFunction } from './exclude-function';
|
|
69
86
|
export { indexOfFunction } from './indexOf-function';
|
|
87
|
+
export { lastIndexOfFunction } from './lastIndexOf-function';
|
|
70
88
|
export { substringFunction } from './substring-function';
|
|
89
|
+
export { matchesFunction } from './matches-function';
|
|
90
|
+
export { matchesFullFunction } from './matchesFull-function';
|
|
91
|
+
export { replaceMatchesFunction } from './replaceMatches-function';
|
|
92
|
+
export { toCharsFunction } from './toChars-function';
|
|
71
93
|
export { containsFunction } from './contains-function';
|
|
72
94
|
export { startsWithFunction } from './startsWith-function';
|
|
73
95
|
export { endsWithFunction } from './endsWith-function';
|
|
@@ -88,9 +110,30 @@ export { toIntegerFunction } from './toInteger-function';
|
|
|
88
110
|
export { toDecimalFunction } from './toDecimal-function';
|
|
89
111
|
export { toStringFunction } from './toString-function';
|
|
90
112
|
export { toBooleanFunction } from './toBoolean-function';
|
|
113
|
+
export { toQuantityFunction } from './toQuantity-function';
|
|
114
|
+
export { toLongFunction } from './toLong-function';
|
|
115
|
+
export { convertsToBooleanFunction } from './convertsToBoolean-function';
|
|
116
|
+
export { convertsToIntegerFunction } from './convertsToInteger-function';
|
|
117
|
+
export { convertsToDecimalFunction } from './convertsToDecimal-function';
|
|
118
|
+
export { convertsToStringFunction } from './convertsToString-function';
|
|
119
|
+
export { convertsToQuantityFunction } from './convertsToQuantity-function';
|
|
120
|
+
export { convertsToLongFunction } from './convertsToLong-function';
|
|
91
121
|
|
|
92
122
|
// Utility functions
|
|
93
123
|
export { traceFunction } from './trace-function';
|
|
124
|
+
export { precisionFunction } from './precision-function';
|
|
125
|
+
export { dateOfFunction } from './dateOf-function';
|
|
126
|
+
export { timeOfFunction } from './timeOf-function';
|
|
127
|
+
export { yearOfFunction } from './yearOf-function';
|
|
128
|
+
export { monthOfFunction } from './monthOf-function';
|
|
129
|
+
export { dayOfFunction } from './dayOf-function';
|
|
130
|
+
export { hourOfFunction } from './hourOf-function';
|
|
131
|
+
export { minuteOfFunction } from './minuteOf-function';
|
|
132
|
+
export { secondOfFunction } from './secondOf-function';
|
|
133
|
+
export { millisecondOfFunction } from './millisecondOf-function';
|
|
134
|
+
export { timezoneOffsetOfFunction } from './timezoneOffsetOf-function';
|
|
135
|
+
export { lowBoundaryFunction } from './lowBoundary-function';
|
|
136
|
+
export { highBoundaryFunction } from './highBoundary-function';
|
|
94
137
|
|
|
95
138
|
// Aggregate functions
|
|
96
139
|
export { aggregateFunction } from './aggregate-function';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FunctionDefinition, FunctionEvaluator, LiteralNode } from '../types';
|
|
2
2
|
import { Errors } from '../errors';
|
|
3
|
-
import { box, unbox } from '../boxing';
|
|
3
|
+
import { box, unbox } from '../interpreter/boxing';
|
|
4
4
|
|
|
5
5
|
export const evaluate: FunctionEvaluator = async (input, context, args, evaluator) => {
|
|
6
6
|
// Check single item in input
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FunctionDefinition, FunctionEvaluator } from '../types';
|
|
2
2
|
import { Errors } from '../errors';
|
|
3
|
-
import { box, unbox } from '../boxing';
|
|
3
|
+
import { box, unbox } from '../interpreter/boxing';
|
|
4
4
|
|
|
5
5
|
export const evaluate: FunctionEvaluator = async (input, context, args, evaluator) => {
|
|
6
6
|
if (args.length !== 1) {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { FunctionDefinition, FunctionEvaluator, ASTNode, RuntimeContext, NodeEvaluator } from '../types';
|
|
2
|
+
import type { FHIRPathValue } from '../interpreter/boxing';
|
|
3
|
+
import { NodeType, isIdentifierNode } from '../types';
|
|
4
|
+
import { box } from '../interpreter/boxing';
|
|
5
|
+
import { evaluate as isOperatorEvaluate } from './is-operator';
|
|
6
|
+
|
|
7
|
+
const isEvaluator: FunctionEvaluator = async (
|
|
8
|
+
input: FHIRPathValue[],
|
|
9
|
+
context: RuntimeContext,
|
|
10
|
+
args: ASTNode[],
|
|
11
|
+
evaluator: NodeEvaluator
|
|
12
|
+
) => {
|
|
13
|
+
// is() function takes one argument - the type name
|
|
14
|
+
if (args.length !== 1) {
|
|
15
|
+
return { value: [], context };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const typeArg = args[0];
|
|
19
|
+
if (!typeArg) {
|
|
20
|
+
return { value: [], context };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Extract type name from the argument AST node
|
|
24
|
+
let typeName: string;
|
|
25
|
+
|
|
26
|
+
if (isIdentifierNode(typeArg)) {
|
|
27
|
+
typeName = typeArg.name;
|
|
28
|
+
} else if (typeArg.type === NodeType.Binary && typeArg.operator === '.') {
|
|
29
|
+
// Handle namespaced types like System.Boolean or FHIR.Patient
|
|
30
|
+
// Reconstruct the full type name from the binary expression
|
|
31
|
+
const leftPart = typeArg.left;
|
|
32
|
+
const rightPart = typeArg.right;
|
|
33
|
+
|
|
34
|
+
if (isIdentifierNode(leftPart) && isIdentifierNode(rightPart)) {
|
|
35
|
+
typeName = `${leftPart.name}.${rightPart.name}`;
|
|
36
|
+
} else {
|
|
37
|
+
throw new Error(`is() requires a type name as argument, got complex expression`);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
// For other node types, try to get the name
|
|
41
|
+
throw new Error(`is() requires a type name as argument, got ${typeArg.type}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Use the is operator implementation with the type name
|
|
45
|
+
return isOperatorEvaluate(input, context, input, [typeName]);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export { isEvaluator };
|
|
49
|
+
|
|
50
|
+
export const isFunction: FunctionDefinition & { evaluate: typeof isEvaluator } = {
|
|
51
|
+
name: 'is',
|
|
52
|
+
category: ['type'],
|
|
53
|
+
description: 'Tests if the input is of the specified type',
|
|
54
|
+
examples: ['Patient.name.is(HumanName)', '"hello".is(String)', '5.is(Integer)'],
|
|
55
|
+
signatures: [
|
|
56
|
+
{
|
|
57
|
+
name: 'is-type-check',
|
|
58
|
+
parameters: [{
|
|
59
|
+
name: 'type',
|
|
60
|
+
type: { type: 'Any', singleton: true },
|
|
61
|
+
expression: true,
|
|
62
|
+
typeReference: true
|
|
63
|
+
}],
|
|
64
|
+
input: { type: 'Any', singleton: true },
|
|
65
|
+
result: { type: 'Boolean', singleton: true }
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
doesNotPropagateEmpty: false,
|
|
69
|
+
evaluate: isEvaluator
|
|
70
|
+
};
|