From b9459a19bb0ca5a12995def7b9b9c01e9eb1b78e Mon Sep 17 00:00:00 2001 From: sofia Date: Wed, 23 Jul 2025 14:59:51 +0300 Subject: [PATCH] Add div/mod parsing --- README.md | 5 ++++ examples/div_mod.reid | 6 ++++ reid/src/ast/mod.rs | 8 ++++-- reid/src/ast/parse.rs | 2 ++ reid/src/ast/process.rs | 2 ++ reid/src/codegen.rs | 8 ++++++ reid/src/lexer.rs | 28 ++++++++++++++++--- reid/src/mir/fmt.rs | 2 ++ reid/src/mir/implement.rs | 58 +++++++++++++++++++++++++++++++-------- reid/src/mir/mod.rs | 2 ++ reid/src/mir/typecheck.rs | 4 ++- reid/src/token_stream.rs | 2 +- reid/tests/e2e.rs | 18 ++++++------ 13 files changed, 116 insertions(+), 29 deletions(-) create mode 100644 examples/div_mod.reid diff --git a/README.md b/README.md index a104013..a6c4706 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,16 @@ Currently missing big features (TODOs) are: - Loops - Debug Information (PARTIALLY DONE) - Ability to specify types in literals and variable definitions +- Intrinsic functions +- Not-Unary Big features that I want later but are not necessary: - Associated functions - User-defined binary operations - Asymmetric binary operations (e.g. string + u32) +- Error handling +- Lexing & parsing of whitespace and comments as well +- LSP implementation Smaller features: - Hex-numbers diff --git a/examples/div_mod.reid b/examples/div_mod.reid new file mode 100644 index 0000000..36248a0 --- /dev/null +++ b/examples/div_mod.reid @@ -0,0 +1,6 @@ +// Arithmetic, function calls and imports! + +fn main() -> u16 { + + return (50 / 5 + 52 % 5); +} diff --git a/reid/src/ast/mod.rs b/reid/src/ast/mod.rs index bee921e..ff4ab7a 100644 --- a/reid/src/ast/mod.rs +++ b/reid/src/ast/mod.rs @@ -81,6 +81,8 @@ pub enum BinaryOperator { Add, Minus, Mult, + Div, + Mod, And, LT, @@ -95,9 +97,11 @@ impl BinaryOperator { pub fn get_precedence(&self) -> i8 { use BinaryOperator::*; match &self { + Minus => 5, Add => 10, - Minus => 10, - Mult => 20, + Mult => 15, + Div => 20, + Mod => 20, And => 100, LT => 100, LE => 100, diff --git a/reid/src/ast/parse.rs b/reid/src/ast/parse.rs index dd0d1ef..4141abc 100644 --- a/reid/src/ast/parse.rs +++ b/reid/src/ast/parse.rs @@ -350,6 +350,8 @@ impl Parse for BinaryOperator { (Some(Token::Plus), _) => BinaryOperator::Add, (Some(Token::Minus), _) => BinaryOperator::Minus, (Some(Token::Star), _) => BinaryOperator::Mult, + (Some(Token::Slash), _) => BinaryOperator::Div, + (Some(Token::Percent), _) => BinaryOperator::Mod, (_, _) => Err(stream.expected_err("expected operator")?)?, }) } diff --git a/reid/src/ast/process.rs b/reid/src/ast/process.rs index a3740d2..4bebf42 100644 --- a/reid/src/ast/process.rs +++ b/reid/src/ast/process.rs @@ -286,6 +286,8 @@ impl ast::BinaryOperator { ast::BinaryOperator::Minus => mir::BinaryOperator::Minus, ast::BinaryOperator::Mult => mir::BinaryOperator::Mult, ast::BinaryOperator::And => mir::BinaryOperator::And, + ast::BinaryOperator::Div => mir::BinaryOperator::Div, + ast::BinaryOperator::Mod => mir::BinaryOperator::Mod, ast::BinaryOperator::LT => mir::BinaryOperator::Cmp(mir::CmpOperator::LT), ast::BinaryOperator::LE => mir::BinaryOperator::Cmp(mir::CmpOperator::LE), ast::BinaryOperator::GT => mir::BinaryOperator::Cmp(mir::CmpOperator::GT), diff --git a/reid/src/codegen.rs b/reid/src/codegen.rs index 21ad10e..ee84f13 100644 --- a/reid/src/codegen.rs +++ b/reid/src/codegen.rs @@ -659,6 +659,14 @@ impl mir::Expression { (mir::BinaryOperator::And, _, _) => Instr::And(lhs, rhs), (mir::BinaryOperator::Cmp(i), _, false) => Instr::ICmp(i.predicate(), lhs, rhs), (mir::BinaryOperator::Cmp(i), _, true) => Instr::FCmp(i.predicate(), lhs, rhs), + (mir::BinaryOperator::Div, true, true) => todo!(), + (mir::BinaryOperator::Div, true, false) => todo!(), + (mir::BinaryOperator::Div, false, true) => todo!(), + (mir::BinaryOperator::Div, false, false) => todo!(), + (mir::BinaryOperator::Mod, true, true) => todo!(), + (mir::BinaryOperator::Mod, true, false) => todo!(), + (mir::BinaryOperator::Mod, false, true) => todo!(), + (mir::BinaryOperator::Mod, false, false) => todo!(), }; Some(StackValue( StackValueKind::Immutable(scope.block.build(instr).unwrap()), diff --git a/reid/src/lexer.rs b/reid/src/lexer.rs index c3f16fe..4032f4c 100644 --- a/reid/src/lexer.rs +++ b/reid/src/lexer.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, str::Chars}; static DECIMAL_NUMERICS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; -#[derive(Debug, Eq, PartialEq, Clone, PartialOrd, Ord)] +#[derive(Eq, PartialEq, Clone, PartialOrd, Ord)] pub enum Token { /// Values Identifier(String), @@ -56,6 +56,10 @@ pub enum Token { Star, /// `-` Minus, + /// `/` + Slash, + /// `%` + Percent, /// `>` GreaterThan, @@ -83,6 +87,8 @@ pub enum Token { /// `.` Dot, + Unknown(char), + Eof, } @@ -149,6 +155,18 @@ impl ToString for Token { Token::Comma => String::from(','), Token::Dot => String::from('.'), Token::Eof => String::new(), + Token::Slash => String::from('/'), + Token::Percent => String::from('%'), + Token::Unknown(val) => val.to_string(), + } + } +} + +impl std::fmt::Debug for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Unknown(value) => write!(f, "Unknown(\'{}\')", value.to_string()), + _ => write!(f, "{}", self.to_string()), } } } @@ -163,7 +181,7 @@ pub struct FullToken { impl Debug for FullToken { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( - "{:?} (Ln {}, Col {})", + "Token({:?}) (Ln {}, Col {})", self.token, self.position.1, self.position.0 )) } @@ -343,8 +361,10 @@ pub fn tokenize>(to_tokenize: T) -> Result, Error '}' => Token::BraceClose, ',' => Token::Comma, '.' => Token::Dot, - // Invalid token - _ => Err(Error::InvalidToken(*character, cursor.position))?, + '/' => Token::Slash, + '%' => Token::Percent, + // Unknown token + value => Token::Unknown(*value), }; tokens.push(FullToken { diff --git a/reid/src/mir/fmt.rs b/reid/src/mir/fmt.rs index f2c04cc..e850fb1 100644 --- a/reid/src/mir/fmt.rs +++ b/reid/src/mir/fmt.rs @@ -299,6 +299,8 @@ impl Display for BinaryOperator { BinaryOperator::Mult => write!(f, "*"), BinaryOperator::And => write!(f, "&&"), BinaryOperator::Cmp(op) => Display::fmt(op, f), + BinaryOperator::Div => write!(f, "/"), + BinaryOperator::Mod => write!(f, "%"), } } } diff --git a/reid/src/mir/implement.rs b/reid/src/mir/implement.rs index 361a3f2..7f36ec3 100644 --- a/reid/src/mir/implement.rs +++ b/reid/src/mir/implement.rs @@ -104,14 +104,20 @@ impl TypeKind { BinaryOperator::Mult => self.clone(), BinaryOperator::And => TypeKind::Bool, BinaryOperator::Cmp(_) => TypeKind::Bool, + BinaryOperator::Div => self.clone(), + BinaryOperator::Mod => self.clone(), } } + /// Reverse of binop_type, where the given hint is the known required output + /// type of the binop, and the output is the hint for the lhs/rhs type. pub fn binop_hint(&self, op: &BinaryOperator) -> Option { match op { - BinaryOperator::Add | BinaryOperator::Minus | BinaryOperator::Mult => { - Some(self.clone()) - } + BinaryOperator::Add + | BinaryOperator::Minus + | BinaryOperator::Mult + | BinaryOperator::Div + | BinaryOperator::Mod => Some(self.clone()), BinaryOperator::And => None, BinaryOperator::Cmp(_) => None, } @@ -465,12 +471,16 @@ impl Expression { } } - pub fn is_zero(&self) -> Option { - Some(self.num_value()? == 0) + pub fn is_zero(&self) -> Result, ErrorKind> { + if let Some(val) = self.num_value()? { + Ok(Some(val == 0)) + } else { + Ok(None) + } } - pub fn num_value(&self) -> Option { - match &self.0 { + pub fn num_value(&self) -> Result, ErrorKind> { + Ok(match &self.0 { ExprKind::Variable(_) => None, ExprKind::Indexed(..) => None, ExprKind::Accessed(..) => None, @@ -478,22 +488,46 @@ impl Expression { ExprKind::Struct(..) => None, ExprKind::Literal(literal) => literal.num_value(), ExprKind::BinOp(op, lhs, rhs) => match op { - BinaryOperator::Add => Some(lhs.num_value()? + rhs.num_value()?), - BinaryOperator::Minus => Some(lhs.num_value()? - rhs.num_value()?), - BinaryOperator::Mult => Some(lhs.num_value()? * rhs.num_value()?), + BinaryOperator::Add => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a + b), + BinaryOperator::Minus => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a - b), + BinaryOperator::Mult => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a * b), BinaryOperator::And => None, BinaryOperator::Cmp(_) => None, + BinaryOperator::Div => { + let rhs_value = rhs.num_value()?; + if rhs_value == Some(0) { + Err(ErrorKind::DivideZero)? + } + maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a / b) + } + BinaryOperator::Mod => { + let rhs_value = rhs.num_value()?; + if rhs_value == Some(0) { + Err(ErrorKind::DivideZero)? + } + maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a % b) + } }, ExprKind::FunctionCall(..) => None, ExprKind::If(_) => None, ExprKind::Block(_) => None, ExprKind::Borrow(_, _) => None, ExprKind::Deref(_) => None, - ExprKind::CastTo(expression, _) => expression.num_value(), - } + ExprKind::CastTo(expression, _) => expression.num_value()?, + }) } } +fn maybe(lhs: Option, rhs: Option, fun: T) -> Option +where + T: FnOnce(i128, i128) -> i128, +{ + if let (Some(lhs), Some(rhs)) = (lhs, rhs) { + Some(fun(lhs, rhs)) + } else { + None + } +} impl IfExpression { pub fn return_type( &self, diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index 24578c2..99fea98 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -218,6 +218,8 @@ pub enum BinaryOperator { Add, Minus, Mult, + Div, + Mod, And, Cmp(CmpOperator), } diff --git a/reid/src/mir/typecheck.rs b/reid/src/mir/typecheck.rs index ba606ff..9d8dfc0 100644 --- a/reid/src/mir/typecheck.rs +++ b/reid/src/mir/typecheck.rs @@ -68,6 +68,8 @@ pub enum ErrorKind { NegativeUnsignedValue(TypeKind), #[error("Cannot cast type {0} into type {1}!")] NotCastableTo(TypeKind, TypeKind), + #[error("Cannot divide by zero")] + DivideZero, } /// Struct used to implement a type-checking pass that can be performed on the @@ -426,7 +428,7 @@ impl Expression { let both_t = lhs_type.collapse_into(&rhs_type)?; if *op == BinaryOperator::Minus && !lhs_type.signed() { - if let (Some(lhs_val), Some(rhs_val)) = (lhs.num_value(), rhs.num_value()) { + if let (Some(lhs_val), Some(rhs_val)) = (lhs.num_value()?, rhs.num_value()?) { if lhs_val < rhs_val { return Err(ErrorKind::NegativeUnsignedValue(lhs_type)); } diff --git a/reid/src/token_stream.rs b/reid/src/token_stream.rs index 1d6043f..2cc9d9d 100644 --- a/reid/src/token_stream.rs +++ b/reid/src/token_stream.rs @@ -235,7 +235,7 @@ impl std::iter::Sum for TokenRange { #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Error { - #[error("Expected {} got \"{}\"", .0, .1.to_string())] + #[error("Expected {} got \"{:?}\"", .0, .1)] Expected(String, Token, TokenRange), #[error("Source file contains no tokens")] FileEmpty, diff --git a/reid/tests/e2e.rs b/reid/tests/e2e.rs index 4502004..f33001f 100644 --- a/reid/tests/e2e.rs +++ b/reid/tests/e2e.rs @@ -18,15 +18,15 @@ fn test(source: &str, name: &str) { )); } -pub static ARRAY: &str = include_str!("../../reid_src/array.reid"); -pub static FIBONACCI: &str = include_str!("../../reid_src/fibonacci.reid"); -pub static HELLO_WORLD: &str = include_str!("../../reid_src/hello_world.reid"); -pub static MUTABLE: &str = include_str!("../../reid_src/mutable.reid"); -pub static STRINGS: &str = include_str!("../../reid_src/strings.reid"); -pub static STRUCTS: &str = include_str!("../../reid_src/struct.reid"); -pub static ARRAY_STRUCTS: &str = include_str!("../../reid_src/array_structs.reid"); -pub static BORROW: &str = include_str!("../../reid_src/borrow.reid"); -pub static ARITHMETIC: &str = include_str!("../../reid_src/arithmetic.reid"); +pub static ARRAY: &str = include_str!("../../examples/array.reid"); +pub static FIBONACCI: &str = include_str!("../../examples/fibonacci.reid"); +pub static HELLO_WORLD: &str = include_str!("../../examples/hello_world.reid"); +pub static MUTABLE: &str = include_str!("../../examples/mutable.reid"); +pub static STRINGS: &str = include_str!("../../examples/strings.reid"); +pub static STRUCTS: &str = include_str!("../../examples/struct.reid"); +pub static ARRAY_STRUCTS: &str = include_str!("../../examples/array_structs.reid"); +pub static BORROW: &str = include_str!("../../examples/borrow.reid"); +pub static ARITHMETIC: &str = include_str!("../../examples/arithmetic.reid"); #[test] fn array_compiles_well() {