Merge branch 'loops'

This commit is contained in:
Sofia 2025-07-23 21:07:42 +03:00
commit a9abb55287
16 changed files with 257 additions and 18 deletions

18
examples/loops.reid Normal file
View File

@ -0,0 +1,18 @@
// Arithmetic, function calls and imports!
import std::print;
import std::from_str;
fn main() -> u32 {
let text = from_str("hello");
for i in 0 .. 10 {
print(&text)
}
let mut num = 0;
while num < 10 {
num = num + 1;
}
return num;
}

View File

@ -440,7 +440,6 @@ impl Builder {
}
Instr::GetStructElemPtr(ptr_val, idx) => {
let ptr_ty = ptr_val.get_type(&self)?;
dbg!(&ptr_ty);
if let Type::Ptr(ty) = ptr_ty {
if let Type::CustomType(val) = *ty {
match self.type_data(&val).kind {

View File

@ -178,6 +178,8 @@ pub enum BlockLevelStatement {
},
Expression(Expression),
Return(ReturnType, Expression),
ForLoop(String, TokenRange, Expression, Expression, Block),
WhileLoop(Expression, Block),
}
#[derive(Debug, Clone)]

View File

@ -155,11 +155,12 @@ impl Parse for PrimaryExpression {
}
Token::DecimalValue(v) => {
stream.next(); // Consume decimal
if let Some(Token::Dot) = stream.peek() {
if let (Some(Token::Dot), Some(Token::DecimalValue(fractional))) =
(stream.peek(), stream.peek2())
{
stream.next(); // Consume dot
let Some(Token::DecimalValue(fractional)) = stream.next() else {
return Err(stream.expected_err("fractional part")?);
};
stream.next(); // Consume fractional
Expression(
Kind::Literal(Literal::Decimal(
format!("{}.{}", v, fractional)
@ -231,10 +232,10 @@ impl Parse for PrimaryExpression {
stream.expect(Token::BracketClose)?;
Expression(Kind::Array(expressions), stream.get_range().unwrap())
}
_ => Err(stream.expected_err("expression inner")?)?,
_ => Err(stream.expecting_err("expression")?)?,
}
} else {
Err(stream.expected_err("expression")?)?
Err(stream.expecting_err("expression")?)?
};
while let Ok(index) = stream.parse::<ValueIndex>() {
@ -667,6 +668,14 @@ impl Parse for BlockLevelStatement {
stream.expect(Token::Semi)?;
Stmt::Return(ReturnType::Hard, exp)
}
Some(Token::For) => {
let for_stmt = stream.parse::<ForStatement>()?;
Stmt::ForLoop(for_stmt.0, for_stmt.1, for_stmt.2, for_stmt.3, for_stmt.4)
}
Some(Token::While) => {
let while_stmt = stream.parse::<WhileStatement>()?;
Stmt::WhileLoop(while_stmt.0, while_stmt.1)
}
_ => {
if let Ok(SetStatement(ident, expr, range)) = stream.parse() {
Stmt::Set(ident, expr, range)
@ -683,6 +692,36 @@ impl Parse for BlockLevelStatement {
}
}
#[derive(Debug)]
pub struct ForStatement(String, TokenRange, Expression, Expression, Block);
#[derive(Debug)]
pub struct WhileStatement(pub Expression, pub Block);
impl Parse for ForStatement {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::For)?;
let Some(Token::Identifier(idx)) = stream.next() else {
return Err(stream.expected_err("loop counter")?);
};
let start_range = stream.get_range().unwrap();
stream.expect(Token::In)?;
let start = stream.parse()?;
stream.expect(Token::Dot)?;
stream.expect(Token::Dot)?;
let end = stream.parse()?;
Ok(ForStatement(idx, start_range, start, end, stream.parse()?))
}
}
impl Parse for WhileStatement {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::While)?;
Ok(WhileStatement(stream.parse()?, stream.parse()?))
}
}
#[derive(Debug)]
pub struct SetStatement(Expression, Expression, TokenRange);

View File

@ -4,7 +4,7 @@ use crate::{
ast::{self},
mir::{
self, CustomTypeKey, ModuleMap, NamedVariableRef, SourceModuleId, StmtKind, StructField,
StructType,
StructType, WhileStatement,
},
};
@ -148,6 +148,72 @@ impl ast::Block {
ast::BlockLevelStatement::Return(_, e) => {
(StmtKind::Expression(e.process(module_id)), e.1)
}
ast::BlockLevelStatement::ForLoop(counter, counter_range, start, end, block) => {
let counter_var = NamedVariableRef(
mir::TypeKind::Vague(mir::VagueType::Unknown),
counter.clone(),
counter_range.as_meta(module_id),
);
let let_statement = mir::Statement(
StmtKind::Let(counter_var.clone(), true, start.process(module_id)),
counter_range.as_meta(module_id),
);
mir_statements.push(let_statement);
let set_new = mir::Statement(
StmtKind::Set(
mir::Expression(
mir::ExprKind::Variable(counter_var.clone()),
counter_range.as_meta(module_id),
),
mir::Expression(
mir::ExprKind::BinOp(
mir::BinaryOperator::Add,
Box::new(mir::Expression(
mir::ExprKind::Variable(counter_var.clone()),
counter_range.as_meta(module_id),
)),
Box::new(mir::Expression(
mir::ExprKind::Literal(mir::Literal::Vague(
mir::VagueLiteral::Number(1),
)),
counter_range.as_meta(module_id),
)),
),
counter_range.as_meta(module_id),
),
),
counter_range.as_meta(module_id),
);
let mut block = block.into_mir(module_id);
block.statements.insert(0, set_new);
(
StmtKind::While(WhileStatement {
condition: mir::Expression(
mir::ExprKind::BinOp(
mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
Box::new(mir::Expression(
mir::ExprKind::Variable(counter_var),
counter_range.as_meta(module_id),
)),
Box::new(end.process(module_id)),
),
counter_range.as_meta(module_id),
),
block,
meta: self.2.as_meta(module_id),
}),
self.2,
)
}
ast::BlockLevelStatement::WhileLoop(expression, block) => (
StmtKind::While(WhileStatement {
condition: expression.process(module_id),
block: block.into_mir(module_id),
meta: self.2.as_meta(module_id),
}),
self.2,
),
};
mir_statements.push(mir::Statement(kind, range.as_meta(module_id)));

View File

@ -19,6 +19,7 @@ use crate::{
mir::{
self, implement::TypeCategory, CustomTypeKey, Metadata, NamedVariableRef, SourceModuleId,
StructField, StructType, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral,
WhileStatement,
},
util::try_all,
};
@ -598,6 +599,54 @@ impl mir::Statement {
}
mir::StmtKind::Import(_) => todo!(),
mir::StmtKind::Expression(expression) => expression.codegen(scope, state),
mir::StmtKind::While(WhileStatement {
condition, block, ..
}) => {
let condition_block = scope.function.ir.block("condition_block");
let condition_true_block = scope.function.ir.block("condition_true");
let condition_failed_block = scope.function.ir.block("condition_failed");
scope
.block
.terminate(Term::Br(condition_block.value()))
.unwrap();
let mut condition_scope = scope.with_block(condition_block);
let condition_res = condition.codegen(&mut condition_scope, state)?.unwrap();
let true_instr = condition_scope
.block
.build(Instr::Constant(ConstValue::Bool(true)))
.unwrap();
let check = condition_scope
.block
.build(Instr::ICmp(
CmpPredicate::EQ,
condition_res.instr(),
true_instr,
))
.unwrap();
condition_scope
.block
.terminate(Term::CondBr(
check,
condition_true_block.value(),
condition_failed_block.value(),
))
.unwrap();
let mut condition_true_scope = scope.with_block(condition_true_block);
block.codegen(&mut condition_true_scope, state)?;
condition_true_scope
.block
.terminate(Term::Br(condition_scope.block.value()))
// Can hard return inside the condition_true_scope
.ok();
scope.swap_block(condition_failed_block);
Ok(None)
}
}
}
}
@ -1000,7 +1049,6 @@ impl mir::Expression {
let TypeKind::CodegenPtr(inner) = &struct_val.1 else {
panic!("tried accessing non-pointer");
};
dbg!(&inner);
let TypeKind::CustomType(key) = *inner.clone() else {
panic!("tried accessing non-custom-type");
};

View File

@ -42,6 +42,12 @@ pub enum Token {
Extern,
/// `struct`
Struct,
/// `while`
While,
/// `for`
For,
/// `In`
In,
// Symbols
/// `;`
@ -136,6 +142,9 @@ impl ToString for Token {
Token::Extern => String::from("extern"),
Token::Struct => String::from("struct"),
Token::AsKeyword => String::from("as"),
Token::For => String::from("for"),
Token::In => String::from("in"),
Token::While => String::from("while"),
Token::Semi => String::from(';'),
Token::Equals => String::from('='),
Token::Colon => String::from(':'),
@ -322,6 +331,9 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
"pub" => Token::PubKeyword,
"struct" => Token::Struct,
"as" => Token::AsKeyword,
"for" => Token::For,
"while" => Token::While,
"in" => Token::In,
_ => Token::Identifier(value),
};
variant

View File

@ -112,6 +112,9 @@ pub fn compile_module<'map>(
is_main,
};
#[cfg(debug_assertions)]
dbg!(&ast_module);
Ok(ast_module.process(module_id))
}

View File

@ -147,7 +147,7 @@ impl Display for Statement {
impl Display for StmtKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Let(var, mutable, block) => {
StmtKind::Let(var, mutable, block) => {
write!(
f,
"let{} {} = {}",
@ -156,9 +156,17 @@ impl Display for StmtKind {
block
)
}
Self::Set(var, expr) => write!(f, "{} = {}", var, expr),
Self::Import(n) => write!(f, "import {}", n),
Self::Expression(exp) => Display::fmt(exp, f),
StmtKind::Set(var, expr) => write!(f, "{} = {}", var, expr),
StmtKind::Import(n) => write!(f, "import {}", n),
StmtKind::Expression(exp) => Display::fmt(exp, f),
StmtKind::While(while_statement) => {
write!(
f,
"while {} {}",
while_statement.condition, while_statement.block,
)
}
}
}
}

View File

@ -9,6 +9,7 @@ pub enum ReturnTypeOther {
NoBlockReturn(Metadata),
IndexingNonArray(Metadata),
DerefNonBorrow(Metadata),
Loop,
}
impl TypeKind {
@ -374,6 +375,7 @@ impl Statement {
),
Import(_) => todo!(),
Expression(expression) => expression.return_type(refs, mod_id),
While(_) => Err(ReturnTypeOther::Loop),
}
}
@ -383,6 +385,7 @@ impl Statement {
StmtKind::Set(_, _) => None,
StmtKind::Import(_) => None,
StmtKind::Expression(expr) => expr.backing_var(),
StmtKind::While(_) => None,
}
}
}

View File

@ -188,7 +188,6 @@ impl<'map> Pass for LinkerPass<'map> {
.borrow_mut();
let func_name = unsafe { path.get_unchecked(1) };
let imported_mod_name = imported.name.clone();
let Some(func) = imported.functions.iter_mut().find(|f| f.name == *func_name)
else {

View File

@ -241,7 +241,7 @@ pub enum ReturnKind {
Soft,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
@ -338,6 +338,14 @@ pub enum StmtKind {
Set(Expression, Expression),
Import(Import),
Expression(Expression),
While(WhileStatement),
}
#[derive(Debug)]
pub struct WhileStatement {
pub condition: Expression,
pub block: Block,
pub meta: Metadata,
}
#[derive(Debug, Clone)]

View File

@ -390,10 +390,14 @@ impl Statement {
StmtKind::Set(_, expression) => {
expression.pass(pass, state, scope, mod_id)?;
}
StmtKind::Import(_) => {} // Never exists at this stage
StmtKind::Import(_) => {}
StmtKind::Expression(expression) => {
expression.pass(pass, state, scope, mod_id)?;
}
StmtKind::While(while_statement) => {
while_statement.condition.pass(pass, state, scope, mod_id)?;
while_statement.block.pass(pass, state, scope, mod_id)?;
}
}
pass.stmt(self, PassState::from(state, scope, Some(mod_id)))?;
@ -412,8 +416,9 @@ impl Statement {
.ok();
}
StmtKind::Set(_, _) => {}
StmtKind::Import(_) => {} // Never exists at this stage
StmtKind::Import(_) => {}
StmtKind::Expression(_) => {}
StmtKind::While(_) => {}
};
Ok(())
}

View File

@ -323,7 +323,7 @@ impl Block {
None
}
StmtKind::Import(_) => todo!(), // TODO
StmtKind::Import(_) => todo!(),
StmtKind::Expression(expression) => {
let res = expression.typecheck(&mut state, &typerefs, None);
state.or_else(res, TypeKind::Void, expression.1);
@ -335,6 +335,24 @@ impl Block {
None
}
}
StmtKind::While(WhileStatement {
condition,
block,
meta,
}) => {
let condition_ty =
condition.typecheck(&mut state, typerefs, Some(&TypeKind::Bool))?;
if condition_ty.assert_known(typerefs, &state)? != TypeKind::Bool {
state.note_errors(
&vec![ErrorKind::TypesIncompatible(condition_ty, TypeKind::Bool)],
*meta,
);
}
block.typecheck(&mut state, typerefs, None)?;
None
}
};
if let Some((ReturnKind::Hard, _)) = ret {

View File

@ -16,6 +16,7 @@ use super::{
IfExpression, Module, ReturnKind, StmtKind,
TypeKind::*,
VagueType::*,
WhileStatement,
};
/// Struct used to implement Type Inference, where an intermediary
@ -126,6 +127,12 @@ impl Block {
let expr_res = expr.infer_types(&mut state, &inner_refs);
state.ok(expr_res, expr.1);
}
StmtKind::While(WhileStatement {
condition, block, ..
}) => {
condition.infer_types(&mut state, &inner_refs)?;
block.infer_types(&mut state, &inner_refs)?;
}
};
}

View File

@ -120,3 +120,7 @@ fn strings_compiles_well() {
fn struct_compiles_well() {
test(include_str!("../../examples/struct.reid"), "test", 17);
}
#[test]
fn loops_compiles_well() {
test(include_str!("../../examples/loops.reid"), "test", 10);
}