Skip to main content

shape_ast/ast/
types.rs

1//! Type system definitions for Shape AST
2
3use super::DocComment;
4use super::functions::Annotation;
5use super::span::Span;
6use super::type_path::TypePath;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub enum TypeAnnotation {
11    /// Basic types: number, string, bool, row, pattern, etc.
12    Basic(String),
13    /// Vector type: T[] or Vec<T>
14    Array(Box<TypeAnnotation>),
15    /// Tuple type: [T1, T2, T3]
16    Tuple(Vec<TypeAnnotation>),
17    /// Object type: { field1: T1, field2?: T2 }
18    Object(Vec<ObjectTypeField>),
19    /// Function type: (T1, T2) => T3
20    Function {
21        params: Vec<FunctionParam>,
22        returns: Box<TypeAnnotation>,
23    },
24    /// Union type: T1 | T2 | T3 (discriminated union - value is ONE of the types)
25    Union(Vec<TypeAnnotation>),
26    /// Intersection type: T1 + T2 (structural merge - value has ALL fields from both types)
27    /// Only valid for object/interface types. Field collisions are compile-time errors.
28    Intersection(Vec<TypeAnnotation>),
29    /// Generic type: Map<K, V>
30    Generic {
31        name: TypePath,
32        args: Vec<TypeAnnotation>,
33    },
34    /// Type reference (custom type or type alias)
35    Reference(TypePath),
36    /// Void type
37    Void,
38    /// Never type
39    Never,
40    /// Null type
41    Null,
42    /// Undefined type
43    Undefined,
44    /// Trait object type: dyn Trait1 + Trait2
45    /// Represents a type-erased value that implements the given traits
46    Dyn(Vec<TypePath>),
47}
48
49impl TypeAnnotation {
50    pub fn option(inner: TypeAnnotation) -> Self {
51        TypeAnnotation::Generic {
52            name: TypePath::simple("Option"),
53            args: vec![inner],
54        }
55    }
56
57    pub fn option_inner(&self) -> Option<&TypeAnnotation> {
58        match self {
59            TypeAnnotation::Generic { name, args }
60                if name.as_str() == "Option" && args.len() == 1 =>
61            {
62                args.first()
63            }
64            _ => None,
65        }
66    }
67
68    pub fn into_option_inner(self) -> Option<TypeAnnotation> {
69        match self {
70            TypeAnnotation::Generic { name, mut args }
71                if name.as_str() == "Option" && args.len() == 1 =>
72            {
73                Some(args.remove(0))
74            }
75            _ => None,
76        }
77    }
78
79    pub fn is_option(&self) -> bool {
80        self.option_inner().is_some()
81    }
82
83    /// Extract a simple type name if this is a Reference or Basic type
84    ///
85    /// Returns `Some(type_name)` for:
86    /// - `TypeAnnotation::Reference(path)` - e.g., `Currency`, `foo::MyType`
87    /// - `TypeAnnotation::Basic(name)` - e.g., `number`, `string`
88    ///
89    /// Returns `None` for complex types like arrays, tuples, functions, etc.
90    pub fn as_simple_name(&self) -> Option<&str> {
91        match self {
92            TypeAnnotation::Reference(path) => Some(path.as_str()),
93            TypeAnnotation::Basic(name) => Some(name.as_str()),
94            _ => None,
95        }
96    }
97
98    /// Extract the type name string for Basic or Reference variants.
99    /// Handles the `Basic(name) | Reference(path)` pattern uniformly.
100    pub fn as_type_name_str(&self) -> Option<&str> {
101        match self {
102            TypeAnnotation::Basic(name) => Some(name.as_str()),
103            TypeAnnotation::Reference(path) => Some(path.as_str()),
104            _ => None,
105        }
106    }
107
108    /// Convert a type annotation to its full string representation.
109    pub fn to_type_string(&self) -> String {
110        match self {
111            TypeAnnotation::Basic(name) => name.clone(),
112            TypeAnnotation::Reference(path) => path.to_string(),
113            TypeAnnotation::Array(inner) => format!("Array<{}>", inner.to_type_string()),
114            TypeAnnotation::Generic { name, args }
115                if name.as_str() == "Option" && args.len() == 1 =>
116            {
117                format!("{}?", args[0].to_type_string())
118            }
119            TypeAnnotation::Generic { name, args } => {
120                let args_str: Vec<String> = args.iter().map(|a| a.to_type_string()).collect();
121                format!("{}<{}>", name, args_str.join(", "))
122            }
123            TypeAnnotation::Tuple(items) => {
124                let items_str: Vec<String> = items.iter().map(|t| t.to_type_string()).collect();
125                format!("[{}]", items_str.join(", "))
126            }
127            TypeAnnotation::Union(items) => {
128                let items_str: Vec<String> = items.iter().map(|t| t.to_type_string()).collect();
129                items_str.join(" | ")
130            }
131            TypeAnnotation::Void => "void".to_string(),
132            TypeAnnotation::Never => "never".to_string(),
133            TypeAnnotation::Null => "null".to_string(),
134            TypeAnnotation::Undefined => "undefined".to_string(),
135            TypeAnnotation::Object(fields) => {
136                let fields_str: Vec<String> = fields
137                    .iter()
138                    .map(|f| {
139                        let opt = if f.optional { "?" } else { "" };
140                        // Include @alias in the type string so the Python extension
141                        // can use the wire name when looking up dict keys.
142                        let alias = f
143                            .annotations
144                            .iter()
145                            .find(|a| a.name == "alias")
146                            .and_then(|a| a.args.first())
147                            .and_then(|arg| match arg {
148                                super::expressions::Expr::Literal(
149                                    super::literals::Literal::String(s),
150                                    _,
151                                ) => Some(s.as_str()),
152                                _ => None,
153                            });
154                        let alias_str = alias.map(|a| format!("@\"{}\" ", a)).unwrap_or_default();
155                        format!(
156                            "{}{}{}: {}",
157                            alias_str,
158                            f.name,
159                            opt,
160                            f.type_annotation.to_type_string()
161                        )
162                    })
163                    .collect();
164                format!("{{{}}}", fields_str.join(", "))
165            }
166            _ => "any".to_string(),
167        }
168    }
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub struct ObjectTypeField {
173    pub name: String,
174    pub optional: bool,
175    pub type_annotation: TypeAnnotation,
176    /// Field annotations (e.g. `@alias("wire name")`)
177    #[serde(default)]
178    pub annotations: Vec<Annotation>,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct FunctionParam {
183    pub name: Option<String>,
184    pub optional: bool,
185    pub type_annotation: TypeAnnotation,
186}
187
188/// A generic parameter on a function, type, trait or impl header.
189///
190/// Two variants:
191/// - [`TypeParam::Type`] — the classic type-level parameter (`<T>`,
192///   `<T: Bound>`, `<T = DefaultType>`).
193/// - [`TypeParam::Const`] — a value-level const generic parameter
194///   (`<const N: int>`, `<const N: int = 4>`). Added in B.2 of the
195///   const-generics track; B.3 binds const args at monomorphization
196///   and B.4 substitutes them into specialized bodies. Until then
197///   most consumers treat const params as a placeholder — the AST
198///   captures the name, declared type, and optional default expression
199///   so later phases can wire them end-to-end.
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub enum TypeParam {
202    /// A type parameter: `T`, `T: Bound`, `T = DefaultType`.
203    Type {
204        name: String,
205        #[serde(default)]
206        span: Span,
207        #[serde(default)]
208        doc_comment: Option<DocComment>,
209        /// Default type argument: `T = int`
210        default_type: Option<TypeAnnotation>,
211        /// Trait bounds: `T: Comparable + Displayable`
212        #[serde(default)]
213        trait_bounds: Vec<TypePath>,
214    },
215    /// A const generic parameter: `const N: int`, `const N: int = 4`.
216    ///
217    /// Parsed in B.1/B.2. Monomorphization (B.3) and body substitution
218    /// (B.4) are not yet implemented — most compiler passes currently
219    /// treat this variant as a stub / placeholder.
220    Const {
221        name: String,
222        #[serde(default)]
223        span: Span,
224        #[serde(default)]
225        doc_comment: Option<DocComment>,
226        /// Declared type: `const N: int`.
227        ty: TypeAnnotation,
228        /// Optional default value expression: `= 4`.
229        default: Option<super::expressions::Expr>,
230    },
231}
232
233impl TypeParam {
234    /// The parameter's declared name (`T` or `N`), common to both variants.
235    pub fn name(&self) -> &str {
236        match self {
237            TypeParam::Type { name, .. } | TypeParam::Const { name, .. } => name,
238        }
239    }
240
241    /// The parameter's source span, common to both variants.
242    pub fn span(&self) -> &Span {
243        match self {
244            TypeParam::Type { span, .. } | TypeParam::Const { span, .. } => span,
245        }
246    }
247
248    /// The parameter's attached doc comment, common to both variants.
249    pub fn doc_comment(&self) -> Option<&DocComment> {
250        match self {
251            TypeParam::Type { doc_comment, .. } | TypeParam::Const { doc_comment, .. } => {
252                doc_comment.as_ref()
253            }
254        }
255    }
256
257    /// Trait bounds, only present on the `Type` variant.
258    ///
259    /// Returns an empty slice for const generics (they have no bounds).
260    pub fn trait_bounds(&self) -> &[TypePath] {
261        match self {
262            TypeParam::Type { trait_bounds, .. } => trait_bounds.as_slice(),
263            // TODO(B.3): const generics may eventually grow predicates
264            // (e.g. `where N > 0`), but not in this phase.
265            TypeParam::Const { .. } => &[],
266        }
267    }
268
269    /// Default type argument, only meaningful for the `Type` variant.
270    pub fn default_type(&self) -> Option<&TypeAnnotation> {
271        match self {
272            TypeParam::Type { default_type, .. } => default_type.as_ref(),
273            TypeParam::Const { .. } => None,
274        }
275    }
276
277    /// True iff this is a const generic parameter.
278    pub fn is_const(&self) -> bool {
279        matches!(self, TypeParam::Const { .. })
280    }
281
282    /// Convenience constructor for a plain type parameter with no bounds
283    /// or default — the shape that stdlib bootstrap / method desugaring
284    /// most often needs.
285    pub fn simple(name: impl Into<String>) -> Self {
286        TypeParam::Type {
287            name: name.into(),
288            span: Span::DUMMY,
289            doc_comment: None,
290            default_type: None,
291            trait_bounds: Vec::new(),
292        }
293    }
294}
295
296/// A predicate in a where clause: `T: Comparable + Display`
297#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
298pub struct WherePredicate {
299    pub type_name: String,
300    pub bounds: Vec<TypePath>,
301}
302
303impl PartialEq for TypeParam {
304    fn eq(&self, other: &Self) -> bool {
305        match (self, other) {
306            (
307                TypeParam::Type {
308                    name: ln,
309                    doc_comment: ld,
310                    default_type: ldt,
311                    trait_bounds: ltb,
312                    ..
313                },
314                TypeParam::Type {
315                    name: rn,
316                    doc_comment: rd,
317                    default_type: rdt,
318                    trait_bounds: rtb,
319                    ..
320                },
321            ) => ln == rn && ld == rd && ldt == rdt && ltb == rtb,
322            (
323                TypeParam::Const {
324                    name: ln,
325                    doc_comment: ld,
326                    ty: lty,
327                    default: ldef,
328                    ..
329                },
330                TypeParam::Const {
331                    name: rn,
332                    doc_comment: rd,
333                    ty: rty,
334                    default: rdef,
335                    ..
336                },
337            ) => ln == rn && ld == rd && lty == rty && ldef == rdef,
338            _ => false,
339        }
340    }
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct TypeAliasDef {
345    pub name: String,
346    #[serde(default)]
347    pub doc_comment: Option<DocComment>,
348    pub type_params: Option<Vec<TypeParam>>,
349    pub type_annotation: TypeAnnotation,
350    /// Meta parameter overrides: type Percent4 = Percent { decimals: 4 }
351    pub meta_param_overrides: Option<std::collections::HashMap<String, super::expressions::Expr>>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct InterfaceDef {
356    pub name: String,
357    #[serde(default)]
358    pub doc_comment: Option<DocComment>,
359    pub type_params: Option<Vec<TypeParam>>,
360    pub members: Vec<InterfaceMember>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub enum InterfaceMember {
365    /// Property signature
366    Property {
367        name: String,
368        optional: bool,
369        type_annotation: TypeAnnotation,
370        #[serde(default)]
371        span: Span,
372        #[serde(default)]
373        doc_comment: Option<DocComment>,
374    },
375    /// Method signature
376    Method {
377        name: String,
378        optional: bool,
379        params: Vec<FunctionParam>,
380        return_type: TypeAnnotation,
381        /// Whether this is an async method
382        is_async: bool,
383        #[serde(default)]
384        span: Span,
385        #[serde(default)]
386        doc_comment: Option<DocComment>,
387    },
388    /// Index signature
389    IndexSignature {
390        param_name: String,
391        param_type: String, // "string" or "number"
392        return_type: TypeAnnotation,
393        #[serde(default)]
394        span: Span,
395        #[serde(default)]
396        doc_comment: Option<DocComment>,
397    },
398}
399
400impl InterfaceMember {
401    pub fn span(&self) -> Span {
402        match self {
403            InterfaceMember::Property { span, .. }
404            | InterfaceMember::Method { span, .. }
405            | InterfaceMember::IndexSignature { span, .. } => *span,
406        }
407    }
408
409    pub fn doc_comment(&self) -> Option<&DocComment> {
410        match self {
411            InterfaceMember::Property { doc_comment, .. }
412            | InterfaceMember::Method { doc_comment, .. }
413            | InterfaceMember::IndexSignature { doc_comment, .. } => doc_comment.as_ref(),
414        }
415    }
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct EnumDef {
420    pub name: String,
421    #[serde(default)]
422    pub doc_comment: Option<DocComment>,
423    pub type_params: Option<Vec<TypeParam>>,
424    pub members: Vec<EnumMember>,
425    /// Annotations applied to the enum (e.g., `@with_label() enum Color { ... }`)
426    #[serde(default)]
427    pub annotations: Vec<super::Annotation>,
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct EnumMember {
432    pub name: String,
433    pub kind: EnumMemberKind,
434    #[serde(default)]
435    pub span: Span,
436    #[serde(default)]
437    pub doc_comment: Option<DocComment>,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub enum EnumMemberKind {
442    /// Unit variant: Variant or Variant = 1
443    Unit { value: Option<EnumValue> },
444    /// Tuple variant: Variant(Type, Type)
445    Tuple(Vec<TypeAnnotation>),
446    /// Struct variant: Variant { field: Type, ... }
447    Struct(Vec<ObjectTypeField>),
448}
449
450#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
451pub enum EnumValue {
452    String(String),
453    Number(f64),
454}
455
456/// A member of a trait definition: either required (signature only) or default (with body)
457#[derive(Debug, Clone, Serialize, Deserialize)]
458pub enum TraitMember {
459    /// Required method — implementors must provide this
460    Required(InterfaceMember),
461    /// Default method — used if implementor does not override
462    Default(MethodDef),
463    /// Associated type declaration: `type Item;` or `type Item: Comparable;`
464    AssociatedType {
465        name: String,
466        bounds: Vec<TypeAnnotation>,
467        #[serde(default)]
468        span: Span,
469        #[serde(default)]
470        doc_comment: Option<DocComment>,
471    },
472}
473
474impl TraitMember {
475    pub fn span(&self) -> Span {
476        match self {
477            TraitMember::Required(member) => member.span(),
478            TraitMember::Default(method) => method.span,
479            TraitMember::AssociatedType { span, .. } => *span,
480        }
481    }
482
483    pub fn doc_comment(&self) -> Option<&DocComment> {
484        match self {
485            TraitMember::Required(member) => member.doc_comment(),
486            TraitMember::Default(method) => method.doc_comment.as_ref(),
487            TraitMember::AssociatedType { doc_comment, .. } => doc_comment.as_ref(),
488        }
489    }
490}
491
492/// A concrete binding for an associated type inside an `impl` block:
493/// `type Item = number;`
494#[derive(Debug, Clone, Serialize, Deserialize)]
495pub struct AssociatedTypeBinding {
496    pub name: String,
497    pub concrete_type: TypeAnnotation,
498}
499
500/// Trait definition — like interface but with `trait` keyword, supporting default methods
501///
502/// ```shape
503/// trait Queryable<T> {
504///     filter(predicate: (T) => bool): Self    // required
505///     method execute() -> Result<Table<T>> {   // default
506///         return Ok(self.filter(|_| true))
507///     }
508/// }
509/// ```
510#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct TraitDef {
512    pub name: String,
513    #[serde(default)]
514    pub doc_comment: Option<DocComment>,
515    pub type_params: Option<Vec<TypeParam>>,
516    /// Supertrait bounds: `trait Foo: Bar + Baz { ... }`
517    #[serde(default)]
518    pub super_traits: Vec<TypeAnnotation>,
519    pub members: Vec<TraitMember>,
520    /// Annotations applied to the trait (e.g., `@documented("...") trait Foo { ... }`)
521    #[serde(default)]
522    pub annotations: Vec<super::Annotation>,
523}
524
525/// Impl block — implements a trait for a type
526///
527/// ```shape
528/// impl Queryable<T> for Table<T> {
529///     method filter(predicate) { /* ... */ }
530///     method execute() { Ok(self) }
531/// }
532/// ```
533///
534/// Under the hood, compiles identically to an extend block (UFCS desugaring)
535/// plus trait validation (all required methods present with correct arities).
536#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct ImplBlock {
538    /// The trait being implemented (e.g., Queryable<T>)
539    pub trait_name: TypeName,
540    /// The type implementing the trait (e.g., Table<T>)
541    pub target_type: TypeName,
542    /// Optional named implementation selector:
543    /// `impl Display for User as JsonDisplay { ... }`
544    pub impl_name: Option<String>,
545    /// Method implementations
546    pub methods: Vec<MethodDef>,
547    /// Associated type bindings: `type Item = number;`
548    pub associated_type_bindings: Vec<AssociatedTypeBinding>,
549    /// Where clause: `where T: Display + Comparable`
550    pub where_clause: Option<Vec<WherePredicate>>,
551}
552
553/// Type extension for adding methods to existing types
554#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
555pub struct ExtendStatement {
556    /// The type being extended (e.g., "Vec")
557    pub type_name: TypeName,
558    /// Methods being added to the type
559    pub methods: Vec<MethodDef>,
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct MethodDef {
564    /// Method name
565    pub name: String,
566    #[serde(default)]
567    pub span: Span,
568    /// Declaring module path for compiler/runtime provenance checks.
569    ///
570    /// This is injected by the module loader for loaded modules and is not part
571    /// of user-authored source syntax.
572    #[serde(default, skip_serializing_if = "Option::is_none")]
573    pub declaring_module_path: Option<String>,
574    #[serde(default)]
575    pub doc_comment: Option<DocComment>,
576    /// Annotations applied to this method (e.g., `@traced`)
577    #[serde(default)]
578    pub annotations: Vec<super::functions::Annotation>,
579    /// Type parameters for generic methods (e.g., `method map<U>(...)`)
580    #[serde(default)]
581    pub type_params: Option<Vec<TypeParam>>,
582    /// Method parameters
583    pub params: Vec<super::functions::FunctionParameter>,
584    /// Optional when clause for conditional method definitions
585    pub when_clause: Option<Box<super::expressions::Expr>>,
586    /// Optional return type annotation
587    pub return_type: Option<TypeAnnotation>,
588    /// Method body
589    pub body: Vec<super::statements::Statement>,
590    /// Whether this is an async method
591    pub is_async: bool,
592}
593
594impl PartialEq for MethodDef {
595    fn eq(&self, other: &Self) -> bool {
596        self.name == other.name
597            && self.doc_comment == other.doc_comment
598            && self.annotations == other.annotations
599            && self.type_params == other.type_params
600            && self.params == other.params
601            && self.when_clause == other.when_clause
602            && self.return_type == other.return_type
603            && self.body == other.body
604            && self.is_async == other.is_async
605    }
606}
607
608#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
609pub enum TypeName {
610    /// Simple type name (e.g., "Vec", "Table", "foo::Bar")
611    Simple(TypePath),
612    /// Generic type name (e.g., "Table<Row>")
613    Generic {
614        name: TypePath,
615        type_args: Vec<TypeAnnotation>,
616    },
617}
618
619// ============================================================================
620// Struct Type Definitions
621// ============================================================================
622
623/// Struct type definition — pure data with named fields
624///
625/// ```shape
626/// type Point { x: number, y: number }
627/// type DataVec<V, K = Timestamp> { index: Vec<K>, data: Vec<V> }
628/// ```
629#[derive(Debug, Clone, Serialize, Deserialize)]
630pub struct StructTypeDef {
631    pub name: String,
632    #[serde(default)]
633    pub doc_comment: Option<DocComment>,
634    pub type_params: Option<Vec<TypeParam>>,
635    pub fields: Vec<StructField>,
636    /// Inline method definitions inside the type body
637    #[serde(default)]
638    pub methods: Vec<MethodDef>,
639    /// Annotations applied to the struct (e.g., `@derive_debug type Foo { ... }`)
640    pub annotations: Vec<Annotation>,
641    /// Optional native layout metadata for `type C`.
642    #[serde(default)]
643    pub native_layout: Option<NativeLayoutBinding>,
644}
645
646/// Native layout binding metadata for `type C` declarations.
647#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
648pub struct NativeLayoutBinding {
649    /// ABI name (currently `"C"`).
650    pub abi: String,
651}
652
653/// A field in a struct type definition
654///
655/// Comptime fields are type-level constants baked at compile time.
656/// They occupy zero runtime slots (no ValueSlot in TypedObject).
657/// Access resolves to a constant push at compile time.
658///
659/// ```shape
660/// type Currency {
661///     comptime symbol: string = "$",
662///     comptime decimals: number = 2,
663///     amount: number,
664/// }
665/// ```
666#[derive(Debug, Clone, Serialize, Deserialize)]
667pub struct StructField {
668    pub annotations: Vec<Annotation>,
669    pub is_comptime: bool,
670    pub name: String,
671    #[serde(default)]
672    pub span: Span,
673    #[serde(default)]
674    pub doc_comment: Option<DocComment>,
675    pub type_annotation: TypeAnnotation,
676    pub default_value: Option<super::expressions::Expr>,
677}
678
679impl PartialEq for StructField {
680    fn eq(&self, other: &Self) -> bool {
681        self.annotations == other.annotations
682            && self.is_comptime == other.is_comptime
683            && self.name == other.name
684            && self.doc_comment == other.doc_comment
685            && self.type_annotation == other.type_annotation
686            && self.default_value == other.default_value
687    }
688}