use crate::{
diagnostics::LoweringError,
lowering::{
ast_lowering::{expression, statement},
ir::{
basic_block::Terminator,
function::{FunctionBuilder, LocalKind},
instruction::{LValInstruct, RValInstruct, RValueKind},
types::MathicType,
value::Value,
},
},
parser::{
Span,
ast::{
control_flow::{ForStmt, IfStmt, WhileStmt},
expression::{ArithOp, BinaryOp, CmpOp},
statement::BlockStmt,
},
},
};
pub fn lower_if(func: &mut FunctionBuilder, stmt: &IfStmt) -> Result<(), LoweringError> {
let IfStmt {
condition,
then_block,
else_block,
} = stmt;
let (condition_val, condition_ty) = expression::lower_expr(func, condition, None)?;
if !func.get_type(condition_ty, condition.span)?.is_bool() {
return Err(LoweringError::MismatchedType {
expected: MathicType::Bool,
found: func.get_type(condition_ty, condition.span)?,
span: condition.span,
});
}
let trigger_block_idx = func.last_block_idx();
let (true_block, false_block) = if let Some(else_block) = else_block {
let target_block_idx = func.last_block_idx() + 3;
let then_block_idx = statement::lower_block(
func,
then_block,
Terminator::Branch {
target: target_block_idx,
span: None,
},
)?;
let else_block_idx = statement::lower_block(
func,
else_block,
Terminator::Branch {
target: target_block_idx,
span: None,
},
)?;
(then_block_idx, else_block_idx)
} else {
let target_block_idx = func.last_block_idx() + 2;
let then_block_idx = statement::lower_block(
func,
then_block,
Terminator::Branch {
target: target_block_idx,
span: None,
},
)?;
func.add_block(Terminator::Return(None, None), None);
(then_block_idx, target_block_idx)
};
func.get_basic_block_mut(trigger_block_idx).terminator = Terminator::CondBranch {
condition: condition_val,
true_block,
false_block,
span: None,
};
Ok(())
}
pub fn lower_while(
func: &mut FunctionBuilder,
stmt: &WhileStmt,
_span: Span,
) -> Result<(), LoweringError> {
let WhileStmt { condition, body } = stmt;
let (loop_breaker_condition, condition_ty_idx) = expression::lower_expr(func, condition, None)?;
let condition_ty = func.get_type(condition_ty_idx, condition.span)?;
if !condition_ty.is_bool() {
return Err(LoweringError::MismatchedType {
expected: MathicType::Bool,
found: condition_ty,
span: condition.span,
});
}
lower_loop(func, body, loop_breaker_condition, Vec::with_capacity(0))
}
pub fn lower_for(
func: &mut FunctionBuilder,
stmt: &ForStmt,
span: Span,
) -> Result<(), LoweringError> {
let ForStmt {
variable,
start,
end,
body,
} = stmt;
let (start_val, start_ty) = expression::lower_expr(func, start, None)?;
let (end_val, _) = expression::lower_expr(func, end, None)?;
let loop_tracker_idx = func.sym_table.add_local(
Some(variable.clone()),
start_ty,
Some(span),
LocalKind::Temp,
)?;
let loop_breaker_condition = RValInstruct {
kind: RValueKind::Binary {
op: BinaryOp::Compare(CmpOp::Lt),
lhs: Box::new(RValInstruct {
kind: RValueKind::Use {
value: Value::InMemory {
local_idx: loop_tracker_idx,
modifier: vec![],
},
span: None,
},
ty: start_ty,
}),
rhs: Box::new(end_val),
span: Span::from(start.span.start..end.span.end),
},
ty: func.get_or_insert_global_type_idx(MathicType::Bool),
};
let extra_instructions = vec![LValInstruct::Assign {
local_idx: loop_tracker_idx,
value: RValInstruct {
kind: RValueKind::Binary {
op: BinaryOp::Arithmetic(ArithOp::Add),
lhs: Box::new(RValInstruct {
kind: RValueKind::Use {
value: Value::InMemory {
local_idx: loop_tracker_idx,
modifier: vec![],
},
span: None,
},
ty: start_ty,
}),
rhs: Box::new(RValInstruct {
kind: RValueKind::Use {
value: 1i32.into(),
span: None,
},
ty: start_ty,
}),
span: Span::from(start.span.start..end.span.end),
},
ty: start_ty,
},
modifier: vec![],
span: None,
}];
func.push_instruction(LValInstruct::Let {
local_idx: loop_tracker_idx,
init: start_val,
span: None,
});
lower_loop(func, body, loop_breaker_condition, extra_instructions)
}
fn lower_loop(
func: &mut FunctionBuilder,
loop_body: &BlockStmt,
condition: RValInstruct,
extra_instructions: Vec<LValInstruct>,
) -> Result<(), LoweringError> {
let start_block_idx = func.last_block_idx() + 1;
let loop_block_idx = start_block_idx + 1;
let end_block_idx = start_block_idx + 2;
func.get_basic_block_mut(func.last_block_idx()).terminator = Terminator::Branch {
target: start_block_idx,
span: None,
};
let start_block_idx = func.add_block(
Terminator::CondBranch {
condition,
true_block: loop_block_idx,
false_block: end_block_idx,
span: None,
},
None,
);
let _ = statement::lower_block(
func,
loop_body,
Terminator::Branch {
target: start_block_idx,
span: None,
},
)?;
for i in extra_instructions {
func.push_instruction(i);
}
func.add_block(Terminator::Return(None, None), None);
Ok(())
}