@atomic-ehr/fhirpath 0.0.1-canary.0c6931e.20250727185306

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 (85) hide show
  1. package/README.md +473 -0
  2. package/dist/index.d.ts +462 -0
  3. package/dist/index.js +10307 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +58 -0
  6. package/src/analyzer/analyzer.ts +499 -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 +157 -0
  12. package/src/api/errors.ts +145 -0
  13. package/src/api/expression.ts +156 -0
  14. package/src/api/index.ts +122 -0
  15. package/src/api/inspect.ts +99 -0
  16. package/src/api/registry.ts +128 -0
  17. package/src/api/types.ts +210 -0
  18. package/src/compiler/compiler.ts +546 -0
  19. package/src/compiler/index.ts +2 -0
  20. package/src/compiler/prototype-context-adapter.ts +99 -0
  21. package/src/compiler/types.ts +24 -0
  22. package/src/index.ts +107 -0
  23. package/src/interpreter/README.md +78 -0
  24. package/src/interpreter/interpreter.ts +475 -0
  25. package/src/interpreter/types.ts +108 -0
  26. package/src/lexer/char-tables.ts +37 -0
  27. package/src/lexer/errors.ts +31 -0
  28. package/src/lexer/index.ts +5 -0
  29. package/src/lexer/lexer.ts +745 -0
  30. package/src/lexer/token.ts +104 -0
  31. package/src/lexer2/index.md +232 -0
  32. package/src/lexer2/index.perf.test.ts +68 -0
  33. package/src/lexer2/index.test.ts +549 -0
  34. package/src/lexer2/index.ts +1251 -0
  35. package/src/lexer2/notes.md +173 -0
  36. package/src/lexer2/optimization-summary.md +718 -0
  37. package/src/parser/ast-factory.ts +220 -0
  38. package/src/parser/ast.ts +144 -0
  39. package/src/parser/collection-parser.ts +89 -0
  40. package/src/parser/diagnostic-messages.ts +216 -0
  41. package/src/parser/diagnostics.ts +85 -0
  42. package/src/parser/error-reporter.ts +230 -0
  43. package/src/parser/index.ts +3 -0
  44. package/src/parser/literal-parser.ts +103 -0
  45. package/src/parser/parse-error.ts +16 -0
  46. package/src/parser/parser-error-factory.ts +141 -0
  47. package/src/parser/parser-state.ts +134 -0
  48. package/src/parser/parser.ts +1272 -0
  49. package/src/parser/pprint.ts +169 -0
  50. package/src/parser/precedence-manager.ts +64 -0
  51. package/src/parser/source-mapper.ts +248 -0
  52. package/src/parser/special-constructs.ts +142 -0
  53. package/src/parser/token-navigator.ts +110 -0
  54. package/src/parser/types.ts +60 -0
  55. package/src/parser2/index.md +177 -0
  56. package/src/parser2/index.perf.test.ts +184 -0
  57. package/src/parser2/index.test.ts +305 -0
  58. package/src/parser2/index.ts +578 -0
  59. package/src/parser2/optimization-summary.md +176 -0
  60. package/src/registry/default-analyzers.ts +257 -0
  61. package/src/registry/default-compilers.ts +31 -0
  62. package/src/registry/index.ts +96 -0
  63. package/src/registry/operations/arithmetic.ts +506 -0
  64. package/src/registry/operations/collection.ts +425 -0
  65. package/src/registry/operations/comparison.ts +432 -0
  66. package/src/registry/operations/existence.ts +703 -0
  67. package/src/registry/operations/filtering.ts +358 -0
  68. package/src/registry/operations/literals.ts +341 -0
  69. package/src/registry/operations/logical.ts +439 -0
  70. package/src/registry/operations/math.ts +128 -0
  71. package/src/registry/operations/membership.ts +132 -0
  72. package/src/registry/operations/navigation.ts +52 -0
  73. package/src/registry/operations/string.ts +507 -0
  74. package/src/registry/operations/subsetting.ts +174 -0
  75. package/src/registry/operations/type-checking.ts +162 -0
  76. package/src/registry/operations/type-conversion.ts +404 -0
  77. package/src/registry/operations/type-operators.ts +308 -0
  78. package/src/registry/operations/utility.ts +644 -0
  79. package/src/registry/registry.ts +146 -0
  80. package/src/registry/types.ts +161 -0
  81. package/src/registry/utils/evaluation-helpers.ts +93 -0
  82. package/src/registry/utils/index.ts +3 -0
  83. package/src/registry/utils/type-system.ts +173 -0
  84. package/src/runtime/context.ts +158 -0
  85. package/src/runtime/debug-context.ts +135 -0
@@ -0,0 +1,145 @@
1
+ import type { Location } from './types';
2
+
3
+ export enum ErrorCode {
4
+ // Parse errors
5
+ PARSE_ERROR = 'PARSE_ERROR',
6
+ SYNTAX_ERROR = 'SYNTAX_ERROR',
7
+ UNEXPECTED_TOKEN = 'UNEXPECTED_TOKEN',
8
+ UNTERMINATED_STRING = 'UNTERMINATED_STRING',
9
+ INVALID_ESCAPE = 'INVALID_ESCAPE',
10
+
11
+ // Parser-specific error codes
12
+ UNCLOSED_PARENTHESIS = 'UNCLOSED_PARENTHESIS',
13
+ UNCLOSED_BRACKET = 'UNCLOSED_BRACKET',
14
+ UNCLOSED_BRACE = 'UNCLOSED_BRACE',
15
+ MISSING_ARGUMENTS = 'MISSING_ARGUMENTS',
16
+ INVALID_OPERATOR = 'INVALID_OPERATOR',
17
+ EXPECTED_EXPRESSION = 'EXPECTED_EXPRESSION',
18
+ EXPECTED_IDENTIFIER = 'EXPECTED_IDENTIFIER',
19
+ MULTIPLE_ERRORS = 'MULTIPLE_ERRORS',
20
+ INVALID_CHARACTER = 'INVALID_CHARACTER',
21
+
22
+ // Type errors
23
+ TYPE_ERROR = 'TYPE_ERROR',
24
+ TYPE_MISMATCH = 'TYPE_MISMATCH',
25
+ UNKNOWN_TYPE = 'UNKNOWN_TYPE',
26
+ CARDINALITY_ERROR = 'CARDINALITY_ERROR',
27
+
28
+ // Runtime errors
29
+ RUNTIME_ERROR = 'RUNTIME_ERROR',
30
+ UNDEFINED_VARIABLE = 'UNDEFINED_VARIABLE',
31
+ UNDEFINED_FUNCTION = 'UNDEFINED_FUNCTION',
32
+ INVALID_ARGUMENT = 'INVALID_ARGUMENT',
33
+ DIVISION_BY_ZERO = 'DIVISION_BY_ZERO',
34
+
35
+ // Analysis errors
36
+ ANALYSIS_ERROR = 'ANALYSIS_ERROR',
37
+ UNREACHABLE_CODE = 'UNREACHABLE_CODE',
38
+ AMBIGUOUS_TYPE = 'AMBIGUOUS_TYPE',
39
+ }
40
+
41
+ export class FHIRPathError extends Error {
42
+ constructor(
43
+ message: string,
44
+ public code: ErrorCode,
45
+ public location?: Location,
46
+ public expression?: string
47
+ ) {
48
+ super(message);
49
+ this.name = 'FHIRPathError';
50
+
51
+ // Ensure proper prototype chain
52
+ Object.setPrototypeOf(this, FHIRPathError.prototype);
53
+ }
54
+
55
+ override toString(): string {
56
+ let result = `${this.name}: ${this.message}`;
57
+
58
+ if (this.expression && this.location) {
59
+ result += `\n\n${this.expression}`;
60
+ if (this.location.offset >= 0 && this.location.length > 0) {
61
+ const indent = ' '.repeat(this.location.offset);
62
+ const marker = '^'.repeat(this.location.length);
63
+ result += `\n${indent}${marker}`;
64
+ }
65
+ }
66
+
67
+ if (this.location) {
68
+ result += `\n\nAt line ${this.location.line}, column ${this.location.column}`;
69
+ }
70
+
71
+ return result;
72
+ }
73
+ }
74
+
75
+ // Error factory functions
76
+ export function parseError(
77
+ message: string,
78
+ location?: Location,
79
+ expression?: string
80
+ ): FHIRPathError {
81
+ return new FHIRPathError(message, ErrorCode.PARSE_ERROR, location, expression);
82
+ }
83
+
84
+ export function syntaxError(
85
+ message: string,
86
+ location?: Location,
87
+ expression?: string
88
+ ): FHIRPathError {
89
+ return new FHIRPathError(message, ErrorCode.SYNTAX_ERROR, location, expression);
90
+ }
91
+
92
+ export function typeError(
93
+ message: string,
94
+ location?: Location,
95
+ expression?: string
96
+ ): FHIRPathError {
97
+ return new FHIRPathError(message, ErrorCode.TYPE_ERROR, location, expression);
98
+ }
99
+
100
+ export function runtimeError(
101
+ message: string,
102
+ location?: Location,
103
+ expression?: string
104
+ ): FHIRPathError {
105
+ return new FHIRPathError(message, ErrorCode.RUNTIME_ERROR, location, expression);
106
+ }
107
+
108
+ export function undefinedVariable(
109
+ name: string,
110
+ location?: Location,
111
+ expression?: string
112
+ ): FHIRPathError {
113
+ return new FHIRPathError(
114
+ `Undefined variable: ${name}`,
115
+ ErrorCode.UNDEFINED_VARIABLE,
116
+ location,
117
+ expression
118
+ );
119
+ }
120
+
121
+ export function undefinedFunction(
122
+ name: string,
123
+ location?: Location,
124
+ expression?: string
125
+ ): FHIRPathError {
126
+ return new FHIRPathError(
127
+ `Undefined function: ${name}`,
128
+ ErrorCode.UNDEFINED_FUNCTION,
129
+ location,
130
+ expression
131
+ );
132
+ }
133
+
134
+ export function invalidArgument(
135
+ message: string,
136
+ location?: Location,
137
+ expression?: string
138
+ ): FHIRPathError {
139
+ return new FHIRPathError(
140
+ `Invalid argument: ${message}`,
141
+ ErrorCode.INVALID_ARGUMENT,
142
+ location,
143
+ expression
144
+ );
145
+ }
@@ -0,0 +1,156 @@
1
+ import type { ASTNode } from '../parser/ast';
2
+ import type {
3
+ FHIRPathExpression as IFHIRPathExpression,
4
+ CompiledExpression,
5
+ EvaluationContext,
6
+ CompileOptions,
7
+ AnalyzeOptions,
8
+ AnalysisResult
9
+ } from './types';
10
+ import { Interpreter } from '../interpreter/interpreter';
11
+ import { Compiler } from '../compiler/compiler';
12
+ import { analyzeFHIRPath } from '../analyzer/analyzer';
13
+ import { RuntimeContextManager } from '../runtime/context';
14
+ import { pprint } from '../parser/pprint';
15
+
16
+ export class FHIRPathExpression implements IFHIRPathExpression {
17
+ constructor(
18
+ public readonly ast: ASTNode,
19
+ private readonly originalExpression: string
20
+ ) {}
21
+
22
+ evaluate(input?: any, context?: EvaluationContext): any[] {
23
+ const interpreter = new Interpreter();
24
+ const inputArray = input === undefined ? [] : Array.isArray(input) ? input : [input];
25
+ const ctx = this.createContext(context, inputArray);
26
+
27
+ const result = interpreter.evaluate(this.ast, inputArray, ctx);
28
+
29
+ return result.value;
30
+ }
31
+
32
+ compile(options?: CompileOptions): CompiledExpression {
33
+ const compiler = new Compiler();
34
+ const compiled = compiler.compile(this.ast);
35
+
36
+ // Create the compiled function
37
+ const fn = (input?: any, context?: EvaluationContext): any[] => {
38
+ const inputArray = input === undefined ? [] : Array.isArray(input) ? input : [input];
39
+ const ctx = this.createContext(context, inputArray);
40
+
41
+ return compiled.fn(ctx);
42
+ };
43
+
44
+ // Add source property
45
+ Object.defineProperty(fn, 'source', {
46
+ value: options?.sourceMap ? this.originalExpression : this.toString(),
47
+ writable: false,
48
+ enumerable: true
49
+ });
50
+
51
+ return fn as CompiledExpression;
52
+ }
53
+
54
+ analyze(options?: AnalyzeOptions): AnalysisResult {
55
+ try {
56
+ // Create analyzer-compatible model provider
57
+ const analyzerModelProvider = options?.modelProvider ? {
58
+ resolveType: options.modelProvider.resolveType.bind(options.modelProvider),
59
+ getPropertyType: (type: any, propName: string) => {
60
+ const props = options.modelProvider!.getProperties(options.modelProvider!.getTypeName ? options.modelProvider!.getTypeName(type) : String(type));
61
+ const prop = props.find(p => p.name === propName);
62
+ if (!prop) return undefined;
63
+ return {
64
+ type: options.modelProvider!.resolveType(prop.type),
65
+ isSingleton: !prop.isCollection
66
+ };
67
+ },
68
+ isAssignable: (from: any, to: any) => {
69
+ const hierarchy = options.modelProvider!.getTypeHierarchy(options.modelProvider!.getTypeName ? options.modelProvider!.getTypeName(from) : String(from));
70
+ const toName = options.modelProvider!.getTypeName ? options.modelProvider!.getTypeName(to) : String(to);
71
+ return hierarchy.includes(toName);
72
+ },
73
+ getTypeName: (type: any) => String(type)
74
+ } : {
75
+ resolveType: () => undefined,
76
+ getPropertyType: () => undefined,
77
+ isAssignable: () => false,
78
+ getTypeName: () => 'unknown'
79
+ };
80
+
81
+ const result = analyzeFHIRPath(this.ast, analyzerModelProvider);
82
+
83
+ return {
84
+ type: result.resultType || { kind: 'unknown' },
85
+ isSingleton: result.resultIsSingleton || false,
86
+ errors: result.diagnostics.filter(d => d.severity === 'error').map(d => ({
87
+ message: d.message,
88
+ location: d.position ? {
89
+ line: d.position.line,
90
+ column: d.position.column,
91
+ offset: d.position.offset,
92
+ length: 1 // We don't have length info in Position
93
+ } : undefined,
94
+ code: 'ANALYSIS_ERROR'
95
+ })),
96
+ warnings: result.diagnostics.filter(d => d.severity === 'warning').map(d => ({
97
+ message: d.message,
98
+ location: d.position ? {
99
+ line: d.position.line,
100
+ column: d.position.column,
101
+ offset: d.position.offset,
102
+ length: 1
103
+ } : undefined,
104
+ code: 'ANALYSIS_WARNING'
105
+ }))
106
+ };
107
+ } catch (error) {
108
+ return {
109
+ type: { kind: 'unknown' },
110
+ isSingleton: false,
111
+ errors: [{
112
+ message: error instanceof Error ? error.message : String(error),
113
+ code: 'ANALYSIS_ERROR'
114
+ }],
115
+ warnings: []
116
+ };
117
+ }
118
+ }
119
+
120
+ toString(): string {
121
+ return pprint(this.ast);
122
+ }
123
+
124
+ private createContext(evalContext?: EvaluationContext, input: any[] = []) {
125
+ let ctx = RuntimeContextManager.create(input);
126
+
127
+ if (evalContext?.variables) {
128
+ for (const [name, value] of Object.entries(evalContext.variables)) {
129
+ ctx = RuntimeContextManager.setVariable(ctx, name, Array.isArray(value) ? value : [value]);
130
+ }
131
+ }
132
+
133
+ if (evalContext?.environment) {
134
+ // Environment variables are stored as regular variables with appropriate prefix
135
+ for (const [name, value] of Object.entries(evalContext.environment)) {
136
+ if (name.startsWith('$')) {
137
+ // Special environment variables
138
+ const varName = name.substring(1); // Remove $ prefix
139
+ ctx = RuntimeContextManager.setSpecialVariable(ctx, varName, Array.isArray(value) ? value : [value]);
140
+ } else {
141
+ // Regular environment variables
142
+ ctx = RuntimeContextManager.setVariable(ctx, name, Array.isArray(value) ? value : [value]);
143
+ }
144
+ }
145
+ }
146
+
147
+ // Store custom functions in context for interpreter to access
148
+ if (evalContext?.customFunctions) {
149
+ // TODO: Custom functions need to be implemented through the registry
150
+ // For now, we'll store them in env
151
+ (ctx as any).customFunctions = evalContext.customFunctions;
152
+ }
153
+
154
+ return ctx;
155
+ }
156
+ }
@@ -0,0 +1,122 @@
1
+ import type {
2
+ FHIRPathExpression,
3
+ CompiledExpression,
4
+ EvaluationContext,
5
+ CompileOptions,
6
+ AnalyzeOptions,
7
+ AnalysisResult,
8
+ InspectResult,
9
+ InspectOptions
10
+ } from './types';
11
+ import { FHIRPathParser } from '../parser/parser';
12
+ import {
13
+ type ParserOptions,
14
+ type ParseResult,
15
+ type ParseDiagnostic,
16
+ type TextRange
17
+ } from '../parser/types';
18
+ import type { ASTNode } from '../parser/ast';
19
+ import { FHIRPathExpression as Expression } from './expression';
20
+ import { FHIRPathError, parseError } from './errors';
21
+ import { publicRegistry } from './registry';
22
+ import { inspect as inspectImpl } from './inspect';
23
+
24
+ // Export parser types for API consumers
25
+ export {
26
+ type ParserOptions,
27
+ type ParseResult,
28
+ type ParseDiagnostic,
29
+ type DiagnosticSeverity,
30
+ type TextRange,
31
+ type Position
32
+ } from '../parser/types';
33
+
34
+ // New parse function with mode support
35
+ export function parse(expression: string, options: ParserOptions = {}): ParseResult {
36
+ const parser = new FHIRPathParser(expression, options);
37
+ return parser.parse();
38
+ }
39
+
40
+ // Convenience function for evaluation (throws on error)
41
+ export function parseForEvaluation(expression: string): ASTNode {
42
+ try {
43
+ const result = parse(expression, { throwOnError: true });
44
+ return result.ast;
45
+ } catch (error) {
46
+ if (error instanceof Error) {
47
+ throw parseError(error.message, undefined, expression);
48
+ }
49
+ throw parseError(String(error), undefined, expression);
50
+ }
51
+ }
52
+
53
+ // Type guards for result types
54
+
55
+ export function isStandardResult(result: ParseResult): result is ParseResult {
56
+ return 'ast' in result && 'diagnostics' in result && 'hasErrors' in result;
57
+ }
58
+
59
+ export function isDiagnosticResult(result: ParseResult): result is ParseResult & { isPartial: boolean; ranges: Map<ASTNode, TextRange> } {
60
+ return isStandardResult(result) && 'isPartial' in result && 'ranges' in result;
61
+ }
62
+
63
+ // Validate function - alternative to removed Validate mode
64
+ export function validate(expression: string): { valid: boolean; diagnostics: ParseDiagnostic[] } {
65
+ const result = parse(expression);
66
+ if (isStandardResult(result)) {
67
+ return {
68
+ valid: !result.hasErrors,
69
+ diagnostics: result.diagnostics
70
+ };
71
+ }
72
+ // Should not happen, but handle gracefully
73
+ return { valid: true, diagnostics: [] };
74
+ }
75
+
76
+
77
+ // Evaluate expression directly
78
+ export function evaluate(
79
+ expression: string | FHIRPathExpression,
80
+ input?: any,
81
+ context?: EvaluationContext
82
+ ): any[] {
83
+ const expr = typeof expression === 'string'
84
+ ? new Expression(parseForEvaluation(expression), expression)
85
+ : expression;
86
+ return expr.evaluate(input, context);
87
+ }
88
+
89
+ // Compile to optimized function
90
+ export function compile(
91
+ expression: string | FHIRPathExpression,
92
+ options?: CompileOptions
93
+ ): CompiledExpression {
94
+ const expr = typeof expression === 'string'
95
+ ? new Expression(parseForEvaluation(expression), expression)
96
+ : expression;
97
+ return expr.compile(options);
98
+ }
99
+
100
+ // Analyze expression for validation
101
+ export function analyze(
102
+ expression: string | FHIRPathExpression,
103
+ options?: AnalyzeOptions
104
+ ): AnalysisResult {
105
+ const expr = typeof expression === 'string'
106
+ ? new Expression(parseForEvaluation(expression), expression)
107
+ : expression;
108
+ return expr.analyze(options);
109
+ }
110
+
111
+ // Inspect expression with debugging information
112
+ export function inspect(
113
+ expression: string | FHIRPathExpression,
114
+ input?: any,
115
+ context?: EvaluationContext,
116
+ options?: InspectOptions
117
+ ): InspectResult {
118
+ return inspectImpl(expression, input, context, options);
119
+ }
120
+
121
+ // Default registry instance
122
+ export const registry = publicRegistry;
@@ -0,0 +1,99 @@
1
+ import type {
2
+ FHIRPathExpression,
3
+ EvaluationContext,
4
+ InspectResult,
5
+ InspectOptions,
6
+ ErrorInfo,
7
+ WarningInfo
8
+ } from './types';
9
+ import { parseForEvaluation } from './index';
10
+ import { FHIRPathExpression as Expression } from './expression';
11
+ import { Interpreter } from '../interpreter/interpreter';
12
+ import { RuntimeContextManager } from '../runtime/context';
13
+ import { createDebugContext, isDebugContext } from '../runtime/debug-context';
14
+ import { FHIRPathError } from './errors';
15
+
16
+ /**
17
+ * Inspect a FHIRPath expression, providing rich debugging information
18
+ * including traces, AST, execution time, and optionally step-by-step evaluation
19
+ */
20
+ export function inspect(
21
+ expression: string | FHIRPathExpression,
22
+ input?: any,
23
+ context?: EvaluationContext,
24
+ options?: InspectOptions
25
+ ): InspectResult {
26
+ const startTime = performance.now();
27
+
28
+ // Parse if string
29
+ const expr = typeof expression === 'string'
30
+ ? new Expression(parseForEvaluation(expression), expression)
31
+ : expression;
32
+ const exprString = typeof expression === 'string' ? expression : expr.toString();
33
+
34
+ // Prepare input (default to empty array)
35
+ const inputValue = input === undefined ? [] : Array.isArray(input) ? input : [input];
36
+
37
+ // Create runtime context
38
+ const runtimeContext = RuntimeContextManager.create(inputValue, context?.variables);
39
+
40
+ // Convert to debug context
41
+ const debugContext = createDebugContext(runtimeContext, options);
42
+
43
+ // Initialize result
44
+ const errors: ErrorInfo[] = [];
45
+ const warnings: WarningInfo[] = [];
46
+ let result: any[] = [];
47
+
48
+ try {
49
+ // Create interpreter
50
+ const interpreter = new Interpreter();
51
+
52
+ // Evaluate with debug context
53
+ const evalResult = interpreter.evaluate(expr.ast, inputValue, debugContext);
54
+ result = evalResult.value;
55
+ } catch (error) {
56
+ // Capture error information
57
+ const errorInfo: ErrorInfo = {
58
+ message: error instanceof Error ? error.message : String(error),
59
+ type: error instanceof FHIRPathError ? error.name : 'Error'
60
+ };
61
+
62
+ if (error instanceof Error && error.stack) {
63
+ errorInfo.stack = error.stack;
64
+ }
65
+
66
+ errors.push(errorInfo);
67
+
68
+ // Re-throw if it's a critical error
69
+ if (error instanceof FHIRPathError && error.name === 'ParseError') {
70
+ throw error;
71
+ }
72
+ }
73
+
74
+ const executionTime = performance.now() - startTime;
75
+
76
+ // Build result
77
+ const inspectResult: InspectResult = {
78
+ result,
79
+ expression: exprString,
80
+ ast: expr.ast,
81
+ executionTime,
82
+ traces: debugContext.traces
83
+ };
84
+
85
+ // Add optional fields
86
+ if (debugContext.steps && debugContext.steps.length > 0) {
87
+ inspectResult.evaluationSteps = debugContext.steps;
88
+ }
89
+
90
+ if (errors.length > 0) {
91
+ inspectResult.errors = errors;
92
+ }
93
+
94
+ if (warnings.length > 0) {
95
+ inspectResult.warnings = warnings;
96
+ }
97
+
98
+ return inspectResult;
99
+ }
@@ -0,0 +1,128 @@
1
+ import type {
2
+ RegistryAPI,
3
+ OperationMetadata,
4
+ OperationInfo
5
+ } from './types';
6
+ import { Registry } from '../registry/registry';
7
+ import type { Operation } from '../registry/types';
8
+
9
+ export class PublicRegistryAPI implements RegistryAPI {
10
+ listFunctions(): OperationMetadata[] {
11
+ return Registry.getAllFunctions().map(op => this.toMetadata(op));
12
+ }
13
+
14
+ listOperators(): OperationMetadata[] {
15
+ const operators = [
16
+ ...Registry.getOperatorsByForm('infix'),
17
+ ...Registry.getOperatorsByForm('prefix'),
18
+ ...Registry.getOperatorsByForm('postfix')
19
+ ];
20
+ return operators.map(op => this.toMetadata(op));
21
+ }
22
+
23
+ listAllOperations(): OperationMetadata[] {
24
+ return Registry.getAllOperations().map(op => this.toMetadata(op));
25
+ }
26
+
27
+ hasOperation(name: string): boolean {
28
+ return Registry.get(name) !== undefined;
29
+ }
30
+
31
+ hasFunction(name: string): boolean {
32
+ const op = Registry.get(name);
33
+ return op !== undefined && op.kind === 'function';
34
+ }
35
+
36
+ hasOperator(symbol: string): boolean {
37
+ const op = Registry.get(symbol);
38
+ return op !== undefined && op.kind === 'operator';
39
+ }
40
+
41
+ getOperationInfo(name: string): OperationInfo | undefined {
42
+ const op = Registry.get(name);
43
+ if (!op) return undefined;
44
+
45
+ return this.toOperationInfo(op);
46
+ }
47
+
48
+ canRegisterFunction(name: string): boolean {
49
+ // Check if name is valid and not already taken
50
+ if (!name || typeof name !== 'string') return false;
51
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) return false;
52
+
53
+ // Check if already exists as built-in
54
+ return !this.hasOperation(name);
55
+ }
56
+
57
+ private toMetadata(op: Operation): OperationMetadata {
58
+ return {
59
+ name: op.name,
60
+ kind: op.kind,
61
+ syntax: {
62
+ notation: op.syntax.notation
63
+ }
64
+ };
65
+ }
66
+
67
+ private toOperationInfo(op: Operation): OperationInfo {
68
+ const info: OperationInfo = {
69
+ ...this.toMetadata(op),
70
+ signature: {}
71
+ };
72
+
73
+ // Add input signature for functions
74
+ if (op.kind === 'function' && op.signature.input) {
75
+ info.signature.input = {
76
+ types: this.extractTypes(op.signature.input.types),
77
+ cardinality: op.signature.input.cardinality
78
+ };
79
+ }
80
+
81
+ // Add parameters
82
+ if ('parameters' in op.signature && op.signature.parameters && op.signature.parameters.length > 0) {
83
+ info.signature.parameters = op.signature.parameters.map((param: any) => ({
84
+ name: param.name,
85
+ types: this.extractTypes(param.types),
86
+ cardinality: param.cardinality,
87
+ optional: param.optional
88
+ }));
89
+ }
90
+
91
+ // Add output signature
92
+ if (op.signature.output) {
93
+ info.signature.output = {
94
+ type: typeof op.signature.output.type === 'string'
95
+ ? op.signature.output.type
96
+ : 'dynamic',
97
+ cardinality: typeof op.signature.output.cardinality === 'string'
98
+ ? (op.signature.output.cardinality === 'all-singleton' ? 'singleton' : op.signature.output.cardinality as any)
99
+ : undefined
100
+ };
101
+ }
102
+
103
+ // Add description if available (for now, operations don't have description in the type)
104
+ // TODO: Add description to Operation type if needed
105
+
106
+ // Add examples if available (for now, operations don't have examples in the type)
107
+ // TODO: Add examples to Operation type if needed
108
+
109
+ return info;
110
+ }
111
+
112
+ private extractTypes(constraint?: any): string[] | undefined {
113
+ if (!constraint) return undefined;
114
+
115
+ if (constraint.kind === 'primitive' || constraint.kind === 'class') {
116
+ return constraint.types;
117
+ } else if (constraint.kind === 'union' && constraint.types) {
118
+ return constraint.types;
119
+ } else if (constraint.kind === 'any') {
120
+ return ['any'];
121
+ }
122
+
123
+ return undefined;
124
+ }
125
+ }
126
+
127
+ // Singleton instance
128
+ export const publicRegistry = new PublicRegistryAPI();