fusabi_frontend/
typed_ast.rs

1//! Typed AST - Optional type-annotated AST representation
2//!
3//! This module provides typed versions of AST nodes that carry type information
4//! alongside the expression structure. This is useful for:
5//! - Type-directed code generation
6//! - Type-aware optimizations
7//! - Debugging and diagnostics
8//!
9//! The typed AST is optional - the compiler can work with or without type information.
10
11use crate::ast::{Expr, Pattern};
12use crate::types::Type;
13
14/// Span information for source code locations
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct Span {
17    /// Start offset in source
18    pub start: usize,
19    /// End offset in source
20    pub end: usize,
21    /// Line number (1-indexed)
22    pub line: usize,
23    /// Column number (1-indexed)
24    pub column: usize,
25}
26
27impl Span {
28    /// Create a new span
29    pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self {
30        Span {
31            start,
32            end,
33            line,
34            column,
35        }
36    }
37
38    /// Create a span from just offsets
39    pub fn from_offsets(start: usize, end: usize) -> Self {
40        Span {
41            start,
42            end,
43            line: 1,
44            column: 1,
45        }
46    }
47
48    /// Combine two spans into one spanning both
49    pub fn combine(&self, other: &Span) -> Span {
50        Span {
51            start: self.start.min(other.start),
52            end: self.end.max(other.end),
53            line: self.line.min(other.line),
54            column: self.column.min(other.column),
55        }
56    }
57}
58
59/// Expression with type annotation
60///
61/// Wraps an expression with its inferred/checked type and optional source location.
62#[derive(Debug, Clone)]
63pub struct TypedExpr {
64    /// The underlying expression
65    pub expr: Expr,
66    /// The type of this expression
67    pub ty: Type,
68    /// Optional source span
69    pub span: Option<Span>,
70}
71
72impl TypedExpr {
73    /// Create a new typed expression without span information
74    pub fn new(expr: Expr, ty: Type) -> Self {
75        TypedExpr {
76            expr,
77            ty,
78            span: None,
79        }
80    }
81
82    /// Create a new typed expression with span information
83    pub fn with_span(expr: Expr, ty: Type, span: Span) -> Self {
84        TypedExpr {
85            expr,
86            ty,
87            span: Some(span),
88        }
89    }
90
91    /// Get the type of this expression
92    pub fn get_type(&self) -> &Type {
93        &self.ty
94    }
95
96    /// Get the underlying expression
97    pub fn get_expr(&self) -> &Expr {
98        &self.expr
99    }
100
101    /// Get the span if available
102    pub fn get_span(&self) -> Option<&Span> {
103        self.span.as_ref()
104    }
105
106    /// Convert to untyped expression
107    pub fn into_expr(self) -> Expr {
108        self.expr
109    }
110}
111
112/// Pattern with type annotation
113///
114/// Wraps a pattern with its inferred/checked type.
115#[derive(Debug, Clone)]
116pub struct TypedPattern {
117    /// The underlying pattern
118    pub pattern: Pattern,
119    /// The type of this pattern
120    pub ty: Type,
121}
122
123impl TypedPattern {
124    /// Create a new typed pattern
125    pub fn new(pattern: Pattern, ty: Type) -> Self {
126        TypedPattern { pattern, ty }
127    }
128
129    /// Get the type of this pattern
130    pub fn get_type(&self) -> &Type {
131        &self.ty
132    }
133
134    /// Get the underlying pattern
135    pub fn get_pattern(&self) -> &Pattern {
136        &self.pattern
137    }
138
139    /// Convert to untyped pattern
140    pub fn into_pattern(self) -> Pattern {
141        self.pattern
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::ast::Literal;
149
150    #[test]
151    fn test_span_new() {
152        let span = Span::new(0, 10, 1, 1);
153        assert_eq!(span.start, 0);
154        assert_eq!(span.end, 10);
155        assert_eq!(span.line, 1);
156        assert_eq!(span.column, 1);
157    }
158
159    #[test]
160    fn test_span_from_offsets() {
161        let span = Span::from_offsets(5, 15);
162        assert_eq!(span.start, 5);
163        assert_eq!(span.end, 15);
164    }
165
166    #[test]
167    fn test_span_combine() {
168        let span1 = Span::new(0, 5, 1, 1);
169        let span2 = Span::new(10, 15, 1, 11);
170        let combined = span1.combine(&span2);
171        assert_eq!(combined.start, 0);
172        assert_eq!(combined.end, 15);
173    }
174
175    #[test]
176    fn test_typed_expr_new() {
177        let expr = Expr::Lit(Literal::Int(42));
178        let ty = Type::Int;
179        let typed = TypedExpr::new(expr.clone(), ty.clone());
180        assert_eq!(typed.get_type(), &ty);
181        assert_eq!(typed.get_expr(), &expr);
182        assert!(typed.get_span().is_none());
183    }
184
185    #[test]
186    fn test_typed_expr_with_span() {
187        let expr = Expr::Lit(Literal::Bool(true));
188        let ty = Type::Bool;
189        let span = Span::new(0, 4, 1, 1);
190        let typed = TypedExpr::with_span(expr, ty.clone(), span);
191        assert_eq!(typed.get_type(), &ty);
192        assert!(typed.get_span().is_some());
193    }
194
195    #[test]
196    fn test_typed_expr_into_expr() {
197        let expr = Expr::Lit(Literal::Int(42));
198        let ty = Type::Int;
199        let typed = TypedExpr::new(expr.clone(), ty);
200        let untyped = typed.into_expr();
201        assert_eq!(untyped, expr);
202    }
203
204    #[test]
205    fn test_typed_pattern_new() {
206        let pattern = Pattern::Wildcard;
207        let ty = Type::Int;
208        let typed = TypedPattern::new(pattern.clone(), ty.clone());
209        assert_eq!(typed.get_type(), &ty);
210        assert_eq!(typed.get_pattern(), &pattern);
211    }
212
213    #[test]
214    fn test_typed_pattern_into_pattern() {
215        let pattern = Pattern::Var("x".to_string());
216        let ty = Type::String;
217        let typed = TypedPattern::new(pattern.clone(), ty);
218        let untyped = typed.into_pattern();
219        assert_eq!(untyped, pattern);
220    }
221}