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    Duration,
268    Timestamp,
269    Decimal,
270}
271
272impl TyStd {
273    pub fn try_new(ident: &Path) -> Option<Self> {
274        Self::try_from_steps(&ident.0)
275    }
276
277    pub fn try_from_steps(steps: &[string::String]) -> Option<Self> {
278        if steps.len() != 2 && steps.first().unwrap() != "std" {
279            return None;
280        }
281        Some(match steps[1].as_str() {
282            "Bool" => Self::Bool,
283            "Int8" => Self::Int8,
284            "Int16" => Self::Int16,
285            "Int32" => Self::Int32,
286            "Int64" => Self::Int64,
287            "Uint8" => Self::UInt8,
288            "Uint16" => Self::UInt16,
289            "Uint32" => Self::UInt32,
290            "Uint64" => Self::UInt64,
291            "Float32" => Self::Float32,
292            "Float64" => Self::Float64,
293            "Text" => Self::Text,
294            "Date" => Self::Date,
295            "Time" => Self::Time,
296            "Duration" => Self::Duration,
297            "Timestamp" => Self::Timestamp,
298            "Decimal" => Self::Decimal,
299            _ => return None,
300        })
301    }
302
303    pub fn is_int(self) -> bool {
304        use TyStd::*;
305        matches!(
306            self,
307            Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64
308        )
309    }
310
311    pub fn is_signed_int(self) -> bool {
312        matches!(
313            self,
314            TyStd::Int8
315                | TyStd::Int16
316                | TyStd::Int32
317                | TyStd::Int64
318                | TyStd::Duration
319                | TyStd::Timestamp
320        )
321    }
322
323    pub fn is_float(self) -> bool {
324        matches!(self, TyStd::Float32 | TyStd::Float64)
325    }
326
327    pub fn is_number(self) -> bool {
328        self.is_int() || self.is_float() || self == Self::Decimal
329    }
330
331    pub fn bits(self) -> u32 {
332        match self {
333            TyStd::Int8 | TyStd::UInt8 => 8,
334            TyStd::Int16 | TyStd::UInt16 => 16,
335            TyStd::Int32 | TyStd::UInt32 | TyStd::Float32 => 32,
336            TyStd::Int64
337            | TyStd::UInt64
338            | TyStd::Float64
339            | TyStd::Duration
340            | TyStd::Time
341            | TyStd::Timestamp => 64,
342            _ => 0,
343        }
344    }
345}