shape_runtime/annotation_context.rs
1//! Annotation context and decorator registry
2//!
3//! This module provides the infrastructure for Shape's annotation system.
4//!
5//! ## Design Philosophy
6//!
7//! Annotations in Shape are **fully defined in Shape stdlib**, not hardcoded in Rust.
8//! This module provides the generic runtime primitives that annotation lifecycle hooks use.
9//!
10//! ## Annotation Lifecycle Hooks
11//!
12//! Annotations can define handlers for different lifecycle events:
13//! - `on_define(fn, ctx)` - Called when function is first defined
14//! - `before(fn, args, ctx)` - Called before each function invocation
15//! - `after(fn, args, result, ctx)` - Called after each function invocation
16//! - `metadata()` - Static metadata for tooling and optimization
17//!
18//! ## Example (stdlib/finance/annotations/pattern.shape)
19//!
20//! ```shape
21//! annotation pattern() {
22//! on_define(fn, ctx) {
23//! ctx.registry("patterns").set(fn.name, fn);
24//! }
25//! metadata() { return { is_pattern: true }; }
26//! }
27//! ```
28//!
29//! ## Runtime Primitives
30//!
31//! The `AnnotationContext` provides domain-agnostic primitives:
32//! - `cache` - Key-value cache for memoization
33//! - `state` - Per-annotation persistent state
34//! - `registry(name)` - Named registries (patterns, strategies, features, etc.)
35//! - `emit(event, data)` - Event emission for alerts, logging
36//! - `data` - Data range manipulation (extend/restore for warmup)
37
38use shape_value::ValueWord;
39use std::collections::HashMap;
40
41// ============================================================================
42// Annotation Registry
43// ============================================================================
44
45/// Registry for annotation definitions
46///
47/// Stores `annotation ... { ... }` definitions that can be looked up by name.
48/// These definitions include lifecycle hooks (on_define, before, after, metadata).
49#[derive(Clone)]
50pub struct AnnotationRegistry {
51 annotations: HashMap<String, shape_ast::ast::AnnotationDef>,
52}
53
54impl AnnotationRegistry {
55 pub fn new() -> Self {
56 Self {
57 annotations: HashMap::new(),
58 }
59 }
60
61 pub fn register(&mut self, def: shape_ast::ast::AnnotationDef) {
62 self.annotations.insert(def.name.clone(), def);
63 }
64
65 pub fn get(&self, name: &str) -> Option<&shape_ast::ast::AnnotationDef> {
66 self.annotations.get(name)
67 }
68
69 pub fn contains(&self, name: &str) -> bool {
70 self.annotations.contains_key(name)
71 }
72}
73
74impl Default for AnnotationRegistry {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80// ============================================================================
81// Annotation Context - Runtime Primitives
82// ============================================================================
83
84/// Context passed to annotation lifecycle hooks
85///
86/// Provides domain-agnostic primitives that annotation handlers use.
87/// This is the `ctx` parameter in handlers like `on_define(fn, ctx)`.
88#[derive(Debug, Clone)]
89pub struct AnnotationContext {
90 /// Key-value cache for memoization (e.g., @cached, @indicator)
91 cache: AnnotationCache,
92 /// Per-annotation persistent state
93 state: AnnotationState,
94 /// Named registries (patterns, strategies, features, etc.)
95 registries: HashMap<String, NamedRegistry>,
96 /// Emitted events (for @alert, @logged annotations)
97 events: Vec<EmittedEvent>,
98 /// Data range manipulation state (for @warmup)
99 data_range: DataRangeState,
100}
101
102impl AnnotationContext {
103 pub fn new() -> Self {
104 Self {
105 cache: AnnotationCache::new(),
106 state: AnnotationState::new(),
107 registries: HashMap::new(),
108 events: Vec::new(),
109 data_range: DataRangeState::new(),
110 }
111 }
112
113 /// Get the cache for memoization
114 pub fn cache(&self) -> &AnnotationCache {
115 &self.cache
116 }
117
118 /// Get mutable cache for memoization
119 pub fn cache_mut(&mut self) -> &mut AnnotationCache {
120 &mut self.cache
121 }
122
123 /// Get the per-annotation state
124 pub fn state(&self) -> &AnnotationState {
125 &self.state
126 }
127
128 /// Get mutable per-annotation state
129 pub fn state_mut(&mut self) -> &mut AnnotationState {
130 &mut self.state
131 }
132
133 /// Get or create a named registry
134 pub fn registry(&mut self, name: &str) -> &mut NamedRegistry {
135 self.registries.entry(name.to_string()).or_default()
136 }
137
138 /// Emit an event (for alerts, logging, etc.)
139 pub fn emit(&mut self, event_type: &str, data: ValueWord) {
140 self.events.push(EmittedEvent {
141 event_type: event_type.to_string(),
142 data,
143 timestamp: std::time::Instant::now(),
144 });
145 }
146
147 /// Get all emitted events
148 pub fn events(&self) -> &[EmittedEvent] {
149 &self.events
150 }
151
152 /// Clear emitted events
153 pub fn clear_events(&mut self) {
154 self.events.clear();
155 }
156
157 /// Get data range manipulation state
158 pub fn data_range(&self) -> &DataRangeState {
159 &self.data_range
160 }
161
162 /// Get mutable data range manipulation state
163 pub fn data_range_mut(&mut self) -> &mut DataRangeState {
164 &mut self.data_range
165 }
166}
167
168impl Default for AnnotationContext {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174// ============================================================================
175// Cache Primitive
176// ============================================================================
177
178/// Key-value cache for annotation memoization
179///
180/// Used by annotations like @cached, @indicator, @memo
181#[derive(Debug, Clone, Default)]
182pub struct AnnotationCache {
183 entries: HashMap<String, CacheEntry>,
184}
185
186#[derive(Debug, Clone)]
187pub struct CacheEntry {
188 pub value: ValueWord,
189 pub created_at: std::time::Instant,
190}
191
192impl AnnotationCache {
193 pub fn new() -> Self {
194 Self {
195 entries: HashMap::new(),
196 }
197 }
198
199 /// Get a cached value by key as ValueWord reference
200 pub fn get(&self, key: &str) -> Option<&ValueWord> {
201 self.entries.get(key).map(|e| &e.value)
202 }
203
204 /// Get a cached entry (includes metadata)
205 pub fn get_entry(&self, key: &str) -> Option<&CacheEntry> {
206 self.entries.get(key)
207 }
208
209 /// Set a cached value
210 pub fn set(&mut self, key: String, value: ValueWord) {
211 self.entries.insert(
212 key,
213 CacheEntry {
214 value,
215 created_at: std::time::Instant::now(),
216 },
217 );
218 }
219
220 /// Check if key exists
221 pub fn contains(&self, key: &str) -> bool {
222 self.entries.contains_key(key)
223 }
224
225 /// Remove a cached value
226 pub fn remove(&mut self, key: &str) -> Option<ValueWord> {
227 self.entries.remove(key).map(|e| e.value)
228 }
229
230 /// Clear all cached values
231 pub fn clear(&mut self) {
232 self.entries.clear();
233 }
234}
235
236// ============================================================================
237// State Primitive
238// ============================================================================
239
240/// Per-annotation persistent state
241///
242/// Used for annotations that need to maintain state across calls
243#[derive(Debug, Clone, Default)]
244pub struct AnnotationState {
245 values: HashMap<String, ValueWord>,
246}
247
248impl AnnotationState {
249 pub fn new() -> Self {
250 Self {
251 values: HashMap::new(),
252 }
253 }
254
255 /// Get a state value as ValueWord reference
256 pub fn get(&self, key: &str) -> Option<&ValueWord> {
257 self.values.get(key)
258 }
259
260 pub fn set(&mut self, key: String, value: ValueWord) {
261 self.values.insert(key, value);
262 }
263
264 pub fn contains(&self, key: &str) -> bool {
265 self.values.contains_key(key)
266 }
267
268 pub fn remove(&mut self, key: &str) -> Option<ValueWord> {
269 self.values.remove(key)
270 }
271
272 pub fn clear(&mut self) {
273 self.values.clear();
274 }
275}
276
277// ============================================================================
278// Registry Primitive
279// ============================================================================
280
281/// A named registry for storing values (functions, patterns, etc.)
282///
283/// Used by custom annotations (e.g. @strategy, @feature)
284#[derive(Debug, Clone, Default)]
285pub struct NamedRegistry {
286 entries: HashMap<String, ValueWord>,
287}
288
289impl NamedRegistry {
290 pub fn new() -> Self {
291 Self {
292 entries: HashMap::new(),
293 }
294 }
295
296 /// Get a registry value as ValueWord reference
297 pub fn get(&self, key: &str) -> Option<&ValueWord> {
298 self.entries.get(key)
299 }
300
301 pub fn set(&mut self, key: String, value: ValueWord) {
302 self.entries.insert(key, value);
303 }
304
305 pub fn contains(&self, key: &str) -> bool {
306 self.entries.contains_key(key)
307 }
308
309 pub fn remove(&mut self, key: &str) -> Option<ValueWord> {
310 self.entries.remove(key)
311 }
312
313 pub fn keys(&self) -> impl Iterator<Item = &String> {
314 self.entries.keys()
315 }
316
317 /// Iterate over values as ValueWord references
318 pub fn values(&self) -> impl Iterator<Item = &ValueWord> {
319 self.entries.values()
320 }
321
322 pub fn len(&self) -> usize {
323 self.entries.len()
324 }
325
326 pub fn is_empty(&self) -> bool {
327 self.entries.is_empty()
328 }
329}
330
331// ============================================================================
332// Event Emission Primitive
333// ============================================================================
334
335/// An emitted event from an annotation handler
336///
337/// Used by annotations like @alert, @logged, @audit_logged
338#[derive(Debug, Clone)]
339pub struct EmittedEvent {
340 pub event_type: String,
341 pub data: ValueWord,
342 pub timestamp: std::time::Instant,
343}
344
345// ============================================================================
346// Data Range Manipulation
347// ============================================================================
348
349/// State for data range manipulation (used by @warmup)
350///
351/// Allows annotations to extend the data range (e.g., for warmup periods)
352/// and then restore it after processing.
353#[derive(Debug, Clone, Default)]
354pub struct DataRangeState {
355 /// Original data range start (if extended)
356 original_start: Option<usize>,
357 /// Original data range end (if extended)
358 original_end: Option<usize>,
359 /// Amount the range was extended
360 extension_amount: Option<usize>,
361}
362
363impl DataRangeState {
364 pub fn new() -> Self {
365 Self {
366 original_start: None,
367 original_end: None,
368 extension_amount: None,
369 }
370 }
371
372 /// Record the original range before extending
373 pub fn save_original(&mut self, start: usize, end: usize) {
374 self.original_start = Some(start);
375 self.original_end = Some(end);
376 }
377
378 /// Record how much the range was extended
379 pub fn set_extension(&mut self, amount: usize) {
380 self.extension_amount = Some(amount);
381 }
382
383 /// Get the original start position
384 pub fn original_start(&self) -> Option<usize> {
385 self.original_start
386 }
387
388 /// Get the original end position
389 pub fn original_end(&self) -> Option<usize> {
390 self.original_end
391 }
392
393 /// Get the extension amount
394 pub fn extension_amount(&self) -> Option<usize> {
395 self.extension_amount
396 }
397
398 /// Check if range is currently extended
399 pub fn is_extended(&self) -> bool {
400 self.extension_amount.is_some()
401 }
402
403 /// Clear the saved state (after restoring)
404 pub fn clear(&mut self) {
405 self.original_start = None;
406 self.original_end = None;
407 self.extension_amount = None;
408 }
409}
410
411// ============================================================================
412// Annotation Processor
413// ============================================================================