Skip to main content

lutra_bin/ir/
mod.rs

1mod literal;
2mod printer;
3
4pub use crate::generated::ir::*;
5#[cfg(feature = "std")]
6pub use printer::{print, print_no_color, print_ty};
7
8use crate::{boxed, string, vec};
9
10impl Program {
11    pub fn get_output_ty(&self) -> &Ty {
12        let main_ty = self.main.ty.kind.as_function().unwrap();
13        &main_ty.body
14    }
15
16    pub fn get_input_ty(&self) -> &Ty {
17        let main_ty = self.main.ty.kind.as_function().unwrap();
18        assert_eq!(main_ty.params.len(), 1);
19        &main_ty.params[0]
20    }
21}
22
23impl Expr {
24    pub fn new(kind: impl Into<ExprKind>, ty: Ty) -> Expr {
25        Expr {
26            kind: kind.into(),
27            ty,
28        }
29    }
30    pub fn new_lit_bool(value: bool) -> Self {
31        Expr {
32            kind: ExprKind::Literal(Literal::Prim8(value as u8)),
33            ty: Ty::bool(),
34        }
35    }
36}
37impl From<ParameterPtr> for ExprKind {
38    fn from(ptr: ParameterPtr) -> Self {
39        ExprKind::Pointer(Pointer::Parameter(ptr))
40    }
41}
42impl From<ExternalPtr> for ExprKind {
43    fn from(ptr: ExternalPtr) -> Self {
44        ExprKind::Pointer(Pointer::External(ptr))
45    }
46}
47impl From<TupleLookup> for ExprKind {
48    fn from(v: TupleLookup) -> Self {
49        ExprKind::TupleLookup(boxed::Box::new(v))
50    }
51}
52impl From<Binding> for ExprKind {
53    fn from(v: Binding) -> Self {
54        ExprKind::Binding(boxed::Box::new(v))
55    }
56}
57impl From<Call> for ExprKind {
58    fn from(v: Call) -> Self {
59        ExprKind::Call(boxed::Box::new(v))
60    }
61}
62impl From<Function> for ExprKind {
63    fn from(v: Function) -> Self {
64        ExprKind::Function(boxed::Box::new(v))
65    }
66}
67impl From<EnumTag> for ExprKind {
68    fn from(v: EnumTag) -> Self {
69        ExprKind::EnumTag(boxed::Box::new(v))
70    }
71}
72impl From<EnumUnwrap> for ExprKind {
73    fn from(value: EnumUnwrap) -> Self {
74        ExprKind::EnumUnwrap(boxed::Box::new(value))
75    }
76}
77
78impl PartialEq for Ty {
79    fn eq(&self, other: &Self) -> bool {
80        self.kind == other.kind
81    }
82}
83
84impl Eq for Ty {}
85
86impl Ty {
87    pub fn new(kind: impl Into<TyKind>) -> Self {
88        Ty {
89            kind: kind.into(),
90            layout: None,
91            name: None,
92            variants_recursive: vec![],
93        }
94    }
95    pub fn new_ident(path: &[&str]) -> Self {
96        use string::ToString;
97        Ty::new(Path(path.iter().map(|n| n.to_string()).collect()))
98    }
99    pub fn bool() -> Self {
100        Ty::new_ident(&["std", "Bool"])
101    }
102    pub fn text() -> Self {
103        Ty::new_ident(&["std", "Text"])
104    }
105    pub fn new_unit() -> Self {
106        Ty {
107            kind: TyKind::Tuple(vec![]),
108            layout: Some(TyLayout {
109                head_size: 0,
110                body_ptrs: vec![],
111            }),
112            name: None,
113            variants_recursive: vec![],
114        }
115    }
116    pub fn is_unit(&self) -> bool {
117        self.kind.is_unit()
118    }
119}
120
121impl TyKind {
122    pub fn is_unit(&self) -> bool {
123        self.as_tuple().is_some_and(|f| f.is_empty())
124    }
125
126    pub fn as_option(&self) -> Option<&Ty> {
127        self.as_enum()
128            .filter(|v| v.len() == 2 && v[0].ty.is_unit() && !v[1].ty.is_unit())
129            .map(|v| &v[1].ty)
130    }
131
132    pub fn as_std(&self) -> Option<TyStd> {
133        self.as_ident().and_then(TyStd::try_new)
134    }
135}
136
137impl From<TyPrimitive> for TyKind {
138    fn from(value: TyPrimitive) -> Self {
139        TyKind::Primitive(value)
140    }
141}
142impl From<vec::Vec<TyTupleField>> for TyKind {
143    fn from(value: vec::Vec<TyTupleField>) -> Self {
144        TyKind::Tuple(value)
145    }
146}
147impl From<TyFunction> for TyKind {
148    fn from(value: TyFunction) -> Self {
149        TyKind::Function(boxed::Box::new(value))
150    }
151}
152impl From<Path> for TyKind {
153    fn from(value: Path) -> Self {
154        TyKind::Ident(value)
155    }
156}
157
158impl Module {
159    pub fn insert(&mut self, path: &[string::String], decl: Decl) {
160        if path.is_empty() {
161            panic!();
162        }
163
164        if path.len() == 1 {
165            self.decls.retain(|d| d.name != path[0]);
166            self.decls.push(ModuledeclsItems {
167                name: path[0].clone(),
168                decl,
169            });
170        } else {
171            let exists = self.decls.iter().any(|d| d.name == path[0]);
172            if !exists {
173                self.decls.push(ModuledeclsItems {
174                    name: path[0].clone(),
175                    decl: Decl::Mod(boxed::Box::new(Module {
176                        decls: vec::Vec::new(),
177                    })),
178                });
179            }
180
181            let sub_module = self.decls.iter_mut().find(|d| d.name == path[0]);
182            let Decl::Mod(sub_module) = &mut sub_module.unwrap().decl else {
183                panic!()
184            };
185            sub_module.insert(&path[1..], decl)
186        }
187    }
188
189    pub fn iter_defs_re(&self) -> impl Iterator<Item = (Path, &Decl)> {
190        self.decls.iter().flat_map(|item| match &item.decl {
191            Decl::Mod(sub_module) => sub_module
192                .iter_defs_re()
193                .map(|(mut p, d)| {
194                    p.0.insert(0, item.name.clone());
195                    (p, d)
196                })
197                .collect::<vec::Vec<_>>(),
198            _ => {
199                vec![(Path(vec![item.name.clone()]), &item.decl)]
200            }
201        })
202    }
203
204    pub fn iter_types_re(&self) -> impl Iterator<Item = (Path, &Ty)> {
205        self.iter_defs_re().filter_map(|(p, d)| {
206            if let Decl::Ty(ty) = d {
207                Some((p, ty))
208            } else {
209                None
210            }
211        })
212    }
213}
214
215impl Path {
216    pub fn is(&self, name: &[&str]) -> bool {
217        self.0.len() == name.len() && core::iter::zip(&self.0, name).all(|(a, b)| a == b)
218    }
219}
220
221impl Literal {
222    pub fn new_int16(value: i16) -> Self {
223        Self::Prim16(value.to_le() as u16)
224    }
225    pub fn new_int32(value: i32) -> Self {
226        Self::Prim32(value.to_le() as u32)
227    }
228    pub fn new_int64(value: i64) -> Self {
229        Self::Prim64(value.to_le() as u64)
230    }
231}
232
233impl TupleLookup {
234    pub fn new(base: Expr, position: u16) -> Self {
235        Self { base, position }
236    }
237}
238
239impl TyPrimitive {
240    pub fn name(self) -> &'static str {
241        match self {
242            Self::Prim8 => "Prim8",
243            Self::Prim16 => "Prim16",
244            Self::Prim32 => "Prim32",
245            Self::Prim64 => "Prim64",
246        }
247    }
248}
249
250/// A helper enum for dealing with std types.
251#[derive(Clone, Copy, Debug, Eq, PartialEq)]
252pub enum TyStd {
253    Bool,
254    Int8,
255    Int16,
256    Int32,
257    Int64,
258    UInt8,
259    UInt16,
260    UInt32,
261    UInt64,
262    Float32,
263    Float64,
264    Text,
265    Date,
266    Time,
267    Timestamp,
268    Decimal,
269}
270
271impl TyStd {
272    pub fn try_new(ident: &Path) -> Option<Self> {
273        Self::try_from_steps(&ident.0)
274    }
275
276    pub fn try_from_steps(steps: &[string::String]) -> Option<Self> {
277        if steps.len() != 2 && steps.first().unwrap() != "std" {
278            return None;
279        }
280        Some(match steps[1].as_str() {
281            "Bool" => Self::Bool,
282            "Int8" => Self::Int8,
283            "Int16" => Self::Int16,
284            "Int32" => Self::Int32,
285            "Int64" => Self::Int64,
286            "Uint8" => Self::UInt8,
287            "Uint16" => Self::UInt16,
288            "Uint32" => Self::UInt32,
289            "Uint64" => Self::UInt64,
290            "Float32" => Self::Float32,
291            "Float64" => Self::Float64,
292            "Text" => Self::Text,
293            "Date" => Self::Date,
294            "Time" => Self::Time,
295            "Timestamp" => Self::Timestamp,
296            "Decimal" => Self::Decimal,
297            _ => return None,
298        })
299    }
300
301    pub fn is_int(self) -> bool {
302        use TyStd::*;
303        matches!(
304            self,
305            Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64
306        )
307    }
308
309    pub fn is_signed_int(self) -> bool {
310        matches!(
311            self,
312            TyStd::Int8 | TyStd::Int16 | TyStd::Int32 | TyStd::Int64
313        )
314    }
315
316    pub fn is_float(self) -> bool {
317        matches!(self, TyStd::Float32 | TyStd::Float64)
318    }
319
320    pub fn is_number(self) -> bool {
321        self.is_int() || self.is_float() || self == Self::Decimal
322    }
323
324    pub fn bits(self) -> u32 {
325        match self {
326            TyStd::Int8 | TyStd::UInt8 => 8,
327            TyStd::Int16 | TyStd::UInt16 => 16,
328            TyStd::Int32 | TyStd::UInt32 | TyStd::Float32 => 32,
329            TyStd::Int64 | TyStd::UInt64 | TyStd::Float64 => 64,
330            _ => 0,
331        }
332    }
333}