Skip to main content

shape_runtime/semantic/
pattern_analysis.rs

1//! Pattern analysis and query validation
2//!
3//! This module handles analysis of pattern definitions and query constructs.
4
5use shape_ast::ast::{DestructurePattern, Query, Span, Spanned};
6use shape_ast::error::Result;
7
8use super::types;
9
10/// Implementation of pattern analysis methods for SemanticAnalyzer
11impl super::SemanticAnalyzer {
12    /// Check pattern assignment (variables must exist)
13    pub(super) fn check_pattern_assignment(
14        &mut self,
15        pattern: &DestructurePattern,
16        expr_type: types::Type,
17    ) -> Result<()> {
18        match pattern {
19            DestructurePattern::Identifier(name, _) => {
20                if self.symbol_table.lookup_variable(name).is_some() {
21                    // Variable exists, update it
22                    self.symbol_table.update_variable(name, expr_type)?;
23                } else {
24                    // Variable doesn't exist, create it as 'var' for backward compatibility
25                    self.symbol_table.define_variable(
26                        name,
27                        expr_type,
28                        shape_ast::ast::VarKind::Var,
29                        true,
30                    )?;
31                }
32            }
33            DestructurePattern::Array(patterns) => {
34                // For array destructuring in assignment
35                for pattern in patterns {
36                    self.check_pattern_assignment(pattern, expr_type.clone())?;
37                }
38            }
39            DestructurePattern::Object(fields) => {
40                // For object destructuring in assignment
41                for field in fields {
42                    self.check_pattern_assignment(&field.pattern, expr_type.clone())?;
43                }
44            }
45            DestructurePattern::Rest(pattern) => {
46                self.check_pattern_assignment(pattern, expr_type)?;
47            }
48            DestructurePattern::Decomposition(bindings) => {
49                // Decomposition assigns component types from an intersection
50                for binding in bindings {
51                    if self.symbol_table.lookup_variable(&binding.name).is_some() {
52                        self.symbol_table
53                            .update_variable(&binding.name, expr_type.clone())?;
54                    } else {
55                        self.symbol_table.define_variable(
56                            &binding.name,
57                            expr_type.clone(),
58                            shape_ast::ast::VarKind::Var,
59                            true,
60                        )?;
61                    }
62                }
63            }
64        }
65        Ok(())
66    }
67
68    /// Analyze a query
69    pub(super) fn analyze_query(&mut self, query: &Query, span: Span) -> Result<()> {
70        match query {
71            Query::Backtest(_) => {
72                // Backtest validation would check strategy exists
73            }
74            Query::Alert(alert) => {
75                // Alert condition must be boolean
76                let cond_type = self.check_expr_type(&alert.condition)?;
77                if cond_type != types::Type::Bool {
78                    return Err(self.error_at(
79                        alert.condition.span(),
80                        format!("Alert condition must be boolean, got {:?}", cond_type),
81                    ));
82                }
83            }
84            Query::With(with_query) => {
85                // Analyze CTEs
86                for cte in &with_query.ctes {
87                    self.analyze_query(&cte.query, span)?;
88                }
89                // Analyze main query
90                self.analyze_query(&with_query.query, span)?;
91            }
92        }
93
94        Ok(())
95    }
96
97    /// Get list of all available patterns (user-defined + built-in)
98    pub fn list_available_patterns(&self) -> Vec<String> {
99        let mut patterns = self.pattern_library.pattern_names();
100        patterns.sort();
101        patterns.dedup();
102        patterns
103    }
104}