diff --git a/examples/loops.reid b/examples/loops.reid new file mode 100644 index 0000000..2c07934 --- /dev/null +++ b/examples/loops.reid @@ -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; +} diff --git a/reid-llvm-lib/src/builder.rs b/reid-llvm-lib/src/builder.rs index 949a147..67de72b 100644 --- a/reid-llvm-lib/src/builder.rs +++ b/reid-llvm-lib/src/builder.rs @@ -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 { diff --git a/reid/src/ast/mod.rs b/reid/src/ast/mod.rs index ff4ab7a..3995f95 100644 --- a/reid/src/ast/mod.rs +++ b/reid/src/ast/mod.rs @@ -178,6 +178,8 @@ pub enum BlockLevelStatement { }, Expression(Expression), Return(ReturnType, Expression), + ForLoop(String, TokenRange, Expression, Expression, Block), + WhileLoop(Expression, Block), } #[derive(Debug, Clone)] diff --git a/reid/src/ast/parse.rs b/reid/src/ast/parse.rs index 4141abc..757cabe 100644 --- a/reid/src/ast/parse.rs +++ b/reid/src/ast/parse.rs @@ -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::() { @@ -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::()?; + 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::()?; + 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 { + 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 { + stream.expect(Token::While)?; + + Ok(WhileStatement(stream.parse()?, stream.parse()?)) + } +} #[derive(Debug)] pub struct SetStatement(Expression, Expression, TokenRange); diff --git a/reid/src/ast/process.rs b/reid/src/ast/process.rs index 8ebd252..4929cad 100644 --- a/reid/src/ast/process.rs +++ b/reid/src/ast/process.rs @@ -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))); diff --git a/reid/src/codegen.rs b/reid/src/codegen.rs index 6abc671..421f2ab 100644 --- a/reid/src/codegen.rs +++ b/reid/src/codegen.rs @@ -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"); }; diff --git a/reid/src/lexer.rs b/reid/src/lexer.rs index 4032f4c..97f1f6f 100644 --- a/reid/src/lexer.rs +++ b/reid/src/lexer.rs @@ -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>(to_tokenize: T) -> Result, Error "pub" => Token::PubKeyword, "struct" => Token::Struct, "as" => Token::AsKeyword, + "for" => Token::For, + "while" => Token::While, + "in" => Token::In, _ => Token::Identifier(value), }; variant diff --git a/reid/src/lib.rs b/reid/src/lib.rs index 58c30ec..81e07fa 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -112,6 +112,9 @@ pub fn compile_module<'map>( is_main, }; + #[cfg(debug_assertions)] + dbg!(&ast_module); + Ok(ast_module.process(module_id)) } diff --git a/reid/src/mir/fmt.rs b/reid/src/mir/fmt.rs index 53e5417..1d1037e 100644 --- a/reid/src/mir/fmt.rs +++ b/reid/src/mir/fmt.rs @@ -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, + ) + } } } } diff --git a/reid/src/mir/implement.rs b/reid/src/mir/implement.rs index 7f36ec3..e4670c8 100644 --- a/reid/src/mir/implement.rs +++ b/reid/src/mir/implement.rs @@ -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, } } } diff --git a/reid/src/mir/linker.rs b/reid/src/mir/linker.rs index 2d9b249..e404c67 100644 --- a/reid/src/mir/linker.rs +++ b/reid/src/mir/linker.rs @@ -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 { diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index 1372bbd..d432090 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -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)] diff --git a/reid/src/mir/pass.rs b/reid/src/mir/pass.rs index 5409d80..f67ad74 100644 --- a/reid/src/mir/pass.rs +++ b/reid/src/mir/pass.rs @@ -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(()) } diff --git a/reid/src/mir/typecheck.rs b/reid/src/mir/typecheck.rs index 9d8dfc0..8e8f297 100644 --- a/reid/src/mir/typecheck.rs +++ b/reid/src/mir/typecheck.rs @@ -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 { diff --git a/reid/src/mir/typeinference.rs b/reid/src/mir/typeinference.rs index 35bf42f..cfb1bcb 100644 --- a/reid/src/mir/typeinference.rs +++ b/reid/src/mir/typeinference.rs @@ -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)?; + } }; } diff --git a/reid/tests/e2e.rs b/reid/tests/e2e.rs index abf94ef..5685490 100644 --- a/reid/tests/e2e.rs +++ b/reid/tests/e2e.rs @@ -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); +}