Skip to content

Commit f0f2266

Browse files
authored
Merge pull request #18 from rsahwe/master
booleans and simple conditional
2 parents 72cf8f8 + a38a55e commit f0f2266

File tree

16 files changed

+333
-47
lines changed

16 files changed

+333
-47
lines changed

src/blush/combiner.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,18 @@ impl<'a> Combiner<'a> {
267267
},
268268
expr.span,
269269
),
270+
ExpressionKind::Conditional {
271+
condition,
272+
true_expression,
273+
false_expression,
274+
} => Expression::new(
275+
ExpressionKind::Conditional {
276+
condition: Box::new(Self::mangle_var_names(condition)),
277+
true_expression: Box::new(Self::mangle_var_names(true_expression)),
278+
false_expression: Box::new(Self::mangle_var_names(false_expression)),
279+
},
280+
expr.span,
281+
),
270282
ExpressionKind::BoolLiteral(_)
271283
| ExpressionKind::IntLiteral(_)
272284
| ExpressionKind::Use { .. } => expr.clone(),
@@ -310,6 +322,16 @@ impl<'a> Combiner<'a> {
310322
Self::referenced_variables(&arg.value, acc)
311323
})
312324
}
325+
ExpressionKind::Conditional {
326+
condition,
327+
true_expression,
328+
false_expression,
329+
} => {
330+
let mut new_base = Self::referenced_variables(condition, base);
331+
new_base = Self::referenced_variables(true_expression, new_base);
332+
new_base = Self::referenced_variables(false_expression, new_base);
333+
new_base
334+
}
313335
}
314336
}
315337

src/blush/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,11 @@ impl Blush {
103103
.combine()?;
104104

105105
let mut checker = Checker::new();
106-
let Ok(checked_program) = checker.check(&combined_program) else {
107-
return Err("Failed to typecheck program".into());
106+
let checked_program = match checker.check(&combined_program) {
107+
Ok(r) => r,
108+
Err(errors) => {
109+
return Err(format!("Failed to typecheck program due to: {:#?}", errors).into());
110+
}
108111
};
109112

110113
let ir = IRGenerator::new(checker.types).program_ir(&checked_program);

src/checker/mod.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,36 @@ impl Checker {
488488
unreachable!("No error, no missing function and no success???")
489489
}
490490
ExpressionKind::Use { .. } => todo!("typechecking use expressions"),
491+
ExpressionKind::Conditional {
492+
condition,
493+
true_expression,
494+
false_expression,
495+
} => {
496+
let true_expression = Box::new(self.check_expression(true_expression, type_hint)?);
497+
let false_expression =
498+
Box::new(self.check_expression(false_expression, type_hint)?);
499+
500+
if true_expression.type_id == false_expression.type_id {
501+
let res_type = true_expression.type_id;
502+
503+
Self::typed_expression(
504+
CheckedExpressionData::Conditional {
505+
condition: Box::new(self.check_expression(condition, Some(BOOL_ID))?),
506+
true_expression,
507+
false_expression,
508+
},
509+
expr.span,
510+
res_type,
511+
type_hint,
512+
)
513+
} else {
514+
Err(Error::type_mismatch(
515+
true_expression.type_id,
516+
false_expression.type_id,
517+
expr.span,
518+
))
519+
}
520+
}
491521
}
492522
}
493523

@@ -564,6 +594,12 @@ pub enum CheckedExpressionData {
564594
arguments: Vec<CheckedFunctionArgument>,
565595
variable_id: VariableId,
566596
},
597+
598+
Conditional {
599+
condition: Box<CheckedExpression>,
600+
true_expression: Box<CheckedExpression>,
601+
false_expression: Box<CheckedExpression>,
602+
},
567603
}
568604

569605
#[derive(PartialEq, Eq, Debug)]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/codegen/emitters/x86_64_linux_nasm.rs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::Emitter;
2-
use crate::ir::tac::{BasicBlock, Entity, Instruction, TempId, Value};
2+
use crate::ir::tac::{BasicBlock, Entity, Instruction, TempId, Type, Value};
33
use std::collections::{HashMap, HashSet};
44
use std::fmt::Write;
55

@@ -11,7 +11,6 @@ pub struct CodeGenerator {
1111
current_stack_offset: HashMap<String, usize>, // (function label -> stack offset)
1212
current_function: Option<String>,
1313
}
14-
1514
impl CodeGenerator {
1615
pub fn new() -> Self {
1716
Self {
@@ -135,8 +134,20 @@ impl CodeGenerator {
135134
unreachable!()
136135
}
137136
}
138-
Instruction::Not { .. } => {
139-
self.text_section.push_str(" TODO: not\n");
137+
Instruction::Not { dest, src } => {
138+
if let Value::Temp(temp_id) = dest.value {
139+
self.allocate_temp_value(temp_id, dest);
140+
141+
let src_asm = self.entity_to_asm(src);
142+
143+
let _ = writeln!(self.text_section, " mov rax, {src_asm}");
144+
let _ = writeln!(self.text_section, " cmp rax, 0");
145+
let _ = writeln!(self.text_section, " sete al");
146+
let _ = writeln!(self.text_section, " movzx eax, al");
147+
let _ = writeln!(self.text_section, " push qword rax");
148+
} else {
149+
unreachable!()
150+
}
140151
}
141152
Instruction::Assign { dest, src } => match (&dest.value, &src.ty) {
142153
(Value::Variable(name), ty) => {
@@ -151,8 +162,23 @@ impl CodeGenerator {
151162
}
152163
_ => unreachable!(),
153164
},
154-
Instruction::Conditional { .. } => {
155-
self.text_section.push_str(" TODO: conditional\n");
165+
Instruction::Conditional {
166+
dest,
167+
condition,
168+
false_label,
169+
} => {
170+
if let Value::Temp(temp_id) = dest.value {
171+
self.allocate_temp_value(temp_id, dest);
172+
173+
let condition_asm = self.entity_to_asm(condition);
174+
175+
let _ = writeln!(self.text_section, " sub rsp, {}", dest.ty.size_in_bytes());
176+
let _ = writeln!(self.text_section, " mov rax, {condition_asm}");
177+
let _ = writeln!(self.text_section, " cmp rax, 0");
178+
let _ = writeln!(self.text_section, " je {false_label}");
179+
} else {
180+
unreachable!()
181+
}
156182
}
157183
Instruction::Call { dest, callee, args } => {
158184
if let Value::Temp(temp_id) = dest.value {
@@ -176,17 +202,34 @@ impl CodeGenerator {
176202
let _ = writeln!(self.text_section, " jmp {label}");
177203
}
178204
Instruction::Ret(entity) => {
179-
let entity_asm = self.entity_to_asm(entity);
205+
let entity_asm = if matches!(entity.ty, Type::Unit) {
206+
"0".to_string()
207+
} else {
208+
self.entity_to_asm(entity)
209+
};
180210
let _ = writeln!(self.text_section, " mov rax, {entity_asm}");
181211
let _ = writeln!(self.text_section, " leave");
182212
let _ = writeln!(self.text_section, " ret");
183213
}
184214
Instruction::Exit(entity) => {
185215
self.text_section.push_str(" mov rax, 60\n");
186-
let entity_asm = self.entity_to_asm(entity);
216+
let entity_asm = if matches!(entity.ty, Type::Unit) {
217+
"0".to_string()
218+
} else {
219+
self.entity_to_asm(entity)
220+
};
187221
let _ = writeln!(self.text_section, " mov rdi, {entity_asm}");
188222
let _ = writeln!(self.text_section, " syscall");
189223
}
224+
Instruction::Label(label) => {
225+
let _ = writeln!(self.text_section, "{label}:");
226+
}
227+
Instruction::Move { dest, src } => {
228+
let dest_asm = self.entity_to_asm(dest);
229+
let src_asm = self.entity_to_asm(src);
230+
231+
let _ = writeln!(self.text_section, " mov {dest_asm}, {src_asm}");
232+
}
190233
}
191234
}
192235

@@ -224,7 +267,7 @@ impl CodeGenerator {
224267
}
225268
Value::Unit => todo!("unit"),
226269
Value::Int(num) => num.to_string(),
227-
Value::Bool(_) => todo!("bool"),
270+
Value::Bool(val) => if *val { "1" } else { "0" }.to_string(),
228271
Value::Variable(name) => {
229272
if let Some(var_name) = self.global_vars.get(name) {
230273
return format!("[{var_name}]");

src/ir/mod.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,45 @@ impl IRGenerator {
283283
));
284284
Entity::temp_val(id, *return_type)
285285
}
286+
CheckedExpressionData::Conditional {
287+
condition,
288+
true_expression,
289+
false_expression,
290+
} => {
291+
let false_label = format!("label#{}", self.get_id());
292+
let end_label = format!("label#{}", self.get_id());
293+
let id = self.get_id();
294+
let ty = Type::Bool; //TODO: doesn't matter at the moment, but probably will, TYPE LOOKUP PLS
295+
296+
let condition = self.expression_ir(condition);
297+
298+
self.add_instruction(Instruction::conditional(
299+
Entity::temp_val(id, ty),
300+
condition,
301+
false_label.clone(),
302+
));
303+
304+
let true_value = self.expression_ir(true_expression);
305+
306+
let ty = true_value.ty.clone();
307+
308+
self.add_instruction(Instruction::Move {
309+
dest: Entity::temp_val(id, true_value.ty.clone()),
310+
src: true_value,
311+
});
312+
self.add_instruction(Instruction::Goto(end_label.clone()));
313+
self.add_instruction(Instruction::Label(false_label));
314+
315+
let false_value = self.expression_ir(false_expression);
316+
317+
self.add_instruction(Instruction::Move {
318+
dest: Entity::temp_val(id, false_value.ty.clone()),
319+
src: false_value,
320+
});
321+
self.add_instruction(Instruction::Label(end_label));
322+
323+
Entity::temp_val(id, ty)
324+
}
286325
}
287326
}
288327
}

src/ir/tac.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,8 @@ pub enum Instruction {
6969
src: Entity,
7070
},
7171
Conditional {
72-
lhs: Entity,
73-
rhs: Entity,
74-
operator: Operator,
75-
true_label: Label,
72+
dest: Entity,
73+
condition: Entity,
7674
false_label: Label,
7775
},
7876
Call {
@@ -83,6 +81,11 @@ pub enum Instruction {
8381
Goto(Label),
8482
Ret(Entity),
8583
Exit(Entity),
84+
Label(Label),
85+
Move {
86+
dest: Entity,
87+
src: Entity,
88+
},
8689
}
8790

8891
impl Instruction {
@@ -104,6 +107,13 @@ impl Instruction {
104107
pub const fn not(dest: Entity, src: Entity) -> Self {
105108
Self::Not { dest, src }
106109
}
110+
pub const fn conditional(dest: Entity, condition: Entity, false_label: Label) -> Self {
111+
Self::Conditional {
112+
dest,
113+
condition,
114+
false_label,
115+
}
116+
}
107117
pub const fn call(dest: Entity, callee: Entity, args: Vec<Entity>) -> Self {
108118
Self::Call { dest, callee, args }
109119
}
@@ -119,14 +129,12 @@ impl Display for Instruction {
119129
Self::Not { dest, src } => write!(f, "{dest} := !{src}"),
120130
Self::Assign { dest, src } => write!(f, "{dest} := {src}"),
121131
Self::Conditional {
122-
lhs,
123-
rhs,
124-
operator,
125-
true_label,
132+
dest,
133+
condition,
126134
false_label,
127135
} => write!(
128136
f,
129-
"if {lhs} {operator} {rhs} then goto {true_label} else goto {false_label}"
137+
"create {dest} and if not {condition} then goto {false_label}"
130138
),
131139
Self::Call { dest, callee, args } => write!(
132140
f,
@@ -139,6 +147,8 @@ impl Display for Instruction {
139147
Self::Goto(label) => write!(f, "goto {label}"),
140148
Self::Ret(entity) => write!(f, "ret {entity}"),
141149
Self::Exit(code) => write!(f, "exit {code}"),
150+
Self::Label(label) => write!(f, "label {label}"),
151+
Self::Move { dest, src } => write!(f, "{dest} := {src}"),
142152
}
143153
}
144154
}
@@ -230,7 +240,7 @@ impl Display for Type {
230240
impl Type {
231241
pub const fn size_in_bytes(&self) -> usize {
232242
match &self {
233-
Self::Bool => 1,
243+
Self::Bool => 8, //TODO: stack needs to be 8 byte aligned and lovely is not optimized anyway, fix later
234244
Self::Unit => 0,
235245
Self::Int | Self::Function { .. } => 8,
236246
}

src/lexer/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ impl<'src> Lexer<'src> {
136136
"type" => Token::new(Type, cur_index, 4),
137137
"true" => Token::new(True, cur_index, 4),
138138
"false" => Token::new(False, cur_index, 5),
139+
"if" => Token::new(If, cur_index, 2),
140+
"else" => Token::new(Else, cur_index, 4),
139141
s => Token::new(Identifier, cur_index, s.len()),
140142
}
141143
}

src/lexer/tokens.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub enum TokenKind {
1818
Type, // type
1919
True, // true
2020
False, // false
21+
If, // if
22+
Else, // else
2123

2224
// syntax
2325
LParen, // (
@@ -73,6 +75,8 @@ impl Display for TokenKind {
7375
Self::Type => "type",
7476
Self::True => "true",
7577
Self::False => "false",
78+
Self::If => "if",
79+
Self::Else => "else",
7680
Self::LParen => "(",
7781
Self::RParen => ")",
7882
Self::LBrace => "{",

src/parser/ast.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ pub enum ExpressionKind {
2323
namespace: Option<String>,
2424
},
2525
Block(Vec<Expression>),
26+
Conditional {
27+
condition: Box<Expression>,
28+
true_expression: Box<Expression>,
29+
false_expression: Box<Expression>,
30+
},
2631
Prefix {
2732
operator: PrefixOperator,
2833
expression: Box<Expression>,
@@ -65,6 +70,15 @@ impl ExpressionKind {
6570
Infix { left, right, .. } => left.kind.is_const() && right.kind.is_const(),
6671
VariableDecl { value, mutable, .. } => !mutable && value.kind.is_const(),
6772
FunctionCall { .. } => false,
73+
Conditional {
74+
condition,
75+
true_expression,
76+
false_expression,
77+
} => {
78+
condition.kind.is_const()
79+
&& true_expression.kind.is_const()
80+
&& false_expression.kind.is_const()
81+
}
6882
BoolLiteral(_) | IntLiteral(_) | Ident { .. } | Function { .. } | Use { .. } => true,
6983
}
7084
}

0 commit comments

Comments
 (0)