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