@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,169 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ASTNode,
|
|
3
|
+
IdentifierNode,
|
|
4
|
+
TypeOrIdentifierNode,
|
|
5
|
+
VariableNode,
|
|
6
|
+
TypeReferenceNode,
|
|
7
|
+
BinaryNode,
|
|
8
|
+
UnaryNode,
|
|
9
|
+
UnionNode,
|
|
10
|
+
FunctionNode,
|
|
11
|
+
CollectionNode,
|
|
12
|
+
MembershipTestNode,
|
|
13
|
+
TypeCastNode,
|
|
14
|
+
IndexNode,
|
|
15
|
+
LiteralNode
|
|
16
|
+
} from './ast';
|
|
17
|
+
import { NodeType } from './ast';
|
|
18
|
+
import { TokenType } from '../lexer/token';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Pretty print an AST node as an S-expression (without position information)
|
|
22
|
+
*/
|
|
23
|
+
export function pprint(node: ASTNode, multiline: boolean = false, indent: number = 0): string {
|
|
24
|
+
const spaces = multiline ? ' '.repeat(indent) : '';
|
|
25
|
+
const nl = multiline ? '\n' : '';
|
|
26
|
+
const childIndent = indent + 2;
|
|
27
|
+
|
|
28
|
+
switch (node.type) {
|
|
29
|
+
case NodeType.Literal:
|
|
30
|
+
const lit = node as LiteralNode;
|
|
31
|
+
return `${spaces}(${pprintLiteralValue(lit)}:${lit.valueType})`;
|
|
32
|
+
|
|
33
|
+
case NodeType.Identifier:
|
|
34
|
+
return `${spaces}(${(node as IdentifierNode).name}:id)`;
|
|
35
|
+
|
|
36
|
+
case NodeType.TypeOrIdentifier:
|
|
37
|
+
return `${spaces}(${(node as TypeOrIdentifierNode).name}:type-or-id)`;
|
|
38
|
+
|
|
39
|
+
case NodeType.Variable:
|
|
40
|
+
const varNode = node as VariableNode;
|
|
41
|
+
const varName = varNode.name.startsWith('$') ? varNode.name : `%${varNode.name}`;
|
|
42
|
+
return `${spaces}(${varName}:var)`;
|
|
43
|
+
|
|
44
|
+
case NodeType.TypeReference:
|
|
45
|
+
return `${spaces}(${(node as TypeReferenceNode).typeName}:type-ref)`;
|
|
46
|
+
|
|
47
|
+
case NodeType.Binary:
|
|
48
|
+
const binary = node as BinaryNode;
|
|
49
|
+
const op = tokenToOp(binary.operator);
|
|
50
|
+
if (multiline) {
|
|
51
|
+
return `${spaces}(${op}${nl}${pprint(binary.left, multiline, childIndent)}${nl}${pprint(binary.right, multiline, childIndent)})`;
|
|
52
|
+
} else {
|
|
53
|
+
return `(${op} ${pprint(binary.left)} ${pprint(binary.right)})`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case NodeType.Unary:
|
|
57
|
+
const unary = node as UnaryNode;
|
|
58
|
+
const unaryOp = tokenToOp(unary.operator);
|
|
59
|
+
if (multiline) {
|
|
60
|
+
return `${spaces}(${unaryOp}:unary${nl}${pprint(unary.operand, multiline, childIndent)})`;
|
|
61
|
+
} else {
|
|
62
|
+
return `(${unaryOp}:unary ${pprint(unary.operand)})`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case NodeType.Union:
|
|
66
|
+
const union = node as UnionNode;
|
|
67
|
+
if (multiline) {
|
|
68
|
+
const operands = union.operands.map(op => pprint(op, multiline, childIndent)).join(nl);
|
|
69
|
+
return `${spaces}(|${nl}${operands})`;
|
|
70
|
+
} else {
|
|
71
|
+
return `(| ${union.operands.map(op => pprint(op)).join(' ')})`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case NodeType.Function:
|
|
75
|
+
const func = node as FunctionNode;
|
|
76
|
+
const funcName = (func.name as IdentifierNode).name;
|
|
77
|
+
if (multiline && func.arguments.length > 0) {
|
|
78
|
+
const args = func.arguments.map(arg => pprint(arg, multiline, childIndent)).join(nl);
|
|
79
|
+
return `${spaces}(${funcName}:fn${nl}${args})`;
|
|
80
|
+
} else {
|
|
81
|
+
const args = func.arguments.map(arg => pprint(arg)).join(' ');
|
|
82
|
+
return args ? `${spaces}(${funcName}:fn ${args})` : `${spaces}(${funcName}:fn)`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
case NodeType.Collection:
|
|
86
|
+
const coll = node as CollectionNode;
|
|
87
|
+
if (multiline && coll.elements.length > 0) {
|
|
88
|
+
const elements = coll.elements.map(el => pprint(el, multiline, childIndent)).join(nl);
|
|
89
|
+
return `${spaces}({}:collection${nl}${elements})`;
|
|
90
|
+
} else {
|
|
91
|
+
const elements = coll.elements.map(el => pprint(el)).join(' ');
|
|
92
|
+
return elements ? `${spaces}({}:collection ${elements})` : `${spaces}({}:collection)`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
case NodeType.MembershipTest:
|
|
96
|
+
const memberTest = node as MembershipTestNode;
|
|
97
|
+
if (multiline) {
|
|
98
|
+
return `${spaces}(is${nl}${pprint(memberTest.expression, multiline, childIndent)}${nl}${' '.repeat(childIndent)}${memberTest.targetType})`;
|
|
99
|
+
} else {
|
|
100
|
+
return `(is ${pprint(memberTest.expression)} ${memberTest.targetType})`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
case NodeType.TypeCast:
|
|
104
|
+
const typeCast = node as TypeCastNode;
|
|
105
|
+
if (multiline) {
|
|
106
|
+
return `${spaces}(as${nl}${pprint(typeCast.expression, multiline, childIndent)}${nl}${' '.repeat(childIndent)}${typeCast.targetType})`;
|
|
107
|
+
} else {
|
|
108
|
+
return `(as ${pprint(typeCast.expression)} ${typeCast.targetType})`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
case NodeType.Index:
|
|
112
|
+
const index = node as IndexNode;
|
|
113
|
+
if (multiline) {
|
|
114
|
+
return `${spaces}([]${nl}${pprint(index.expression, multiline, childIndent)}${nl}${pprint(index.index, multiline, childIndent)})`;
|
|
115
|
+
} else {
|
|
116
|
+
return `([] ${pprint(index.expression)} ${pprint(index.index)})`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
default:
|
|
120
|
+
return `${spaces}(unknown:${node.type})`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function pprintLiteralValue(node: LiteralNode): string {
|
|
125
|
+
const value = node.value;
|
|
126
|
+
const valueType = node.valueType;
|
|
127
|
+
|
|
128
|
+
switch (valueType) {
|
|
129
|
+
case 'string':
|
|
130
|
+
return `'${value}'`;
|
|
131
|
+
case 'number':
|
|
132
|
+
return String(value);
|
|
133
|
+
case 'boolean':
|
|
134
|
+
return String(value);
|
|
135
|
+
case 'null':
|
|
136
|
+
return '{}';
|
|
137
|
+
case 'date':
|
|
138
|
+
case 'time':
|
|
139
|
+
case 'datetime':
|
|
140
|
+
return `@${value}`;
|
|
141
|
+
default:
|
|
142
|
+
return String(value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function tokenToOp(token: TokenType): string {
|
|
147
|
+
switch (token) {
|
|
148
|
+
case TokenType.PLUS: return '+';
|
|
149
|
+
case TokenType.MINUS: return '-';
|
|
150
|
+
case TokenType.STAR: return '*';
|
|
151
|
+
case TokenType.DIV: return 'div';
|
|
152
|
+
case TokenType.MOD: return 'mod';
|
|
153
|
+
case TokenType.GT: return '>';
|
|
154
|
+
case TokenType.LT: return '<';
|
|
155
|
+
case TokenType.GTE: return '>=';
|
|
156
|
+
case TokenType.LTE: return '<=';
|
|
157
|
+
case TokenType.EQ: return '=';
|
|
158
|
+
case TokenType.NEQ: return '!=';
|
|
159
|
+
case TokenType.AND: return 'and';
|
|
160
|
+
case TokenType.OR: return 'or';
|
|
161
|
+
case TokenType.XOR: return 'xor';
|
|
162
|
+
case TokenType.IMPLIES: return 'implies';
|
|
163
|
+
case TokenType.NOT: return 'not';
|
|
164
|
+
case TokenType.DOT: return '.';
|
|
165
|
+
case TokenType.CONTAINS: return 'contains';
|
|
166
|
+
case TokenType.IN: return 'in';
|
|
167
|
+
default: return token;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Analyzer,
|
|
3
|
+
TypeInfo,
|
|
4
|
+
TypeRef,
|
|
5
|
+
Operator,
|
|
6
|
+
Function,
|
|
7
|
+
Literal,
|
|
8
|
+
TypeConstraint,
|
|
9
|
+
TypeInferenceRule,
|
|
10
|
+
CardinalityInferenceRule
|
|
11
|
+
} from './types';
|
|
12
|
+
import type { ModelProvider } from '../analyzer/types';
|
|
13
|
+
|
|
14
|
+
// Helper to check if type matches constraint
|
|
15
|
+
export function matchesConstraint(type: TypeRef, constraint: TypeConstraint): boolean {
|
|
16
|
+
if (constraint.kind === 'any') return true;
|
|
17
|
+
|
|
18
|
+
if (!constraint.types) return true;
|
|
19
|
+
|
|
20
|
+
// For now, simple type name matching
|
|
21
|
+
// TODO: Handle inheritance and complex types
|
|
22
|
+
let typeName = typeof type === 'string' ? type : (type as any).type || (type as any).name || 'unknown';
|
|
23
|
+
|
|
24
|
+
// Normalize case - constraint types are capitalized, but actual types might be lowercase
|
|
25
|
+
const normalizedTypeName = typeName.charAt(0).toUpperCase() + typeName.slice(1);
|
|
26
|
+
|
|
27
|
+
return constraint.types.includes(normalizedTypeName) || constraint.types.includes(typeName);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Helper to format constraint for error messages
|
|
31
|
+
export function formatConstraint(constraint: TypeConstraint): string {
|
|
32
|
+
if (constraint.kind === 'any') return 'any type';
|
|
33
|
+
if (!constraint.types || constraint.types.length === 0) return 'any type';
|
|
34
|
+
if (constraint.types.length === 1) return constraint.types[0]!;
|
|
35
|
+
return constraint.types.join(' or ');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Helper to resolve type inference rule
|
|
39
|
+
export function resolveTypeInferenceRule(
|
|
40
|
+
rule: TypeInferenceRule,
|
|
41
|
+
input: TypeRef,
|
|
42
|
+
args: TypeRef[],
|
|
43
|
+
analyzer: Analyzer
|
|
44
|
+
): TypeRef {
|
|
45
|
+
if (typeof rule === 'string') {
|
|
46
|
+
if (rule === 'preserve-input') return input;
|
|
47
|
+
if (rule === 'promote-numeric') {
|
|
48
|
+
// Check if we have numeric types
|
|
49
|
+
const types = [input, ...args].map(t => {
|
|
50
|
+
const rawType = typeof t === 'string' ? t : (t as any).type || (t as any).name || 'unknown';
|
|
51
|
+
// Normalize to uppercase first letter
|
|
52
|
+
return rawType.charAt(0).toUpperCase() + rawType.slice(1);
|
|
53
|
+
});
|
|
54
|
+
if (types.includes('Decimal')) return analyzer.resolveType('Decimal');
|
|
55
|
+
if (types.includes('Integer')) return analyzer.resolveType('Integer');
|
|
56
|
+
return input; // fallback
|
|
57
|
+
}
|
|
58
|
+
return analyzer.resolveType(rule); // Fixed type name - resolve through analyzer
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof rule === 'function') {
|
|
62
|
+
// TODO: We need to pass ModelProvider to the rule function
|
|
63
|
+
// For now, just return input
|
|
64
|
+
return input;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return input; // fallback
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Helper to resolve cardinality inference rule
|
|
71
|
+
export function resolveCardinalityInferenceRule(
|
|
72
|
+
rule: CardinalityInferenceRule,
|
|
73
|
+
inputIsSingleton: boolean,
|
|
74
|
+
argsAreSingleton: boolean[]
|
|
75
|
+
): boolean {
|
|
76
|
+
if (rule === 'singleton') return true;
|
|
77
|
+
if (rule === 'collection') return false;
|
|
78
|
+
if (rule === 'preserve-input') return inputIsSingleton;
|
|
79
|
+
if (rule === 'all-singleton') return inputIsSingleton && argsAreSingleton.every(s => s);
|
|
80
|
+
|
|
81
|
+
if (typeof rule === 'function') {
|
|
82
|
+
return rule(inputIsSingleton, argsAreSingleton);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return inputIsSingleton; // fallback
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Default analyzer for operators
|
|
89
|
+
export function defaultOperatorAnalyze(
|
|
90
|
+
this: Operator,
|
|
91
|
+
analyzer: Analyzer,
|
|
92
|
+
input: TypeInfo,
|
|
93
|
+
args: TypeInfo[]
|
|
94
|
+
): TypeInfo {
|
|
95
|
+
const { signature } = this;
|
|
96
|
+
|
|
97
|
+
// Validate parameter count
|
|
98
|
+
const expectedParams = signature.parameters.length;
|
|
99
|
+
if (args.length !== expectedParams) {
|
|
100
|
+
analyzer.error(`Operator '${this.name}' expects ${expectedParams} parameter(s) but got ${args.length}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Validate parameter types and cardinality
|
|
104
|
+
signature.parameters.forEach((param, i) => {
|
|
105
|
+
const arg = args[i];
|
|
106
|
+
if (!arg) return;
|
|
107
|
+
|
|
108
|
+
// Check type constraint
|
|
109
|
+
if (param.types && !matchesConstraint(arg.type, param.types)) {
|
|
110
|
+
const argTypeName = typeof arg.type === 'string'
|
|
111
|
+
? arg.type
|
|
112
|
+
: (arg.type as any).type || (arg.type as any).name || 'unknown';
|
|
113
|
+
analyzer.error(
|
|
114
|
+
`Operator '${this.name}' parameter '${param.name}' expects ${formatConstraint(param.types)} but got ${argTypeName}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check cardinality constraint
|
|
119
|
+
if (param.cardinality) {
|
|
120
|
+
if (param.cardinality === 'singleton' && !arg.isSingleton) {
|
|
121
|
+
analyzer.error(
|
|
122
|
+
`Operator '${this.name}' parameter '${param.name}' requires singleton value but got collection`
|
|
123
|
+
);
|
|
124
|
+
} else if (param.cardinality === 'collection' && arg.isSingleton) {
|
|
125
|
+
analyzer.error(
|
|
126
|
+
`Operator '${this.name}' parameter '${param.name}' requires collection but got singleton`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
// 'any' accepts both
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Determine output type
|
|
134
|
+
const outputType = resolveTypeInferenceRule(
|
|
135
|
+
signature.output.type,
|
|
136
|
+
input.type,
|
|
137
|
+
args.map(a => a.type),
|
|
138
|
+
analyzer
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Determine output cardinality
|
|
142
|
+
const isSingleton = resolveCardinalityInferenceRule(
|
|
143
|
+
signature.output.cardinality,
|
|
144
|
+
input.isSingleton,
|
|
145
|
+
args.map(a => a.isSingleton)
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return { type: outputType, isSingleton };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Default analyzer for functions
|
|
152
|
+
export function defaultFunctionAnalyze(
|
|
153
|
+
this: Function,
|
|
154
|
+
analyzer: Analyzer,
|
|
155
|
+
input: TypeInfo,
|
|
156
|
+
args: TypeInfo[]
|
|
157
|
+
): TypeInfo {
|
|
158
|
+
const { signature } = this;
|
|
159
|
+
|
|
160
|
+
// Check input constraints if specified
|
|
161
|
+
if (signature.input) {
|
|
162
|
+
if (signature.input.types && !matchesConstraint(input.type, signature.input.types)) {
|
|
163
|
+
const inputTypeName = typeof input.type === 'string'
|
|
164
|
+
? input.type
|
|
165
|
+
: (input.type as any).type || (input.type as any).name || 'unknown';
|
|
166
|
+
analyzer.error(
|
|
167
|
+
`Function '${this.name}' expects input of type ${formatConstraint(signature.input.types)} but got ${inputTypeName}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (signature.input.cardinality) {
|
|
172
|
+
if (signature.input.cardinality === 'singleton' && !input.isSingleton) {
|
|
173
|
+
analyzer.error(`Function '${this.name}' requires singleton input but got collection`);
|
|
174
|
+
} else if (signature.input.cardinality === 'collection' && input.isSingleton) {
|
|
175
|
+
analyzer.error(`Function '${this.name}' requires collection input but got singleton`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Validate parameters
|
|
181
|
+
const requiredParams = signature.parameters.filter(p => !p.optional);
|
|
182
|
+
if (args.length < requiredParams.length) {
|
|
183
|
+
analyzer.error(
|
|
184
|
+
`Function '${this.name}' requires at least ${requiredParams.length} parameter(s) but got ${args.length}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (args.length > signature.parameters.length) {
|
|
189
|
+
analyzer.error(
|
|
190
|
+
`Function '${this.name}' expects at most ${signature.parameters.length} parameter(s) but got ${args.length}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check each parameter
|
|
195
|
+
signature.parameters.forEach((param, i) => {
|
|
196
|
+
const arg = args[i];
|
|
197
|
+
if (!arg && !param.optional) {
|
|
198
|
+
analyzer.error(`Function '${this.name}' parameter '${param.name}' is required`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (!arg) return; // Optional parameter not provided
|
|
202
|
+
|
|
203
|
+
// Check type constraint
|
|
204
|
+
if (param.types && !matchesConstraint(arg.type, param.types)) {
|
|
205
|
+
const argTypeName = typeof arg.type === 'string'
|
|
206
|
+
? arg.type
|
|
207
|
+
: (arg.type as any).type || (arg.type as any).name || 'unknown';
|
|
208
|
+
analyzer.error(
|
|
209
|
+
`Function '${this.name}' parameter '${param.name}' expects ${formatConstraint(param.types)} but got ${argTypeName}`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check cardinality constraint
|
|
214
|
+
if (param.cardinality) {
|
|
215
|
+
if (param.cardinality === 'singleton' && !arg.isSingleton) {
|
|
216
|
+
analyzer.error(
|
|
217
|
+
`Function '${this.name}' parameter '${param.name}' requires singleton value but got collection`
|
|
218
|
+
);
|
|
219
|
+
} else if (param.cardinality === 'collection' && arg.isSingleton) {
|
|
220
|
+
analyzer.error(
|
|
221
|
+
`Function '${this.name}' parameter '${param.name}' requires collection but got singleton`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Determine output type
|
|
228
|
+
const outputType = resolveTypeInferenceRule(
|
|
229
|
+
signature.output.type,
|
|
230
|
+
input.type,
|
|
231
|
+
args.map(a => a.type),
|
|
232
|
+
analyzer
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Determine output cardinality
|
|
236
|
+
const isSingleton = resolveCardinalityInferenceRule(
|
|
237
|
+
signature.output.cardinality,
|
|
238
|
+
input.isSingleton,
|
|
239
|
+
args.map(a => a.isSingleton)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return { type: outputType, isSingleton };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Default analyzer for literals
|
|
246
|
+
export function defaultLiteralAnalyze(
|
|
247
|
+
this: Literal,
|
|
248
|
+
analyzer: Analyzer,
|
|
249
|
+
input: TypeInfo,
|
|
250
|
+
args: TypeInfo[]
|
|
251
|
+
): TypeInfo {
|
|
252
|
+
// Literals have fixed output type and cardinality
|
|
253
|
+
return {
|
|
254
|
+
type: analyzer.resolveType(this.signature.output.type),
|
|
255
|
+
isSingleton: true
|
|
256
|
+
};
|
|
257
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Compiler, CompiledExpression } from './types';
|
|
2
|
+
|
|
3
|
+
export function defaultFunctionCompile(
|
|
4
|
+
compiler: Compiler,
|
|
5
|
+
input: CompiledExpression,
|
|
6
|
+
args: CompiledExpression[]
|
|
7
|
+
): CompiledExpression {
|
|
8
|
+
// Default implementation returns a simple pass-through
|
|
9
|
+
return {
|
|
10
|
+
fn: (ctx) => {
|
|
11
|
+
throw new Error('Function compile not implemented');
|
|
12
|
+
},
|
|
13
|
+
type: compiler.resolveType('Any'),
|
|
14
|
+
isSingleton: false
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function defaultOperatorCompile(
|
|
19
|
+
compiler: Compiler,
|
|
20
|
+
left: CompiledExpression,
|
|
21
|
+
right: CompiledExpression[]
|
|
22
|
+
): CompiledExpression {
|
|
23
|
+
// Default implementation returns a simple pass-through
|
|
24
|
+
return {
|
|
25
|
+
fn: (ctx) => {
|
|
26
|
+
throw new Error('Operator compile not implemented');
|
|
27
|
+
},
|
|
28
|
+
type: compiler.resolveType('Any'),
|
|
29
|
+
isSingleton: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Registry } from './registry';
|
|
2
|
+
import { arithmeticOperators } from './operations/arithmetic';
|
|
3
|
+
import { logicalOperators } from './operations/logical';
|
|
4
|
+
import { comparisonOperators } from './operations/comparison';
|
|
5
|
+
import { membershipOperators } from './operations/membership';
|
|
6
|
+
import { typeOperators } from './operations/type-operators';
|
|
7
|
+
import { literals } from './operations/literals';
|
|
8
|
+
import { existenceFunctions } from './operations/existence';
|
|
9
|
+
import {
|
|
10
|
+
aggregateFunction,
|
|
11
|
+
childrenFunction,
|
|
12
|
+
descendantsFunction,
|
|
13
|
+
iifFunction,
|
|
14
|
+
defineVariableFunction,
|
|
15
|
+
traceFunction,
|
|
16
|
+
checkFunction
|
|
17
|
+
} from './operations/utility';
|
|
18
|
+
import { typeFunction, isFunction, asFunction } from './operations/type-checking';
|
|
19
|
+
import { absFunction, roundFunction, sqrtFunction } from './operations/math';
|
|
20
|
+
import { whereFunction, selectFunction, ofTypeFunction, repeatFunction } from './operations/filtering';
|
|
21
|
+
import {
|
|
22
|
+
containsFunction, lengthFunction, substringFunction, startsWithFunction,
|
|
23
|
+
endsWithFunction, upperFunction, lowerFunction, indexOfFunction,
|
|
24
|
+
replaceFunction, splitFunction, joinFunction, trimFunction, toCharsFunction
|
|
25
|
+
} from './operations/string';
|
|
26
|
+
import {
|
|
27
|
+
toStringFunction, toIntegerFunction, toDecimalFunction,
|
|
28
|
+
toBooleanFunction, toQuantityFunction
|
|
29
|
+
} from './operations/type-conversion';
|
|
30
|
+
import { tailFunction, skipFunction, takeFunction } from './operations/subsetting';
|
|
31
|
+
import { unionFunction, combineFunction, intersectFunction, excludeFunction, collectionOperators } from './operations/collection';
|
|
32
|
+
|
|
33
|
+
// Export types
|
|
34
|
+
export * from './types';
|
|
35
|
+
export { Registry } from './registry';
|
|
36
|
+
export * from './default-analyzers';
|
|
37
|
+
|
|
38
|
+
// Register all operations on module load
|
|
39
|
+
[
|
|
40
|
+
...arithmeticOperators,
|
|
41
|
+
...logicalOperators,
|
|
42
|
+
...comparisonOperators,
|
|
43
|
+
...membershipOperators,
|
|
44
|
+
...typeOperators,
|
|
45
|
+
...collectionOperators,
|
|
46
|
+
...literals,
|
|
47
|
+
...existenceFunctions,
|
|
48
|
+
aggregateFunction,
|
|
49
|
+
childrenFunction,
|
|
50
|
+
descendantsFunction,
|
|
51
|
+
iifFunction,
|
|
52
|
+
defineVariableFunction,
|
|
53
|
+
traceFunction,
|
|
54
|
+
checkFunction,
|
|
55
|
+
typeFunction,
|
|
56
|
+
isFunction,
|
|
57
|
+
asFunction,
|
|
58
|
+
absFunction,
|
|
59
|
+
roundFunction,
|
|
60
|
+
sqrtFunction,
|
|
61
|
+
whereFunction,
|
|
62
|
+
selectFunction,
|
|
63
|
+
ofTypeFunction,
|
|
64
|
+
repeatFunction,
|
|
65
|
+
containsFunction,
|
|
66
|
+
lengthFunction,
|
|
67
|
+
substringFunction,
|
|
68
|
+
startsWithFunction,
|
|
69
|
+
endsWithFunction,
|
|
70
|
+
upperFunction,
|
|
71
|
+
lowerFunction,
|
|
72
|
+
indexOfFunction,
|
|
73
|
+
replaceFunction,
|
|
74
|
+
splitFunction,
|
|
75
|
+
joinFunction,
|
|
76
|
+
trimFunction,
|
|
77
|
+
toCharsFunction,
|
|
78
|
+
toStringFunction,
|
|
79
|
+
toIntegerFunction,
|
|
80
|
+
toDecimalFunction,
|
|
81
|
+
toBooleanFunction,
|
|
82
|
+
toQuantityFunction,
|
|
83
|
+
tailFunction,
|
|
84
|
+
skipFunction,
|
|
85
|
+
takeFunction,
|
|
86
|
+
unionFunction,
|
|
87
|
+
combineFunction,
|
|
88
|
+
intersectFunction,
|
|
89
|
+
excludeFunction
|
|
90
|
+
].forEach(op => Registry.register(op));
|
|
91
|
+
|
|
92
|
+
// Export default registry instance
|
|
93
|
+
export default Registry;
|