Skip to main content

microcad_lang/lower/
mod.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Lowering compile step
5
6pub mod ir;
7
8#[allow(clippy::module_inception)]
9mod lower;
10
11use microcad_lang_base::{
12    ComputedHash, DiagResult, Diagnostic, Diagnostics, Hashed, Identifier, LineIndex, PushDiag,
13    Refer, Span, SrcRef, SrcReferrer,
14};
15
16pub use lower::{LowerError, LowerErrorsWithSource, LowerResult};
17
18/// Check if the element only includes one identifier
19pub trait SingleIdentifier {
20    /// If the element only includes one identifier, return it
21    fn single_identifier(&self) -> Option<&Identifier>;
22
23    /// Returns true if the element only includes a single identifier.
24    fn is_single_identifier(&self) -> bool {
25        self.single_identifier().is_some()
26    }
27}
28
29/// Identifier accessor.
30pub trait Identifiable {
31    /// Get clone of the identifier.
32    fn id(&self) -> Identifier {
33        self.id_ref().clone()
34    }
35
36    /// Get reference to the identifier.
37    fn id_ref(&self) -> &Identifier;
38
39    /// Get identifier as string.
40    fn id_as_str(&self) -> &str {
41        self.id_ref().0.as_str()
42    }
43}
44
45/// Interface for elements which have *initializers*.
46pub trait Initialized<'a> {
47    /// return iterator of body statements.
48    fn statements(&'a self) -> std::slice::Iter<'a, ir::Statement>;
49
50    /// Return iterator over all initializers.
51    fn inits(&'a self) -> ir::Inits<'a>
52    where
53        Self: std::marker::Sized,
54    {
55        ir::Inits::new(self)
56    }
57}
58
59pub struct LowerContext<'source> {
60    pub source: Hashed<&'source str>,
61    line_index: LineIndex,
62    line_offset: u32,
63    diagnostics: Diagnostics,
64}
65
66impl<'source> LowerContext<'source> {
67    pub fn new(source: &'source str) -> Self {
68        LowerContext {
69            source: Hashed::new(source),
70            line_index: LineIndex::new(source),
71            line_offset: 0,
72            diagnostics: Diagnostics::default(),
73        }
74    }
75
76    pub fn with_line_offset(self, line_offset: u32) -> Self {
77        Self {
78            source: self.source,
79            line_index: self.line_index,
80            line_offset,
81            diagnostics: Diagnostics::default(),
82        }
83    }
84
85    pub fn src_ref(&self, span: &Span) -> SrcRef {
86        self.line_index
87            .src_ref(self.source.value(), span, self.source.computed_hash())
88            .with_line_offset(self.line_offset)
89    }
90
91    // Use `impl PushDiag` here
92    pub fn warning(&mut self, diagnostic: LowerError) -> DiagResult<()> {
93        let src_ref = diagnostic.src_ref();
94        self.diagnostics
95            .push_diag(Diagnostic::Warning(std::rc::Rc::new(Refer::new(
96                diagnostic.into(),
97                src_ref,
98            ))))
99    }
100}
101
102pub trait Lower: Sized {
103    type AstNode;
104
105    fn lower(node: &Self::AstNode, context: &mut LowerContext) -> Result<Self, LowerError>;
106}