@atomic-ehr/fhirpath 0.0.1-canary.8687028.20250724113707

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 (54) hide show
  1. package/README.md +307 -0
  2. package/dist/index.js +7975 -0
  3. package/package.json +48 -0
  4. package/src/analyzer/analyzer.ts +486 -0
  5. package/src/analyzer/model-provider.ts +244 -0
  6. package/src/analyzer/schemas/index.ts +2 -0
  7. package/src/analyzer/schemas/types.ts +40 -0
  8. package/src/analyzer/types.ts +142 -0
  9. package/src/api/builder.ts +148 -0
  10. package/src/api/errors.ts +134 -0
  11. package/src/api/expression.ts +149 -0
  12. package/src/api/index.ts +57 -0
  13. package/src/api/registry.ts +128 -0
  14. package/src/api/types.ts +154 -0
  15. package/src/compiler/compiler.ts +550 -0
  16. package/src/compiler/index.ts +2 -0
  17. package/src/compiler/types.ts +23 -0
  18. package/src/index.ts +52 -0
  19. package/src/interpreter/README.md +78 -0
  20. package/src/interpreter/context.ts +181 -0
  21. package/src/interpreter/interpreter.ts +429 -0
  22. package/src/interpreter/types.ts +132 -0
  23. package/src/lexer/char-tables.ts +37 -0
  24. package/src/lexer/errors.ts +31 -0
  25. package/src/lexer/index.ts +5 -0
  26. package/src/lexer/lexer.ts +745 -0
  27. package/src/lexer/token.ts +104 -0
  28. package/src/parser/ast.ts +123 -0
  29. package/src/parser/index.ts +3 -0
  30. package/src/parser/parser.ts +701 -0
  31. package/src/parser/pprint.ts +169 -0
  32. package/src/registry/default-analyzers.ts +257 -0
  33. package/src/registry/default-compilers.ts +31 -0
  34. package/src/registry/index.ts +93 -0
  35. package/src/registry/operations/arithmetic.ts +506 -0
  36. package/src/registry/operations/collection.ts +384 -0
  37. package/src/registry/operations/comparison.ts +432 -0
  38. package/src/registry/operations/existence.ts +719 -0
  39. package/src/registry/operations/filtering.ts +374 -0
  40. package/src/registry/operations/literals.ts +341 -0
  41. package/src/registry/operations/logical.ts +402 -0
  42. package/src/registry/operations/math.ts +128 -0
  43. package/src/registry/operations/membership.ts +132 -0
  44. package/src/registry/operations/string.ts +507 -0
  45. package/src/registry/operations/subsetting.ts +174 -0
  46. package/src/registry/operations/type-checking.ts +162 -0
  47. package/src/registry/operations/type-conversion.ts +404 -0
  48. package/src/registry/operations/type-operators.ts +307 -0
  49. package/src/registry/operations/utility.ts +510 -0
  50. package/src/registry/registry.ts +146 -0
  51. package/src/registry/types.ts +162 -0
  52. package/src/registry/utils/evaluation-helpers.ts +93 -0
  53. package/src/registry/utils/index.ts +3 -0
  54. package/src/registry/utils/type-system.ts +173 -0
@@ -0,0 +1,132 @@
1
+ // Core types for FHIRPath interpreter following the stream-processing mental model
2
+
3
+ /**
4
+ * The result of evaluating any FHIRPath expression.
5
+ * Every expression returns a collection and potentially modified context.
6
+ */
7
+ export interface EvaluationResult {
8
+ value: any[]; // Always a collection (even single values are collections of one)
9
+ context: Context;
10
+ }
11
+
12
+ /**
13
+ * Context carries variables and environment data parallel to the data stream.
14
+ * It flows through expressions and can be modified by certain operations.
15
+ *
16
+ * Uses JavaScript prototype chain for efficient inheritance.
17
+ */
18
+ export interface Context {
19
+ // User-defined variables (%varName)
20
+ // Using Record instead of Map for prototype chain compatibility
21
+ variables: Record<string, any[]>;
22
+
23
+ // Special environment variables
24
+ env: {
25
+ $this?: any[]; // Current item in iterator functions
26
+ $index?: number; // Current index in iterator functions
27
+ $total?: any[]; // Accumulator in aggregate function
28
+ };
29
+
30
+ // Root context variables
31
+ $context?: any[]; // Original input to the expression
32
+ $resource?: any[]; // Current resource being processed
33
+ $rootResource?: any[]; // Top-level resource
34
+
35
+ // Custom functions (if any)
36
+ customFunctions?: Record<string, (context: Context, input: any[], ...args: any[]) => any[]>;
37
+ }
38
+
39
+ /**
40
+ * Error thrown during evaluation with position information
41
+ */
42
+ export class EvaluationError extends Error {
43
+ constructor(
44
+ message: string,
45
+ public position?: { line: number; column: number; offset: number }
46
+ ) {
47
+ super(message);
48
+ this.name = 'EvaluationError';
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Type information for runtime type checking
54
+ */
55
+ export interface TypeInfo {
56
+ namespace: string; // 'System' or 'FHIR'
57
+ name: string; // Type name like 'String', 'Patient'
58
+ isCollection?: boolean;
59
+ }
60
+
61
+ /**
62
+ * Helper type for ensuring we always work with collections
63
+ */
64
+ export type Collection<T = any> = T[];
65
+
66
+ /**
67
+ * Singleton conversion result
68
+ */
69
+ export type SingletonResult<T = any> = T | undefined;
70
+
71
+ /**
72
+ * Helper functions for working with collections
73
+ */
74
+ export const CollectionUtils = {
75
+ /**
76
+ * Convert any value to a collection
77
+ */
78
+ toCollection(value: any): any[] {
79
+ if (value === null || value === undefined) {
80
+ return [];
81
+ }
82
+ return Array.isArray(value) ? value : [value];
83
+ },
84
+
85
+ /**
86
+ * Apply singleton evaluation rules
87
+ * @returns The single value or undefined if rules don't apply
88
+ * @throws Error if multiple items when single expected
89
+ */
90
+ toSingleton(collection: any[], expectedType?: string): SingletonResult {
91
+ if (collection.length === 0) {
92
+ return undefined; // Empty propagates
93
+ }
94
+
95
+ if (collection.length === 1) {
96
+ const value = collection[0];
97
+
98
+ // Rule 2: Collection with one item, expecting Boolean → true
99
+ if (expectedType === 'boolean' && typeof value !== 'boolean') {
100
+ return true;
101
+ }
102
+
103
+ // Rule 1: Collection with one item convertible to expected type → use it
104
+ return value;
105
+ }
106
+
107
+ // Rule 4: Multiple items → ERROR
108
+ throw new EvaluationError(`Expected single value but got ${collection.length} items`);
109
+ },
110
+
111
+ /**
112
+ * Check if a collection is empty
113
+ */
114
+ isEmpty(collection: any[]): boolean {
115
+ return collection.length === 0;
116
+ },
117
+
118
+ /**
119
+ * Flatten nested collections
120
+ */
121
+ flatten(collection: any[]): any[] {
122
+ const result: any[] = [];
123
+ for (const item of collection) {
124
+ if (Array.isArray(item)) {
125
+ result.push(...item);
126
+ } else {
127
+ result.push(item);
128
+ }
129
+ }
130
+ return result;
131
+ }
132
+ };
@@ -0,0 +1,37 @@
1
+ // Character classification lookup table for O(1) checks
2
+ export const CHAR_FLAGS = new Uint8Array(128);
3
+
4
+ // Bit flags for character properties
5
+ export const FLAG_DIGIT = 1 << 0;
6
+ export const FLAG_ALPHA = 1 << 1;
7
+ export const FLAG_WHITESPACE = 1 << 2;
8
+ export const FLAG_IDENTIFIER_START = 1 << 3;
9
+ export const FLAG_IDENTIFIER_CONT = 1 << 4;
10
+
11
+ // Initialize lookup table (called once at startup)
12
+ export function initCharTables(): void {
13
+ // Digits
14
+ for (let i = 48; i <= 57; i++) {
15
+ CHAR_FLAGS[i]! |= FLAG_DIGIT | FLAG_IDENTIFIER_CONT;
16
+ }
17
+
18
+ // Letters
19
+ for (let i = 65; i <= 90; i++) {
20
+ CHAR_FLAGS[i]! |= FLAG_ALPHA | FLAG_IDENTIFIER_START | FLAG_IDENTIFIER_CONT;
21
+ }
22
+ for (let i = 97; i <= 122; i++) {
23
+ CHAR_FLAGS[i]! |= FLAG_ALPHA | FLAG_IDENTIFIER_START | FLAG_IDENTIFIER_CONT;
24
+ }
25
+
26
+ // Underscore
27
+ CHAR_FLAGS[95]! |= FLAG_IDENTIFIER_START | FLAG_IDENTIFIER_CONT;
28
+
29
+ // Whitespace
30
+ CHAR_FLAGS[32]! |= FLAG_WHITESPACE; // space
31
+ CHAR_FLAGS[9]! |= FLAG_WHITESPACE; // tab
32
+ CHAR_FLAGS[10]! |= FLAG_WHITESPACE; // newline
33
+ CHAR_FLAGS[13]! |= FLAG_WHITESPACE; // carriage return
34
+ }
35
+
36
+ // Initialize the table immediately
37
+ initCharTables();
@@ -0,0 +1,31 @@
1
+ import type { Position } from './token';
2
+
3
+ export class LexerError extends Error {
4
+ constructor(
5
+ message: string,
6
+ public position: Position,
7
+ public char?: string
8
+ ) {
9
+ super(message);
10
+ this.name = 'LexerError';
11
+ }
12
+
13
+ override toString(): string {
14
+ const location = `${this.position.line}:${this.position.column}`;
15
+ const charInfo = this.char ? ` (found '${this.char}')` : '';
16
+ return `${this.name}: ${this.message} at ${location}${charInfo}`;
17
+ }
18
+ }
19
+
20
+ export function formatError(error: LexerError, input: string): string {
21
+ const lines = input.split('\n');
22
+ const line = lines[error.position.line - 1] || '';
23
+ const pointer = ' '.repeat(error.position.column - 1) + '^';
24
+
25
+ return [
26
+ error.toString(),
27
+ '',
28
+ line,
29
+ pointer
30
+ ].join('\n');
31
+ }
@@ -0,0 +1,5 @@
1
+ export { FHIRPathLexer, lex } from './lexer';
2
+ export type { Token, Position } from './token';
3
+ export { TokenType, Channel } from './token';
4
+ export { LexerError, formatError } from './errors';
5
+ export { CHAR_FLAGS, FLAG_DIGIT, FLAG_ALPHA, FLAG_WHITESPACE, FLAG_IDENTIFIER_START, FLAG_IDENTIFIER_CONT } from './char-tables';