libjuno 0.1.2

Library for JunoLang
use crate::ir::backend::LoopFrame;
use crate::metair::*;

use super::*;

impl<'ctx> LLVMBackend<'ctx> {
    pub fn lower_stmt(&mut self, stmt: &MetaStmt) -> Result<(), LLVMError> {
        match stmt {
            MetaStmt::Let { name, mutable: _, ty, value } => {
                self.lower_let(*name, ty.as_ref(), value.as_ref())
            }
            MetaStmt::Break => { self.lower_break() }
            MetaStmt::If { cond, then_body, else_ifs, else_body } => {
                self.lower_if(cond, then_body, else_ifs, else_body.as_ref())
            }
            MetaStmt::Continue => { self.lower_continue() }
            MetaStmt::Loop { body } => { self.lower_loop(body) }
            MetaStmt::Return(expr) => { self.lower_return(expr.as_ref()) }
            MetaStmt::Assign { target, value } => { self.lower_assign(*target, value) }
            MetaStmt::Expr(expr) => { self.lower_expr_stmt(expr) }
        }
    }
    fn lower_expr_stmt(&mut self, expr: &MetaExpr) -> Result<(), LLVMError> {
        match &expr.kind {
            MetaExprKind::Call { target, args } => {
                self.lower_call(&target, &args)?;
            }

            _ => {
                self.lower_expr(expr)?;
            }
        }

        Ok(())
    }
    fn lower_continue(&mut self) -> Result<(), LLVMError> {
        let frame = self.loop_stack
            .last()
            .ok_or_else(|| { LLVMError::Message("continue used outside of a loop".into()) })?;

        self.builder
            .build_unconditional_branch(frame.continue_block)
            .map_err(|e| LLVMError::Message(e.to_string()))?;

        Ok(())
    }
    fn lower_break(&mut self) -> Result<(), LLVMError> {
        let frame = self.loop_stack
            .last()
            .ok_or_else(|| { LLVMError::Message("break used outside of a loop".into()) })?;

        self.builder
            .build_unconditional_branch(frame.break_block)
            .map_err(|e| LLVMError::Message(e.to_string()))?;

        Ok(())
    }
    fn lower_loop(&mut self, body: &[MetaStmt]) -> Result<(), LLVMError> {
        let function = self.current_function();

        let header_bb = self.context.append_basic_block(function, "loop.header");

        let body_bb = self.context.append_basic_block(function, "loop.body");

        let exit_bb = self.context.append_basic_block(function, "loop.exit");

        self.builder
            .build_unconditional_branch(header_bb)
            .map_err(|e| LLVMError::Message(e.to_string()))?;

        self.builder.position_at_end(header_bb);

        self.builder
            .build_unconditional_branch(body_bb)
            .map_err(|e| LLVMError::Message(e.to_string()))?;

        self.builder.position_at_end(body_bb);

        self.loop_stack.push(LoopFrame {
            break_block: exit_bb,
            continue_block: header_bb,
        });

        self.push_scope();

        for stmt in body {
            if self.builder.get_insert_block().unwrap().get_terminator().is_some() {
                break;
            }

            self.lower_stmt(stmt)?;
        }

        self.pop_scope();

        self.loop_stack.pop();

        if self.builder.get_insert_block().unwrap().get_terminator().is_none() {
            self.builder
                .build_unconditional_branch(header_bb)
                .map_err(|e| LLVMError::Message(e.to_string()))?;
        }

        self.builder.position_at_end(exit_bb);

        Ok(())
    }
    fn lower_if(
        &mut self,
        cond: &MetaExpr,
        then_body: &[MetaStmt],
        else_ifs: &[(MetaExpr, Vec<MetaStmt>)],
        else_body: Option<&Vec<MetaStmt>>
    ) -> Result<(), LLVMError> {
        let function = self.current_function();

        let merge_bb = self.context.append_basic_block(function, "if.merge");

        let then_bb = self.context.append_basic_block(function, "if.then");

        let first_else_bb = self.context.append_basic_block(function, "if.else");

        let cond = self.lower_expr(cond)?.into_int_value();

        self.builder
            .build_conditional_branch(cond, then_bb, first_else_bb)
            .map_err(|e| LLVMError::Message(e.to_string()))?;

        self.builder.position_at_end(then_bb);

        self.push_scope();

        for stmt in then_body {
            self.lower_stmt(stmt)?;

            if self.builder.get_insert_block().unwrap().get_terminator().is_some() {
                break;
            }
        }

        self.pop_scope();

        if self.builder.get_insert_block().unwrap().get_terminator().is_none() {
            self.builder
                .build_unconditional_branch(merge_bb)
                .map_err(|e| LLVMError::Message(e.to_string()))?;
        }

        self.builder.position_at_end(first_else_bb);

        if else_ifs.is_empty() {
            if let Some(body) = else_body {
                self.push_scope();

                for stmt in body {
                    self.lower_stmt(stmt)?;

                    if self.builder.get_insert_block().unwrap().get_terminator().is_some() {
                        break;
                    }
                }

                self.pop_scope();
            }
        } else {
            let (cond, body) = &else_ifs[0];

            self.lower_if(cond, body, &else_ifs[1..], else_body)?;
        }

        if self.builder.get_insert_block().unwrap().get_terminator().is_none() {
            self.builder
                .build_unconditional_branch(merge_bb)
                .map_err(|e| LLVMError::Message(e.to_string()))?;
        }

        self.builder.position_at_end(merge_bb);

        Ok(())
    }
    fn lower_return(&mut self, value: Option<&MetaExpr>) -> Result<(), LLVMError> {
        match value {
            Some(expr) => {
                let value = self.lower_expr(expr)?;

                self.builder
                    .build_return(Some(&value))
                    .map_err(|e| LLVMError::Message(e.to_string()))?;
            }

            None => {
                self.builder.build_return(None).map_err(|e| LLVMError::Message(e.to_string()))?;
            }
        }

        Ok(())
    }
    fn lower_assign(&mut self, target: SymbolId, value: &MetaExpr) -> Result<(), LLVMError> {
        let (ptr, ty) = {
            let var = self.get_variable(target)?;
            (var.ptr, var.ty)
        };

        let value = self.lower_expr(value)?;

        self.builder.build_store(ptr, value).map_err(|e| LLVMError::Message(e.to_string()))?;
        Ok(())
    }
    fn lower_let(
        &mut self,
        name: SymbolId,
        ty: Option<&MetaType>,
        value: Option<&MetaExpr>
    ) -> Result<(), LLVMError> {
        let ty = ty.ok_or_else(|| LLVMError::Message("let without type".into()))?;

        let llvm_ty = self.lower_type(ty)?;

        let ptr = self.builder
            .build_alloca(llvm_ty, &self.program.symbol_table[name as usize])
            .map_err(|e| LLVMError::Message(e.to_string()))?;

        self.insert_variable(name, ptr, llvm_ty);

        if let Some(expr) = value {
            let value = self.lower_expr(expr)?;

            self.builder.build_store(ptr, value).map_err(|e| LLVMError::Message(e.to_string()))?;
        }

        Ok(())
    }
}