@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,374 @@
1
+ import type { Function } from '../types';
2
+ import { defaultFunctionAnalyze } from '../default-analyzers';
3
+ import { defaultFunctionCompile } from '../default-compilers';
4
+ import { ContextManager } from '../../interpreter/context';
5
+ import { isTruthy } from '../utils';
6
+
7
+ export const whereFunction: Function = {
8
+ name: 'where',
9
+ kind: 'function',
10
+
11
+ syntax: {
12
+ notation: 'where(criteria)'
13
+ },
14
+
15
+ signature: {
16
+ input: {
17
+ types: { kind: 'any' },
18
+ cardinality: 'any'
19
+ },
20
+ parameters: [
21
+ {
22
+ name: 'criteria',
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: true,
34
+ deterministic: true
35
+ },
36
+
37
+ analyze: defaultFunctionAnalyze,
38
+
39
+ evaluate: (interpreter, context, input, criteria) => {
40
+ const results: any[] = [];
41
+ if (!criteria) {
42
+ throw new Error('where() requires a predicate expression');
43
+ }
44
+
45
+ for (let i = 0; i < input.length; i++) {
46
+ const item = input[i];
47
+ const iterContext = ContextManager.setIteratorContext(context, item, i);
48
+ const result = interpreter.evaluate(criteria, [item], iterContext);
49
+
50
+ if (isTruthy(result.value)) {
51
+ results.push(item);
52
+ }
53
+ }
54
+
55
+ return { value: results, context };
56
+ },
57
+
58
+ compile: (compiler, input, args) => {
59
+ const criteria = args[0];
60
+ if (!criteria) {
61
+ throw new Error('where() requires a predicate expression');
62
+ }
63
+
64
+ return {
65
+ fn: (ctx) => {
66
+ const inputValue = input.fn(ctx);
67
+ const results: any[] = [];
68
+
69
+ for (let i = 0; i < inputValue.length; i++) {
70
+ const item = inputValue[i];
71
+ const iterCtx = {
72
+ ...ctx,
73
+ focus: [item],
74
+ env: {
75
+ ...ctx.env,
76
+ $index: i,
77
+ $this: [item]
78
+ }
79
+ };
80
+ const predicateResult = criteria.fn(iterCtx);
81
+
82
+ if (isTruthy(predicateResult)) {
83
+ results.push(item);
84
+ }
85
+ }
86
+
87
+ return results;
88
+ },
89
+ type: input.type,
90
+ isSingleton: false,
91
+ source: `${input.source || ''}.where(${criteria.source || ''})`
92
+ };
93
+ }
94
+ };
95
+
96
+ export const selectFunction: Function = {
97
+ name: 'select',
98
+ kind: 'function',
99
+
100
+ syntax: {
101
+ notation: 'select(expression)'
102
+ },
103
+
104
+ signature: {
105
+ input: {
106
+ types: { kind: 'any' },
107
+ cardinality: 'any'
108
+ },
109
+ parameters: [
110
+ {
111
+ name: 'expression',
112
+ kind: 'expression',
113
+ types: { kind: 'any' },
114
+ cardinality: 'any',
115
+ optional: false
116
+ }
117
+ ],
118
+ output: {
119
+ type: 'any',
120
+ cardinality: 'collection'
121
+ },
122
+ propagatesEmpty: true,
123
+ deterministic: true
124
+ },
125
+
126
+ analyze: function(analyzer, input, args) {
127
+ // First run default validation
128
+ defaultFunctionAnalyze.call(this, analyzer, input, args);
129
+
130
+ // For select(), the output type is determined by the expression result
131
+ const expressionInfo = args[0];
132
+ if (!expressionInfo) {
133
+ return { type: analyzer.resolveType('Any'), isSingleton: false };
134
+ }
135
+
136
+ // select() always returns a collection
137
+ return { type: expressionInfo.type, isSingleton: false };
138
+ },
139
+
140
+ evaluate: (interpreter, context, input, expression) => {
141
+ const results: any[] = [];
142
+ if (!expression) {
143
+ throw new Error('select() requires an expression');
144
+ }
145
+
146
+ for (let i = 0; i < input.length; i++) {
147
+ const item = input[i];
148
+ const iterContext = ContextManager.setIteratorContext(context, item, i);
149
+ const result = interpreter.evaluate(expression, [item], iterContext);
150
+ results.push(...result.value);
151
+ }
152
+
153
+ return { value: results, context };
154
+ },
155
+
156
+ compile: (compiler, input, args) => {
157
+ const expression = args[0];
158
+ if (!expression) {
159
+ throw new Error('select() requires an expression');
160
+ }
161
+
162
+ return {
163
+ fn: (ctx) => {
164
+ const inputValue = input.fn(ctx);
165
+ const results: any[] = [];
166
+
167
+ for (let i = 0; i < inputValue.length; i++) {
168
+ const item = inputValue[i];
169
+ const iterCtx = {
170
+ ...ctx,
171
+ focus: [item],
172
+ env: {
173
+ ...ctx.env,
174
+ $index: i,
175
+ $this: [item]
176
+ }
177
+ };
178
+ const exprResult = expression.fn(iterCtx);
179
+ results.push(...exprResult);
180
+ }
181
+
182
+ return results;
183
+ },
184
+ type: expression.type,
185
+ isSingleton: false,
186
+ source: `${input.source || ''}.select(${expression.source || ''})`
187
+ };
188
+ }
189
+ };
190
+
191
+ export const ofTypeFunction: Function = {
192
+ name: 'ofType',
193
+ kind: 'function',
194
+
195
+ syntax: {
196
+ notation: 'ofType(type)'
197
+ },
198
+
199
+ signature: {
200
+ input: {
201
+ types: { kind: 'any' },
202
+ cardinality: 'any'
203
+ },
204
+ parameters: [
205
+ {
206
+ name: 'type',
207
+ kind: 'expression',
208
+ types: { kind: 'any' },
209
+ cardinality: 'any',
210
+ optional: false
211
+ }
212
+ ],
213
+ output: {
214
+ type: 'preserve-input',
215
+ cardinality: 'collection'
216
+ },
217
+ propagatesEmpty: true,
218
+ deterministic: true
219
+ },
220
+
221
+ analyze: defaultFunctionAnalyze,
222
+
223
+ evaluate: (interpreter, context, input, typeNode) => {
224
+ const results: any[] = [];
225
+
226
+ // Extract type name from AST node
227
+ let typeName: string;
228
+ if (typeNode && typeNode.type === 11) { // TypeReference (correct enum value)
229
+ typeName = typeNode.typeName;
230
+ } else if (typeNode && typeNode.type === 1) { // TypeOrIdentifier
231
+ typeName = typeNode.name;
232
+ } else {
233
+ throw new Error('ofType() requires a type reference');
234
+ }
235
+
236
+ for (const item of input) {
237
+ if (isOfType(item, typeName)) {
238
+ results.push(item);
239
+ }
240
+ }
241
+
242
+ return { value: results, context };
243
+ },
244
+
245
+ compile: (compiler, input, args) => {
246
+ // The TypeSystem module with isOfType helper
247
+ const { TypeSystem } = require('../utils/type-system');
248
+
249
+ // The argument should be a TypeReference node compiled to return the type name
250
+ const typeArg = args[0];
251
+ if (!typeArg) {
252
+ throw new Error('ofType() requires a type reference');
253
+ }
254
+
255
+ return {
256
+ fn: (ctx) => {
257
+ const inputValue = input.fn(ctx);
258
+ const results: any[] = [];
259
+
260
+ // Get the type name
261
+ let typeName: string;
262
+ try {
263
+ const typeResult = typeArg.fn(ctx);
264
+ if (typeResult.length === 1 && typeof typeResult[0] === 'string') {
265
+ typeName = typeResult[0];
266
+ } else {
267
+ throw new Error('Type reference must evaluate to a string');
268
+ }
269
+ } catch (e: any) {
270
+ // If it's a type reference that cannot be evaluated, extract from source
271
+ if (typeArg.source && /^[A-Z]/.test(typeArg.source)) {
272
+ typeName = typeArg.source;
273
+ } else {
274
+ throw new Error(`Cannot determine type name: ${e.message}`);
275
+ }
276
+ }
277
+
278
+ // Filter by type
279
+ for (const item of inputValue) {
280
+ if (TypeSystem.isType(item, typeName)) {
281
+ results.push(item);
282
+ }
283
+ }
284
+
285
+ return results;
286
+ },
287
+ type: input.type,
288
+ isSingleton: false,
289
+ source: `${input.source || ''}.ofType(${typeArg.source || ''})`
290
+ };
291
+ }
292
+ };
293
+
294
+ export const repeatFunction: Function = {
295
+ name: 'repeat',
296
+ kind: 'function',
297
+
298
+ syntax: {
299
+ notation: 'repeat(expression)'
300
+ },
301
+
302
+ signature: {
303
+ input: {
304
+ types: { kind: 'any' },
305
+ cardinality: 'any'
306
+ },
307
+ parameters: [
308
+ {
309
+ name: 'expression',
310
+ kind: 'expression',
311
+ types: { kind: 'any' },
312
+ cardinality: 'any',
313
+ optional: false
314
+ }
315
+ ],
316
+ output: {
317
+ type: 'preserve-input',
318
+ cardinality: 'collection'
319
+ },
320
+ propagatesEmpty: true,
321
+ deterministic: true
322
+ },
323
+
324
+ analyze: defaultFunctionAnalyze,
325
+
326
+ evaluate: (interpreter, context, input, expression) => {
327
+ let current = input;
328
+ const seen = new Set();
329
+
330
+ while (current.length > 0) {
331
+ const nextResults: any[] = [];
332
+
333
+ for (let i = 0; i < current.length; i++) {
334
+ const item = current[i];
335
+ const itemKey = JSON.stringify(item);
336
+
337
+ if (!seen.has(itemKey)) {
338
+ seen.add(itemKey);
339
+ const iterContext = ContextManager.setIteratorContext(context, item, i);
340
+ const result = interpreter.evaluate(expression, [item], iterContext);
341
+ nextResults.push(...result.value);
342
+ }
343
+ }
344
+
345
+ current = nextResults;
346
+ }
347
+
348
+ return { value: Array.from(seen).map(key => JSON.parse(key as string)), context };
349
+ },
350
+
351
+ compile: defaultFunctionCompile
352
+ };
353
+
354
+ // Helper function for type checking
355
+ function isOfType(item: any, typeName: string): boolean {
356
+ // Handle FHIR resource types
357
+ if (item && typeof item === 'object' && item.resourceType === typeName) {
358
+ return true;
359
+ }
360
+
361
+ // Handle primitive types
362
+ switch (typeName) {
363
+ case 'String':
364
+ return typeof item === 'string';
365
+ case 'Boolean':
366
+ return typeof item === 'boolean';
367
+ case 'Integer':
368
+ return typeof item === 'number' && Number.isInteger(item);
369
+ case 'Decimal':
370
+ return typeof item === 'number';
371
+ default:
372
+ return false;
373
+ }
374
+ }
@@ -0,0 +1,341 @@
1
+ import type { Literal } from '../types';
2
+ import { defaultLiteralAnalyze } from '../default-analyzers';
3
+
4
+ export const integerLiteral: Literal = {
5
+ name: 'integer-literal',
6
+ kind: 'literal',
7
+
8
+ syntax: {
9
+ pattern: /^-?\d+$/,
10
+ notation: '123'
11
+ },
12
+
13
+ signature: {
14
+ output: {
15
+ type: 'Integer',
16
+ cardinality: 'singleton'
17
+ }
18
+ },
19
+
20
+ parse: (value: string) => parseInt(value, 10),
21
+
22
+ analyze: defaultLiteralAnalyze,
23
+
24
+ evaluate: (interpreter, context, input, ...args) => {
25
+ // Value is passed as first argument from parser
26
+ const value = args[0];
27
+ return { value: [value], context };
28
+ },
29
+
30
+ compile: (compiler, input, args) => {
31
+ // Value is captured from parser
32
+ const value = (input as any).value || 0; // Parser stores parsed value
33
+ return {
34
+ fn: (ctx) => [value],
35
+ type: compiler.resolveType('Integer'),
36
+ isSingleton: true,
37
+ source: value.toString()
38
+ };
39
+ }
40
+ };
41
+
42
+ export const decimalLiteral: Literal = {
43
+ name: 'decimal-literal',
44
+ kind: 'literal',
45
+
46
+ syntax: {
47
+ pattern: /^-?\d+\.\d+$/,
48
+ notation: '123.45'
49
+ },
50
+
51
+ signature: {
52
+ output: {
53
+ type: 'Decimal',
54
+ cardinality: 'singleton'
55
+ }
56
+ },
57
+
58
+ parse: (value: string) => parseFloat(value),
59
+
60
+ analyze: defaultLiteralAnalyze,
61
+
62
+ evaluate: (interpreter, context, input, ...args) => {
63
+ const value = args[0];
64
+ return { value: [value], context };
65
+ },
66
+
67
+ compile: (compiler, input, args) => {
68
+ const value = (input as any).value || 0.0;
69
+ return {
70
+ fn: (ctx) => [value],
71
+ type: compiler.resolveType('Decimal'),
72
+ isSingleton: true,
73
+ source: value.toString()
74
+ };
75
+ }
76
+ };
77
+
78
+ export const trueLiteral: Literal = {
79
+ name: 'true',
80
+ kind: 'literal',
81
+
82
+ syntax: {
83
+ keywords: ['true'],
84
+ notation: 'true'
85
+ },
86
+
87
+ signature: {
88
+ output: {
89
+ type: 'Boolean',
90
+ cardinality: 'singleton'
91
+ }
92
+ },
93
+
94
+ parse: () => true,
95
+
96
+ analyze: defaultLiteralAnalyze,
97
+
98
+ evaluate: (interpreter, context) => ({ value: [true], context }),
99
+
100
+ compile: (compiler) => ({
101
+ fn: (ctx) => [true],
102
+ type: compiler.resolveType('Boolean'),
103
+ isSingleton: true,
104
+ source: 'true'
105
+ })
106
+ };
107
+
108
+ export const falseLiteral: Literal = {
109
+ name: 'false',
110
+ kind: 'literal',
111
+
112
+ syntax: {
113
+ keywords: ['false'],
114
+ notation: 'false'
115
+ },
116
+
117
+ signature: {
118
+ output: {
119
+ type: 'Boolean',
120
+ cardinality: 'singleton'
121
+ }
122
+ },
123
+
124
+ parse: () => false,
125
+
126
+ analyze: defaultLiteralAnalyze,
127
+
128
+ evaluate: (interpreter, context) => ({ value: [false], context }),
129
+
130
+ compile: (compiler) => ({
131
+ fn: (ctx) => [false],
132
+ type: compiler.resolveType('Boolean'),
133
+ isSingleton: true,
134
+ source: 'false'
135
+ })
136
+ };
137
+
138
+ export const stringLiteral: Literal = {
139
+ name: 'string-literal',
140
+ kind: 'literal',
141
+
142
+ syntax: {
143
+ pattern: /^'([^'\\]|\\.)*'$/,
144
+ notation: "'hello'"
145
+ },
146
+
147
+ signature: {
148
+ output: {
149
+ type: 'String',
150
+ cardinality: 'singleton'
151
+ }
152
+ },
153
+
154
+ parse: (value: string) => {
155
+ // Remove quotes and unescape
156
+ const content = value.slice(1, -1);
157
+ return content
158
+ .replace(/\\'/g, "'")
159
+ .replace(/\\\\/g, "\\")
160
+ .replace(/\\n/g, "\n")
161
+ .replace(/\\r/g, "\r")
162
+ .replace(/\\t/g, "\t")
163
+ .replace(/\\f/g, "\f")
164
+ .replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
165
+ },
166
+
167
+ analyze: defaultLiteralAnalyze,
168
+
169
+ evaluate: (interpreter, context, input, ...args) => {
170
+ const value = args[0];
171
+ return { value: [value], context };
172
+ },
173
+
174
+ compile: (compiler, input, args) => {
175
+ const value = (input as any).value || '';
176
+ return {
177
+ fn: (ctx) => [value],
178
+ type: compiler.resolveType('String'),
179
+ isSingleton: true,
180
+ source: `'${value.replace(/'/g, "\\'")}'`
181
+ };
182
+ }
183
+ };
184
+
185
+ export const dateTimeLiteral: Literal = {
186
+ name: 'datetime-literal',
187
+ kind: 'literal',
188
+
189
+ syntax: {
190
+ pattern: /@\d{4}(-\d{2}(-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})?)?)?)?/,
191
+ notation: '@2023-01-01T12:00:00Z'
192
+ },
193
+
194
+ signature: {
195
+ output: {
196
+ type: 'DateTime',
197
+ cardinality: 'singleton'
198
+ }
199
+ },
200
+
201
+ parse: (value: string) => {
202
+ // Remove @ prefix
203
+ const dateStr = value.substring(1);
204
+
205
+ // Parse partial dates by padding with defaults
206
+ const parts = dateStr.match(/^(\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|[+-]\d{2}:\d{2})?)?)?)?$/);
207
+ if (!parts) throw new Error(`Invalid DateTime literal: ${value}`);
208
+
209
+ const [, year, month = '01', day = '01', hour = '00', minute = '00', second = '00', ms = '0', tz = ''] = parts;
210
+
211
+ const isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms.padEnd(3, '0')}${tz || 'Z'}`;
212
+ return new Date(isoString);
213
+ },
214
+
215
+ analyze: defaultLiteralAnalyze,
216
+
217
+ evaluate: (interpreter, context, input, ...args) => {
218
+ const value = args[0];
219
+ return { value: [value], context };
220
+ },
221
+
222
+ compile: (compiler, input, args) => {
223
+ const value = (input as any).value || new Date();
224
+ return {
225
+ fn: (ctx) => [value],
226
+ type: compiler.resolveType('DateTime'),
227
+ isSingleton: true,
228
+ source: `@${value.toISOString()}`
229
+ };
230
+ }
231
+ };
232
+
233
+ export const timeLiteral: Literal = {
234
+ name: 'time-literal',
235
+ kind: 'literal',
236
+
237
+ syntax: {
238
+ pattern: /@T\d{2}:\d{2}(:\d{2}(\.\d+)?)?/,
239
+ notation: '@T12:00:00'
240
+ },
241
+
242
+ signature: {
243
+ output: {
244
+ type: 'Time',
245
+ cardinality: 'singleton'
246
+ }
247
+ },
248
+
249
+ parse: (value: string) => {
250
+ // Remove @T prefix
251
+ const timeStr = value.substring(2);
252
+
253
+ // Parse time components
254
+ const parts = timeStr.match(/^(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?$/);
255
+ if (!parts) throw new Error(`Invalid Time literal: ${value}`);
256
+
257
+ const [, hour, minute, second = '00', ms = '0'] = parts as string[];
258
+
259
+ // Store as object with time components
260
+ return {
261
+ hour: parseInt(hour || '0', 10),
262
+ minute: parseInt(minute || '0', 10),
263
+ second: parseInt(second || '0', 10),
264
+ millisecond: parseInt(ms.padEnd(3, '0'), 10)
265
+ };
266
+ },
267
+
268
+ analyze: defaultLiteralAnalyze,
269
+
270
+ evaluate: (interpreter, context, input, ...args) => {
271
+ const value = args[0];
272
+ return { value: [value], context };
273
+ },
274
+
275
+ compile: (compiler, input, args) => {
276
+ const value = (input as any).value || { hour: 0, minute: 0, second: 0 };
277
+ return {
278
+ fn: (ctx) => [value],
279
+ type: compiler.resolveType('Time'),
280
+ isSingleton: true,
281
+ source: `@T${String(value.hour).padStart(2, '0')}:${String(value.minute).padStart(2, '0')}:${String(value.second).padStart(2, '0')}`
282
+ };
283
+ }
284
+ };
285
+
286
+ export const quantityLiteral: Literal = {
287
+ name: 'quantity-literal',
288
+ kind: 'literal',
289
+
290
+ syntax: {
291
+ pattern: /^-?\d+(\.\d+)?\s*'[^']+'$/,
292
+ notation: "5.4 'mg'"
293
+ },
294
+
295
+ signature: {
296
+ output: {
297
+ type: 'Quantity',
298
+ cardinality: 'singleton'
299
+ }
300
+ },
301
+
302
+ parse: (value: string) => {
303
+ const match = value.match(/^(-?\d+(?:\.\d+)?)\s*'([^']+)'$/);
304
+ if (!match) throw new Error(`Invalid Quantity literal: ${value}`);
305
+
306
+ const [, num, unit] = match as [string, string, string];
307
+ return {
308
+ value: parseFloat(num || '0'),
309
+ unit: unit || ''
310
+ };
311
+ },
312
+
313
+ analyze: defaultLiteralAnalyze,
314
+
315
+ evaluate: (interpreter, context, input, ...args) => {
316
+ const value = args[0];
317
+ return { value: [value], context };
318
+ },
319
+
320
+ compile: (compiler, input, args) => {
321
+ const value = (input as any).value || { value: 0, unit: '' };
322
+ return {
323
+ fn: (ctx) => [value],
324
+ type: compiler.resolveType('Quantity'),
325
+ isSingleton: true,
326
+ source: `${value.value} '${value.unit}'`
327
+ };
328
+ }
329
+ };
330
+
331
+ // Export all literals
332
+ export const literals = [
333
+ integerLiteral,
334
+ decimalLiteral,
335
+ trueLiteral,
336
+ falseLiteral,
337
+ stringLiteral,
338
+ dateTimeLiteral,
339
+ timeLiteral,
340
+ quantityLiteral
341
+ ];