Skip to main content

tensorlogic_ir/
term.rs

1//! Terms: variables and constants.
2
3use serde::{Deserialize, Serialize};
4
5use crate::parametric_types::ParametricType;
6
7/// Type annotation for a term.
8///
9/// This is the simple string-based type annotation. For parametric types,
10/// use [`ParametricType`] which supports type constructors and variables.
11#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct TypeAnnotation {
13    pub type_name: String,
14}
15
16impl TypeAnnotation {
17    pub fn new(type_name: impl Into<String>) -> Self {
18        TypeAnnotation {
19            type_name: type_name.into(),
20        }
21    }
22
23    /// Convert to a parametric type (concrete type)
24    pub fn to_parametric(&self) -> ParametricType {
25        ParametricType::concrete(&self.type_name)
26    }
27
28    /// Create from a parametric type if it's a concrete type
29    pub fn from_parametric(ty: &ParametricType) -> Option<Self> {
30        match ty {
31            ParametricType::Concrete(name) => Some(TypeAnnotation::new(name.clone())),
32            _ => None, // Can't convert parametric or variable types
33        }
34    }
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
38pub enum Term {
39    Var(String),
40    Const(String),
41    /// Typed term with explicit type annotation
42    Typed {
43        value: Box<Term>,
44        type_annotation: TypeAnnotation,
45    },
46}
47
48impl Term {
49    pub fn var(name: impl Into<String>) -> Self {
50        Term::Var(name.into())
51    }
52
53    pub fn constant(name: impl Into<String>) -> Self {
54        Term::Const(name.into())
55    }
56
57    /// Create a typed variable
58    pub fn typed_var(name: impl Into<String>, type_name: impl Into<String>) -> Self {
59        Term::Typed {
60            value: Box::new(Term::Var(name.into())),
61            type_annotation: TypeAnnotation::new(type_name),
62        }
63    }
64
65    /// Create a typed constant
66    pub fn typed_const(name: impl Into<String>, type_name: impl Into<String>) -> Self {
67        Term::Typed {
68            value: Box::new(Term::Const(name.into())),
69            type_annotation: TypeAnnotation::new(type_name),
70        }
71    }
72
73    /// Attach a type annotation to an existing term
74    pub fn with_type(self, type_name: impl Into<String>) -> Self {
75        Term::Typed {
76            value: Box::new(self),
77            type_annotation: TypeAnnotation::new(type_name),
78        }
79    }
80
81    pub fn is_var(&self) -> bool {
82        match self {
83            Term::Var(_) => true,
84            Term::Typed { value, .. } => value.is_var(),
85            _ => false,
86        }
87    }
88
89    pub fn is_const(&self) -> bool {
90        match self {
91            Term::Const(_) => true,
92            Term::Typed { value, .. } => value.is_const(),
93            _ => false,
94        }
95    }
96
97    pub fn name(&self) -> &str {
98        match self {
99            Term::Var(n) | Term::Const(n) => n,
100            Term::Typed { value, .. } => value.name(),
101        }
102    }
103
104    /// Get the type annotation if present
105    pub fn get_type(&self) -> Option<&TypeAnnotation> {
106        match self {
107            Term::Typed {
108                type_annotation, ..
109            } => Some(type_annotation),
110            _ => None,
111        }
112    }
113
114    /// Get the underlying untyped term
115    pub fn untyped(&self) -> &Term {
116        match self {
117            Term::Typed { value, .. } => value.untyped(),
118            term => term,
119        }
120    }
121}