mimium_lang/
compiler.rs

1pub mod bytecodegen;
2pub(crate) mod intrinsics;
3pub mod mirgen;
4pub mod parser;
5pub mod typing;
6use crate::plugin::{ExtFunTypeInfo, MacroFunction};
7
8/// Stage information for multi-stage programming.
9/// Moved from plugin.rs to be shared across compiler modules.
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum EvalStage {
12    /// Persistent stage - accessible from all stages (like builtins)
13    Persistent,
14    /// Specific stage number
15    Stage(u8),
16}
17
18impl std::fmt::Display for EvalStage {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            EvalStage::Persistent => write!(f, "persistent"),
22            EvalStage::Stage(n) => write!(f, "{}", n),
23        }
24    }
25}
26
27impl EvalStage {
28    pub fn is_available_in_macro(&self) -> bool {
29        matches!(self, EvalStage::Persistent | EvalStage::Stage(0))
30    }
31    pub fn is_available_in_vm(&self) -> bool {
32        matches!(self, EvalStage::Persistent | EvalStage::Stage(1))
33    }
34
35    /// Format the stage for error messages
36    pub fn format_for_error(&self) -> String {
37        match self {
38            EvalStage::Persistent => "persistent".to_string(),
39            EvalStage::Stage(n) => n.to_string(),
40        }
41    }
42
43    /// Increment the current stage for bracket expressions
44    pub fn increment(self) -> EvalStage {
45        match self {
46            EvalStage::Persistent => EvalStage::Persistent, // Persistent stays persistent
47            EvalStage::Stage(n) => EvalStage::Stage(n + 1),
48        }
49    }
50
51    /// Decrement the current stage for escape expressions
52    pub fn decrement(self) -> EvalStage {
53        match self {
54            EvalStage::Persistent => EvalStage::Persistent, // Persistent stays persistent
55            EvalStage::Stage(n) => EvalStage::Stage(n.saturating_sub(1)),
56        }
57    }
58}
59
60#[derive(Debug, Clone)]
61pub enum ErrorKind {
62    TypeMismatch(Type, Type),
63    CircularType,
64    IndexOutOfRange(u16, u16),
65    IndexForNonTuple(Type),
66    VariableNotFound(String),
67    NonPrimitiveInFeed,
68    NotApplicable, //need?
69    IndexOutOfBounds,
70    TypeError,
71    Unknown,
72}
73#[derive(Debug, Clone)]
74pub struct Error(pub ErrorKind, pub Span);
75
76impl std::fmt::Display for ErrorKind {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            ErrorKind::VariableNotFound(_) => {
80                write!(f, "Variable Not Found.")
81            }
82            ErrorKind::TypeMismatch(expect, actual) => {
83                write!(
84                    f,
85                    "Type Mismatch, expected {expect}, but the actual was {actual}."
86                )
87            }
88            ErrorKind::IndexForNonTuple(t) => {
89                write!(f, "Index access for non tuple-type {t}.")
90            }
91            ErrorKind::IndexOutOfRange(r, a) => {
92                write!(
93                    f,
94                    "Tuple index out of range, number of elements are {r} but accessed with {a}."
95                )
96            }
97            ErrorKind::NotApplicable => {
98                write!(f, "Application to non-function type value.")
99            }
100            ErrorKind::CircularType => write!(f, "Circular loop of type definition"),
101            ErrorKind::NonPrimitiveInFeed => write!(f, "Feed can take only non-funtion type."),
102            ErrorKind::IndexOutOfBounds => write!(f, "Array index out of bounds."),
103            ErrorKind::TypeError => write!(f, "Type error in expression."),
104            ErrorKind::Unknown => write!(f, "unknwon error."),
105        }
106    }
107}
108impl std::fmt::Display for Error {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        self.0.fmt(f)
111    }
112}
113
114impl std::error::Error for Error {}
115
116impl ReportableError for Error {
117    fn get_labels(&self) -> Vec<(crate::utils::metadata::Location, String)> {
118        todo!()
119    }
120}
121
122use std::path::PathBuf;
123
124use mirgen::recursecheck;
125
126use crate::{
127    interner::{ExprNodeId, Symbol, TypeNodeId},
128    mir::Mir,
129    runtime::vm,
130    types::Type,
131    utils::{error::ReportableError, metadata::Span},
132};
133pub fn emit_ast(
134    src: &str,
135    path: Option<PathBuf>,
136) -> Result<ExprNodeId, Vec<Box<dyn ReportableError>>> {
137    let (ast, errs) = parser::parse_to_expr(src, path.clone());
138    if errs.is_empty() {
139        let ast = parser::add_global_context(ast, path.clone().unwrap_or_default());
140        let (ast, _errs) =
141            mirgen::convert_pronoun::convert_pronoun(ast, path.clone().unwrap_or_default());
142        Ok(recursecheck::convert_recurse(
143            ast,
144            path.clone().unwrap_or_default(),
145        ))
146    } else {
147        Err(errs)
148    }
149}
150
151#[derive(Clone, Copy, Debug, Default)]
152pub struct Config {
153    pub self_eval_mode: bytecodegen::SelfEvalMode,
154}
155
156pub struct Context {
157    ext_fns: Vec<ExtFunTypeInfo>,
158    macros: Vec<Box<dyn MacroFunction>>,
159    file_path: Option<PathBuf>,
160    config: Config,
161}
162// Compiler implements Send because MacroFunction may modify system plugins but it will never conflict with VM execution
163unsafe impl Send for Context {}
164
165#[derive(Debug, Clone, Copy, Default, PartialEq)]
166pub struct IoChannelInfo {
167    pub input: u32,
168    pub output: u32,
169}
170
171impl Context {
172    pub fn new(
173        ext_fns: impl IntoIterator<Item = ExtFunTypeInfo>,
174        macros: impl IntoIterator<Item = Box<dyn MacroFunction>>,
175        file_path: Option<PathBuf>,
176        config: Config,
177    ) -> Self {
178        Self {
179            ext_fns: ext_fns.into_iter().collect(),
180            macros: macros.into_iter().collect(),
181            file_path,
182            config,
183        }
184    }
185    pub fn get_ext_typeinfos(&self) -> Vec<(Symbol, TypeNodeId)> {
186        self.ext_fns
187            .clone()
188            .into_iter()
189            .map(|ExtFunTypeInfo { name, ty, .. }| (name, ty))
190            .chain(self.macros.iter().map(|m| (m.get_name(), m.get_type())))
191            .collect()
192    }
193    pub fn emit_mir(&self, src: &str) -> Result<Mir, Vec<Box<dyn ReportableError>>> {
194        let path = self.file_path.clone();
195        let (ast, mut parse_errs) = parser::parse_to_expr(src, path);
196        // let ast = parser::add_global_context(ast, self.file_path.unwrap_or_default());
197        let mir = mirgen::compile(
198            ast,
199            self.get_ext_typeinfos().as_slice(),
200            &self.macros,
201            self.file_path.clone(),
202        );
203        if parse_errs.is_empty() {
204            mir
205        } else {
206            let _ = mir.map_err(|mut e| {
207                parse_errs.append(&mut e);
208            });
209            Err(parse_errs)
210        }
211    }
212    pub fn emit_bytecode(&self, src: &str) -> Result<vm::Program, Vec<Box<dyn ReportableError>>> {
213        let mir = self.emit_mir(src)?;
214        let config = bytecodegen::Config {
215            self_eval_mode: self.config.self_eval_mode,
216        };
217        Ok(bytecodegen::gen_bytecode(mir, config))
218    }
219}
220
221// pub fn interpret_top(
222//     content: String,
223//     global_ctx: &mut ast_interpreter::Context,
224// ) -> Result<ast_interpreter::Value, Vec<Box<dyn ReportableError>>> {
225//     let ast = emit_ast(&content, None)?;
226//     ast_interpreter::eval_ast(ast, global_ctx).map_err(|e| {
227//         let eb: Box<dyn ReportableError> = Box::new(e);
228//         vec![eb]
229//     })
230// }
231
232#[cfg(test)]
233mod test {
234    use crate::{
235        function,
236        interner::ToSymbol,
237        numeric,
238        types::{PType, Type},
239    };
240
241    use super::*;
242    fn get_source() -> &'static str {
243        //type annotation input:float is not necessary ideally
244        // but we have to for now for because of subtyping issue
245        r#"
246fn counter(){
247    self + 1
248}
249fn dsp(input:float){
250    let res = input + counter()
251    (0,res)
252}
253"#
254    }
255    fn test_context() -> Context {
256        let addfn = ExtFunTypeInfo::new(
257            "add".to_symbol(),
258            function!(vec![numeric!(), numeric!()], numeric!()),
259            EvalStage::Persistent,
260        );
261        let extfns = [addfn];
262        Context::new(extfns, [], None, Config::default())
263    }
264    #[test]
265    fn mir_channelcount() {
266        let src = &get_source();
267        let ctx = test_context();
268        let mir = ctx.emit_mir(src).unwrap();
269        log::trace!("Mir: {mir}");
270        let iochannels = mir.get_dsp_iochannels().unwrap();
271        assert_eq!(iochannels.input, 1);
272        assert_eq!(iochannels.output, 2);
273    }
274    #[test]
275    fn bytecode_channelcount() {
276        let src = &get_source();
277        let ctx = test_context();
278        let prog = ctx.emit_bytecode(src).unwrap();
279        let iochannels = prog.iochannels.unwrap();
280        assert_eq!(iochannels.input, 1);
281        assert_eq!(iochannels.output, 2);
282    }
283}