@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,358 @@
1
+ import type { Function } from '../types';
2
+ import { defaultFunctionAnalyze } from '../default-analyzers';
3
+ import { defaultFunctionCompile } from '../default-compilers';
4
+ import { RuntimeContextManager } from '../../runtime/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 = RuntimeContextManager.withIterator(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 = RuntimeContextManager.withIterator(ctx, item, i);
72
+ const predicateResult = criteria.fn(iterCtx);
73
+
74
+ if (isTruthy(predicateResult)) {
75
+ results.push(item);
76
+ }
77
+ }
78
+
79
+ return results;
80
+ },
81
+ type: input.type,
82
+ isSingleton: false,
83
+ source: `${input.source || ''}.where(${criteria.source || ''})`
84
+ };
85
+ }
86
+ };
87
+
88
+ export const selectFunction: Function = {
89
+ name: 'select',
90
+ kind: 'function',
91
+
92
+ syntax: {
93
+ notation: 'select(expression)'
94
+ },
95
+
96
+ signature: {
97
+ input: {
98
+ types: { kind: 'any' },
99
+ cardinality: 'any'
100
+ },
101
+ parameters: [
102
+ {
103
+ name: 'expression',
104
+ kind: 'expression',
105
+ types: { kind: 'any' },
106
+ cardinality: 'any',
107
+ optional: false
108
+ }
109
+ ],
110
+ output: {
111
+ type: 'any',
112
+ cardinality: 'collection'
113
+ },
114
+ propagatesEmpty: true,
115
+ deterministic: true
116
+ },
117
+
118
+ analyze: function(analyzer, input, args) {
119
+ // First run default validation
120
+ defaultFunctionAnalyze.call(this, analyzer, input, args);
121
+
122
+ // For select(), the output type is determined by the expression result
123
+ const expressionInfo = args[0];
124
+ if (!expressionInfo) {
125
+ return { type: analyzer.resolveType('Any'), isSingleton: false };
126
+ }
127
+
128
+ // select() always returns a collection
129
+ return { type: expressionInfo.type, isSingleton: false };
130
+ },
131
+
132
+ evaluate: (interpreter, context, input, expression) => {
133
+ const results: any[] = [];
134
+ if (!expression) {
135
+ throw new Error('select() requires an expression');
136
+ }
137
+
138
+ for (let i = 0; i < input.length; i++) {
139
+ const item = input[i];
140
+ const iterContext = RuntimeContextManager.withIterator(context, item, i);
141
+ const result = interpreter.evaluate(expression, [item], iterContext);
142
+ results.push(...result.value);
143
+ }
144
+
145
+ return { value: results, context };
146
+ },
147
+
148
+ compile: (compiler, input, args) => {
149
+ const expression = args[0];
150
+ if (!expression) {
151
+ throw new Error('select() requires an expression');
152
+ }
153
+
154
+ return {
155
+ fn: (ctx) => {
156
+ const inputValue = input.fn(ctx);
157
+ const results: any[] = [];
158
+
159
+ for (let i = 0; i < inputValue.length; i++) {
160
+ const item = inputValue[i];
161
+ const iterCtx = RuntimeContextManager.withIterator(ctx, item, i);
162
+ const exprResult = expression.fn(iterCtx);
163
+ results.push(...exprResult);
164
+ }
165
+
166
+ return results;
167
+ },
168
+ type: expression.type,
169
+ isSingleton: false,
170
+ source: `${input.source || ''}.select(${expression.source || ''})`
171
+ };
172
+ }
173
+ };
174
+
175
+ export const ofTypeFunction: Function = {
176
+ name: 'ofType',
177
+ kind: 'function',
178
+
179
+ syntax: {
180
+ notation: 'ofType(type)'
181
+ },
182
+
183
+ signature: {
184
+ input: {
185
+ types: { kind: 'any' },
186
+ cardinality: 'any'
187
+ },
188
+ parameters: [
189
+ {
190
+ name: 'type',
191
+ kind: 'expression',
192
+ types: { kind: 'any' },
193
+ cardinality: 'any',
194
+ optional: false
195
+ }
196
+ ],
197
+ output: {
198
+ type: 'preserve-input',
199
+ cardinality: 'collection'
200
+ },
201
+ propagatesEmpty: true,
202
+ deterministic: true
203
+ },
204
+
205
+ analyze: defaultFunctionAnalyze,
206
+
207
+ evaluate: (interpreter, context, input, typeNode) => {
208
+ const results: any[] = [];
209
+
210
+ // Extract type name from AST node
211
+ let typeName: string;
212
+ if (typeNode && typeNode.type === 11) { // TypeReference (correct enum value)
213
+ typeName = typeNode.typeName;
214
+ } else if (typeNode && typeNode.type === 1) { // TypeOrIdentifier
215
+ typeName = typeNode.name;
216
+ } else {
217
+ throw new Error('ofType() requires a type reference');
218
+ }
219
+
220
+ for (const item of input) {
221
+ if (isOfType(item, typeName)) {
222
+ results.push(item);
223
+ }
224
+ }
225
+
226
+ return { value: results, context };
227
+ },
228
+
229
+ compile: (compiler, input, args) => {
230
+ // The TypeSystem module with isOfType helper
231
+ const { TypeSystem } = require('../utils/type-system');
232
+
233
+ // The argument should be a TypeReference node compiled to return the type name
234
+ const typeArg = args[0];
235
+ if (!typeArg) {
236
+ throw new Error('ofType() requires a type reference');
237
+ }
238
+
239
+ return {
240
+ fn: (ctx) => {
241
+ const inputValue = input.fn(ctx);
242
+ const results: any[] = [];
243
+
244
+ // Get the type name
245
+ let typeName: string;
246
+ try {
247
+ const typeResult = typeArg.fn(ctx);
248
+ if (typeResult.length === 1 && typeof typeResult[0] === 'string') {
249
+ typeName = typeResult[0];
250
+ } else {
251
+ throw new Error('Type reference must evaluate to a string');
252
+ }
253
+ } catch (e: any) {
254
+ // If it's a type reference that cannot be evaluated, extract from source
255
+ if (typeArg.source && /^[A-Z]/.test(typeArg.source)) {
256
+ typeName = typeArg.source;
257
+ } else {
258
+ throw new Error(`Cannot determine type name: ${e.message}`);
259
+ }
260
+ }
261
+
262
+ // Filter by type
263
+ for (const item of inputValue) {
264
+ if (TypeSystem.isType(item, typeName)) {
265
+ results.push(item);
266
+ }
267
+ }
268
+
269
+ return results;
270
+ },
271
+ type: input.type,
272
+ isSingleton: false,
273
+ source: `${input.source || ''}.ofType(${typeArg.source || ''})`
274
+ };
275
+ }
276
+ };
277
+
278
+ export const repeatFunction: Function = {
279
+ name: 'repeat',
280
+ kind: 'function',
281
+
282
+ syntax: {
283
+ notation: 'repeat(expression)'
284
+ },
285
+
286
+ signature: {
287
+ input: {
288
+ types: { kind: 'any' },
289
+ cardinality: 'any'
290
+ },
291
+ parameters: [
292
+ {
293
+ name: 'expression',
294
+ kind: 'expression',
295
+ types: { kind: 'any' },
296
+ cardinality: 'any',
297
+ optional: false
298
+ }
299
+ ],
300
+ output: {
301
+ type: 'preserve-input',
302
+ cardinality: 'collection'
303
+ },
304
+ propagatesEmpty: true,
305
+ deterministic: true
306
+ },
307
+
308
+ analyze: defaultFunctionAnalyze,
309
+
310
+ evaluate: (interpreter, context, input, expression) => {
311
+ let current = input;
312
+ const seen = new Set();
313
+
314
+ while (current.length > 0) {
315
+ const nextResults: any[] = [];
316
+
317
+ for (let i = 0; i < current.length; i++) {
318
+ const item = current[i];
319
+ const itemKey = JSON.stringify(item);
320
+
321
+ if (!seen.has(itemKey)) {
322
+ seen.add(itemKey);
323
+ const iterContext = RuntimeContextManager.withIterator(context, item, i);
324
+ const result = interpreter.evaluate(expression, [item], iterContext);
325
+ nextResults.push(...result.value);
326
+ }
327
+ }
328
+
329
+ current = nextResults;
330
+ }
331
+
332
+ return { value: Array.from(seen).map(key => JSON.parse(key as string)), context };
333
+ },
334
+
335
+ compile: defaultFunctionCompile
336
+ };
337
+
338
+ // Helper function for type checking
339
+ function isOfType(item: any, typeName: string): boolean {
340
+ // Handle FHIR resource types
341
+ if (item && typeof item === 'object' && item.resourceType === typeName) {
342
+ return true;
343
+ }
344
+
345
+ // Handle primitive types
346
+ switch (typeName) {
347
+ case 'String':
348
+ return typeof item === 'string';
349
+ case 'Boolean':
350
+ return typeof item === 'boolean';
351
+ case 'Integer':
352
+ return typeof item === 'number' && Number.isInteger(item);
353
+ case 'Decimal':
354
+ return typeof item === 'number';
355
+ default:
356
+ return false;
357
+ }
358
+ }
@@ -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
+ ];