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            declaring_module_path: None,
172            doc_comment: None,
173            type_params: None,
174            params: vec![],
175            return_type: None,
176            body: vec![],
177            annotations: vec![],
178            where_clause: None,
179            is_async: false,
180            is_comptime: false,
181        };
182
183        ctx.register_function(func);
184
185        // TODO: Update test when BytecodeExecutor/VM integration is complete
186        // Function should be registered in evaluator
187        // assert!(ctx.evaluator().get_function("my_func").is_some());
188    }
189
190    #[test]
191    fn test_function_with_undefined_annotation_no_crash() {
192        let mut ctx = ExecutionContext::new_empty();
193
194        // Function has @undefined annotation but no annotation definition registered
195        let func = FunctionDef {
196            name: "annotated_func".to_string(),
197            name_span: Span::DUMMY,
198            declaring_module_path: None,
199            doc_comment: None,
200            type_params: None,
201            params: vec![],
202            return_type: None,
203            body: vec![],
204            annotations: vec![Annotation {
205                name: "undefined".to_string(),
206                args: vec![],
207                span: Span::DUMMY,
208            }],
209            where_clause: None,
210            is_async: false,
211            is_comptime: false,
212        };
213
214        // Should not crash - undefined annotations are silently ignored
215        ctx.register_function(func);
216
217        // TODO: Update test when BytecodeExecutor/VM integration is complete
218        // Function should still be registered
219        // assert!(ctx.evaluator().get_function("annotated_func").is_some());
220    }
221
222    #[test]
223    fn test_annotation_with_on_define_handler_is_called() {
224        let mut ctx = ExecutionContext::new_empty();
225
226        // Create an annotation definition with on_define handler
227        // The handler body sets a value in the annotation context state
228        // For simplicity, we use a literal expression that doesn't require complex evaluation
229        let ann_def = AnnotationDef {
230            name: "tracked".to_string(),
231            name_span: Span::DUMMY,
232            doc_comment: None,
233            params: vec![],
234            allowed_targets: None,
235            handlers: vec![AnnotationHandler {
236                handler_type: AnnotationHandlerType::OnDefine,
237                params: vec![shape_ast::ast::AnnotationHandlerParam {
238                    name: "fn".to_string(),
239                    is_variadic: false,
240                }],
241                return_type: None,
242                // Simple expression that just returns the fn parameter
243                // This tests that the handler is called and fn is bound
244                body: Expr::Identifier("fn".to_string(), Span::DUMMY),
245                span: Span::DUMMY,
246            }],
247            span: Span::DUMMY,
248        };
249
250        ctx.register_annotation(ann_def);
251
252        // Create a function with @tracked annotation
253        let func = FunctionDef {
254            name: "tracked_func".to_string(),
255            name_span: Span::DUMMY,
256            declaring_module_path: None,
257            doc_comment: None,
258            type_params: None,
259            params: vec![],
260            return_type: None,
261            body: vec![],
262            annotations: vec![Annotation {
263                name: "tracked".to_string(),
264                args: vec![],
265                span: Span::DUMMY,
266            }],
267            where_clause: None,
268            is_async: false,
269            is_comptime: false,
270        };
271
272        // Register function - this should trigger on_define handler
273        ctx.register_function(func);
274
275        // TODO: Update test when BytecodeExecutor/VM integration is complete
276        // Function should be registered
277        // assert!(ctx.evaluator().get_function("tracked_func").is_some());
278    }
279
280    #[test]
281    fn test_lookup_pattern_not_found() {
282        let ctx = ExecutionContext::new_empty();
283
284        let result = ctx.lookup_pattern("nonexistent");
285        assert!(result.is_err());
286
287        let err = result.unwrap_err();
288        let msg = format!("{}", err);
289        assert!(msg.contains("Unknown pattern"));
290        assert!(msg.contains("nonexistent"));
291    }
292
293    #[test]
294    fn test_annotation_context_registry_access() {
295        let mut ctx = ExecutionContext::new_empty();
296
297        // Access and modify the annotation context registry
298        {
299            let registry = ctx.annotation_context_mut().registry("test_registry");
300            registry.set(
301                "key1".to_string(),
302                shape_value::ValueWord::from_string(std::sync::Arc::new("value1".to_string())),
303            );
304        }
305
306        // Verify the value is stored
307        let registry = ctx.annotation_context_mut().registry("test_registry");
308        assert!(registry.get("key1").is_some());
309    }
310}