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    /// Merge additional type schemas into the context's registry.
108    /// Used after compilation to make inline object schemas available for wire serialization.
109    pub fn merge_type_schemas(&mut self, other: TypeSchemaRegistry) {
110        Arc::make_mut(&mut self.type_schema_registry).merge(other);
111    }
112
113    // =========================================================================
114    // Enum Registry Methods (for sum types)
115    // =========================================================================
116
117    /// Register an enum definition
118    ///
119    /// This enables sum type support by tracking which enums exist and their variants.
120    /// Called during semantic analysis when processing `enum` declarations.
121    pub fn register_enum(&mut self, enum_def: shape_ast::ast::EnumDef) {
122        self.enum_registry.register(enum_def);
123    }
124
125    /// Look up an enum definition by name
126    pub fn lookup_enum(&self, name: &str) -> Option<&shape_ast::ast::EnumDef> {
127        self.enum_registry.get(name)
128    }
129
130    /// Check if an enum exists
131    pub fn has_enum(&self, name: &str) -> bool {
132        self.enum_registry.contains(name)
133    }
134
135    /// Get the enum registry (for advanced queries)
136    pub fn enum_registry(&self) -> &super::EnumRegistry {
137        &self.enum_registry
138    }
139
140    // =========================================================================
141    // Struct Type Registry Methods (for REPL persistence)
142    // =========================================================================
143
144    /// Register a struct type definition for REPL persistence
145    ///
146    /// This stores the full StructTypeDef so that type definitions survive across
147    /// REPL sessions. When a new REPL command is compiled, previously registered
148    /// struct types are injected into the program so the compiler can see them.
149    pub fn register_struct_type(&mut self, struct_def: shape_ast::ast::StructTypeDef) {
150        self.struct_type_registry
151            .insert(struct_def.name.clone(), struct_def);
152    }
153
154    /// Get all registered struct type definitions
155    ///
156    /// Returns an iterator over all struct type definitions registered in previous
157    /// REPL sessions. Used to inject them into the program before compilation.
158    pub fn struct_type_defs(
159        &self,
160    ) -> &std::collections::HashMap<String, shape_ast::ast::StructTypeDef> {
161        &self.struct_type_registry
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::super::ExecutionContext;
168    use shape_ast::ast::{
169        Annotation, AnnotationDef, AnnotationHandler, AnnotationHandlerType, Expr, FunctionDef,
170        Span,
171    };
172
173    #[test]
174    fn test_register_annotation_definition() {
175        let mut ctx = ExecutionContext::new_empty();
176
177        // Create a simple annotation definition (no handlers for this test)
178        let ann_def = AnnotationDef {
179            name: "test_annotation".to_string(),
180            name_span: Span::DUMMY,
181            doc_comment: None,
182            params: vec![],
183            allowed_targets: None,
184            handlers: vec![],
185            span: Span::DUMMY,
186        };
187
188        ctx.register_annotation(ann_def);
189
190        // Verify the annotation is registered
191        assert!(ctx.annotation_registry.contains("test_annotation"));
192    }
193
194    #[test]
195    fn test_function_without_annotations_registers_normally() {
196        let mut ctx = ExecutionContext::new_empty();
197
198        let func = FunctionDef {
199            name: "my_func".to_string(),
200            name_span: Span::DUMMY,
201            declaring_module_path: None,
202            doc_comment: None,
203            type_params: None,
204            params: vec![],
205            return_type: None,
206            body: vec![],
207            annotations: vec![],
208            where_clause: None,
209            is_async: false,
210            is_comptime: false,
211        };
212
213        ctx.register_function(func);
214
215        // TODO: Update test when BytecodeExecutor/VM integration is complete
216        // Function should be registered in evaluator
217        // assert!(ctx.evaluator().get_function("my_func").is_some());
218    }
219
220    #[test]
221    fn test_function_with_undefined_annotation_no_crash() {
222        let mut ctx = ExecutionContext::new_empty();
223
224        // Function has @undefined annotation but no annotation definition registered
225        let func = FunctionDef {
226            name: "annotated_func".to_string(),
227            name_span: Span::DUMMY,
228            declaring_module_path: None,
229            doc_comment: None,
230            type_params: None,
231            params: vec![],
232            return_type: None,
233            body: vec![],
234            annotations: vec![Annotation {
235                name: "undefined".to_string(),
236                args: vec![],
237                span: Span::DUMMY,
238            }],
239            where_clause: None,
240            is_async: false,
241            is_comptime: false,
242        };
243
244        // Should not crash - undefined annotations are silently ignored
245        ctx.register_function(func);
246
247        // TODO: Update test when BytecodeExecutor/VM integration is complete
248        // Function should still be registered
249        // assert!(ctx.evaluator().get_function("annotated_func").is_some());
250    }
251
252    #[test]
253    fn test_annotation_with_on_define_handler_is_called() {
254        let mut ctx = ExecutionContext::new_empty();
255
256        // Create an annotation definition with on_define handler
257        // The handler body sets a value in the annotation context state
258        // For simplicity, we use a literal expression that doesn't require complex evaluation
259        let ann_def = AnnotationDef {
260            name: "tracked".to_string(),
261            name_span: Span::DUMMY,
262            doc_comment: None,
263            params: vec![],
264            allowed_targets: None,
265            handlers: vec![AnnotationHandler {
266                handler_type: AnnotationHandlerType::OnDefine,
267                params: vec![shape_ast::ast::AnnotationHandlerParam {
268                    name: "fn".to_string(),
269                    is_variadic: false,
270                }],
271                return_type: None,
272                // Simple expression that just returns the fn parameter
273                // This tests that the handler is called and fn is bound
274                body: Expr::Identifier("fn".to_string(), Span::DUMMY),
275                span: Span::DUMMY,
276            }],
277            span: Span::DUMMY,
278        };
279
280        ctx.register_annotation(ann_def);
281
282        // Create a function with @tracked annotation
283        let func = FunctionDef {
284            name: "tracked_func".to_string(),
285            name_span: Span::DUMMY,
286            declaring_module_path: None,
287            doc_comment: None,
288            type_params: None,
289            params: vec![],
290            return_type: None,
291            body: vec![],
292            annotations: vec![Annotation {
293                name: "tracked".to_string(),
294                args: vec![],
295                span: Span::DUMMY,
296            }],
297            where_clause: None,
298            is_async: false,
299            is_comptime: false,
300        };
301
302        // Register function - this should trigger on_define handler
303        ctx.register_function(func);
304
305        // TODO: Update test when BytecodeExecutor/VM integration is complete
306        // Function should be registered
307        // assert!(ctx.evaluator().get_function("tracked_func").is_some());
308    }
309
310    #[test]
311    fn test_lookup_pattern_not_found() {
312        let ctx = ExecutionContext::new_empty();
313
314        let result = ctx.lookup_pattern("nonexistent");
315        assert!(result.is_err());
316
317        let err = result.unwrap_err();
318        let msg = format!("{}", err);
319        assert!(msg.contains("Unknown pattern"));
320        assert!(msg.contains("nonexistent"));
321    }
322
323    #[test]
324    fn test_annotation_context_registry_access() {
325        let mut ctx = ExecutionContext::new_empty();
326
327        // Access and modify the annotation context registry
328        {
329            let registry = ctx.annotation_context_mut().registry("test_registry");
330            registry.set(
331                "key1".to_string(),
332                shape_value::ValueWord::from_string(std::sync::Arc::new("value1".to_string())),
333            );
334        }
335
336        // Verify the value is stored
337        let registry = ctx.annotation_context_mut().registry("test_registry");
338        assert!(registry.get("key1").is_some());
339    }
340}