@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,110 @@
|
|
|
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
|
+
import type { RuntimeContext } from '../runtime/context';
|
|
8
|
+
|
|
9
|
+
export interface EvaluationResult {
|
|
10
|
+
value: any[]; // Always a collection (even single values are collections of one)
|
|
11
|
+
context: RuntimeContext;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Re-export RuntimeContext as Context for backward compatibility
|
|
15
|
+
export type { RuntimeContext as Context } from '../runtime/context';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Error thrown during evaluation with position information
|
|
19
|
+
*/
|
|
20
|
+
export class EvaluationError extends Error {
|
|
21
|
+
constructor(
|
|
22
|
+
message: string,
|
|
23
|
+
public position?: { line: number; column: number; offset: number }
|
|
24
|
+
) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'EvaluationError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Type information for runtime type checking
|
|
32
|
+
*/
|
|
33
|
+
export interface TypeInfo {
|
|
34
|
+
namespace: string; // 'System' or 'FHIR'
|
|
35
|
+
name: string; // Type name like 'String', 'Patient'
|
|
36
|
+
isCollection?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Helper type for ensuring we always work with collections
|
|
41
|
+
*/
|
|
42
|
+
export type Collection<T = any> = T[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Singleton conversion result
|
|
46
|
+
*/
|
|
47
|
+
export type SingletonResult<T = any> = T | undefined;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Helper functions for working with collections
|
|
51
|
+
*/
|
|
52
|
+
export const CollectionUtils = {
|
|
53
|
+
/**
|
|
54
|
+
* Convert any value to a collection
|
|
55
|
+
*/
|
|
56
|
+
toCollection(value: any): any[] {
|
|
57
|
+
if (value === null || value === undefined) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
return Array.isArray(value) ? value : [value];
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Apply singleton evaluation rules
|
|
65
|
+
* @returns The single value or undefined if rules don't apply
|
|
66
|
+
* @throws Error if multiple items when single expected
|
|
67
|
+
*/
|
|
68
|
+
toSingleton(collection: any[], expectedType?: string): SingletonResult {
|
|
69
|
+
if (collection.length === 0) {
|
|
70
|
+
return undefined; // Empty propagates
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (collection.length === 1) {
|
|
74
|
+
const value = collection[0];
|
|
75
|
+
|
|
76
|
+
// Rule 2: Collection with one item, expecting Boolean → true
|
|
77
|
+
if (expectedType === 'boolean' && typeof value !== 'boolean') {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Rule 1: Collection with one item convertible to expected type → use it
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Rule 4: Multiple items → ERROR
|
|
86
|
+
throw new EvaluationError(`Expected single value but got ${collection.length} items`);
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if a collection is empty
|
|
91
|
+
*/
|
|
92
|
+
isEmpty(collection: any[]): boolean {
|
|
93
|
+
return collection.length === 0;
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Flatten nested collections
|
|
98
|
+
*/
|
|
99
|
+
flatten(collection: any[]): any[] {
|
|
100
|
+
const result: any[] = [];
|
|
101
|
+
for (const item of collection) {
|
|
102
|
+
if (Array.isArray(item)) {
|
|
103
|
+
result.push(...item);
|
|
104
|
+
} else {
|
|
105
|
+
result.push(item);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
@@ -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';
|