Skip to main content

shape_runtime/context/
registries.rs

1//! Registry access methods for ExecutionContext
2//!
3//! Handles type methods, type schemas, pattern registries,
4//! and annotation lifecycle dispatch.
5
6use super::super::annotation_context::AnnotationContext;
7use super::super::type_methods::TypeMethodRegistry;
8use super::super::type_schema::TypeSchemaRegistry;
9use shape_ast::ast::{AnnotationDef, AnnotationHandlerType, FunctionDef};
10use shape_ast::error::{Result, ShapeError};
11use std::sync::Arc;
12
13impl super::ExecutionContext {
14    /// Register an annotation definition
15    ///
16    /// Annotation definitions are stored and used to dispatch lifecycle hooks
17    /// when functions with those annotations are registered.
18    pub fn register_annotation(&mut self, def: AnnotationDef) {
19        self.annotation_registry.register(def);
20    }
21
22    /// Get the annotation context (for lifecycle hooks)
23    pub fn annotation_context(&self) -> &AnnotationContext {
24        &self.annotation_context
25    }
26
27    /// Get mutable annotation context (for lifecycle hooks)
28    pub fn annotation_context_mut(&mut self) -> &mut AnnotationContext {
29        &mut self.annotation_context
30    }
31
32    /// Register a user-defined function
33    ///
34    /// This dispatches `on_define` lifecycle hooks for all annotations on the function.
35    pub fn register_function(&mut self, function: FunctionDef) {
36        self.dispatch_on_define_hooks(&function);
37    }
38
39    /// Dispatch on_define lifecycle hooks for all annotations on a function
40    ///
41    /// For each annotation on the function:
42    /// 1. Look up the annotation definition in the registry
43    /// 2. If it has an on_define handler, execute it
44    fn dispatch_on_define_hooks(&mut self, func: &FunctionDef) {
45        for annotation in &func.annotations {
46            // Look up the annotation definition
47            if let Some(ann_def) = self.annotation_registry.get(&annotation.name).cloned() {
48                // Find the on_define handler
49                for handler in &ann_def.handlers {
50                    if handler.handler_type == AnnotationHandlerType::OnDefine {
51                        // Execute the on_define handler
52                        self.execute_on_define_handler(&ann_def, handler, func);
53                    }
54                }
55            }
56            // If annotation definition not found, no hooks are executed
57            // Annotations must be defined to have behavior
58        }
59    }
60
61    /// Execute an on_define lifecycle handler
62    ///
63    /// The handler body is a Shape expression that will be evaluated
64    /// with `fn` and `ctx` bound to the function and annotation context.
65    /// Currently a stub until VM-based closure handling is implemented.
66    fn execute_on_define_handler(
67        &mut self,
68        _ann_def: &AnnotationDef,
69        _handler: &shape_ast::ast::AnnotationHandler,
70        _func: &FunctionDef,
71    ) {
72        self.sync_pattern_registry_from_annotation_context();
73    }
74
75    /// Sync pattern registry from annotation context
76    ///
77    /// When an annotation's on_define calls ctx.registry("patterns").set(...),
78    /// we need to copy those entries to the main pattern_registry for .find() lookup.
79    /// Currently a stub until VM-based closure handling is implemented.
80    fn sync_pattern_registry_from_annotation_context(&mut self) {}
81
82    /// Look up a pattern by name from the pattern registry
83    ///
84    /// Returns an error with a helpful message if the pattern is not found.
85    pub fn lookup_pattern(&self, name: &str) -> Result<&super::super::closure::Closure> {
86        self.pattern_registry
87            .get(name)
88            .ok_or_else(|| ShapeError::RuntimeError {
89                message: format!(
90                    "Unknown pattern: '{}'. Did you register it in the pattern registry?",
91                    name
92                ),
93                location: None,
94            })
95    }
96
97    /// Get the type method registry
98    pub fn type_method_registry(&self) -> &Arc<TypeMethodRegistry> {
99        &self.type_method_registry
100    }
101
102    /// Get the type schema registry for JIT type specialization
103    pub fn type_schema_registry(&self) -> &Arc<TypeSchemaRegistry> {
104        &self.type_schema_registry
105    }
106
107    // =========================================================================
108    // Enum Registry Methods (for sum types)
109    // =========================================================================
110
111    /// Register an enum definition
112    ///
113    /// This enables sum type support by tracking which enums exist and their variants.
114    /// Called during semantic analysis when processing `enum` declarations.
115    pub fn register_enum(&mut self, enum_def: shape_ast::ast::EnumDef) {
116        self.enum_registry.register(enum_def);
117    }
118
119    /// Look up an enum definition by name
120    pub fn lookup_enum(&self, name: &str) -> Option<&shape_ast::ast::EnumDef> {
121        self.enum_registry.get(name)
122    }
123
124    /// Check if an enum exists
125    pub fn has_enum(&self, name: &str) -> bool {
126        self.enum_registry.contains(name)
127    }
128
129    /// Get the enum registry (for advanced queries)
130    pub fn enum_registry(&self) -> &super::EnumRegistry {
131        &self.enum_registry
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::super::ExecutionContext;
138    use shape_ast::ast::{
139        Annotation, AnnotationDef, AnnotationHandler, AnnotationHandlerType, Expr, FunctionDef,
140        Span,
141    };
142
143    #[test]
144    fn test_register_annotation_definition() {
145        let mut ctx = ExecutionContext::new_empty();
146
147        // Create a simple annotation definition (no handlers for this test)
148        let ann_def = AnnotationDef {
149            name: "test_annotation".to_string(),
150            name_span: Span::DUMMY,
151            doc_comment: None,
152            params: vec![],
153            allowed_targets: None,
154            handlers: vec![],
155            span: Span::DUMMY,
156        };
157
158        ctx.register_annotation(ann_def);
159
160        // Verify the annotation is registered
161        assert!(ctx.annotation_registry.contains("test_annotation"));
162    }
163
164    #[test]
165    fn test_function_without_annotations_registers_normally() {
166        let mut ctx = ExecutionContext::new_empty();
167
168        let func = FunctionDef {
169            name: "my_func".to_string(),
170            name_span: Span::DUMMY,
171            doc_comment: None,
172            type_params: None,
173            params: vec![],
174            return_type: None,
175            body: vec![],
176            annotations: vec![],
177            where_clause: None,
178            is_async: false,
179            is_comptime: false,
180        };
181
182        ctx.register_function(func);
183
184        // TODO: Update test when BytecodeExecutor/VM integration is complete
185        // Function should be registered in evaluator
186        // assert!(ctx.evaluator().get_function("my_func").is_some());
187    }
188
189    #[test]
190    fn test_function_with_undefined_annotation_no_crash() {
191        let mut ctx = ExecutionContext::new_empty();
192
193        // Function has @undefined annotation but no annotation definition registered
194        let func = FunctionDef {
195            name: "annotated_func".to_string(),
196            name_span: Span::DUMMY,
197            doc_comment: None,
198            type_params: None,
199            params: vec![],
200            return_type: None,
201            body: vec![],
202            annotations: vec![Annotation {
203                name: "undefined".to_string(),
204                args: vec![],
205                span: Span::DUMMY,
206            }],
207            where_clause: None,
208            is_async: false,
209            is_comptime: false,
210        };
211
212        // Should not crash - undefined annotations are silently ignored
213        ctx.register_function(func);
214
215        // TODO: Update test when BytecodeExecutor/VM integration is complete
216        // Function should still be registered
217        // assert!(ctx.evaluator().get_function("annotated_func").is_some());
218    }
219
220    #[test]
221    fn test_annotation_with_on_define_handler_is_called() {
222        let mut ctx = ExecutionContext::new_empty();
223
224        // Create an annotation definition with on_define handler
225        // The handler body sets a value in the annotation context state
226        // For simplicity, we use a literal expression that doesn't require complex evaluation
227        let ann_def = AnnotationDef {
228            name: "tracked".to_string(),
229            name_span: Span::DUMMY,
230            doc_comment: None,
231            params: vec![],
232            allowed_targets: None,
233            handlers: vec![AnnotationHandler {
234                handler_type: AnnotationHandlerType::OnDefine,
235                params: vec![shape_ast::ast::AnnotationHandlerParam {
236                    name: "fn".to_string(),
237                    is_variadic: false,
238                }],
239                return_type: None,
240                // Simple expression that just returns the fn parameter
241                // This tests that the handler is called and fn is bound
242                body: Expr::Identifier("fn".to_string(), Span::DUMMY),
243                span: Span::DUMMY,
244            }],
245            span: Span::DUMMY,
246        };
247
248        ctx.register_annotation(ann_def);
249
250        // Create a function with @tracked annotation
251        let func = FunctionDef {
252            name: "tracked_func".to_string(),
253            name_span: Span::DUMMY,
254            doc_comment: None,
255            type_params: None,
256            params: vec![],
257            return_type: None,
258            body: vec![],
259            annotations: vec![Annotation {
260                name: "tracked".to_string(),
261                args: vec![],
262                span: Span::DUMMY,
263            }],
264            where_clause: None,
265            is_async: false,
266            is_comptime: false,
267        };
268
269        // Register function - this should trigger on_define handler
270        ctx.register_function(func);
271
272        // TODO: Update test when BytecodeExecutor/VM integration is complete
273        // Function should be registered
274        // assert!(ctx.evaluator().get_function("tracked_func").is_some());
275    }
276
277    #[test]
278    fn test_lookup_pattern_not_found() {
279        let ctx = ExecutionContext::new_empty();
280
281        let result = ctx.lookup_pattern("nonexistent");
282        assert!(result.is_err());
283
284        let err = result.unwrap_err();
285        let msg = format!("{}", err);
286        assert!(msg.contains("Unknown pattern"));
287        assert!(msg.contains("nonexistent"));
288    }
289
290    #[test]
291    fn test_annotation_context_registry_access() {
292        let mut ctx = ExecutionContext::new_empty();
293
294        // Access and modify the annotation context registry
295        {
296            let registry = ctx.annotation_context_mut().registry("test_registry");
297            registry.set(
298                "key1".to_string(),
299                shape_value::ValueWord::from_string(std::sync::Arc::new("value1".to_string())),
300            );
301        }
302
303        // Verify the value is stored
304        let registry = ctx.annotation_context_mut().registry("test_registry");
305        assert!(registry.get("key1").is_some());
306    }
307}