Add div/mod parsing

This commit is contained in:
Sofia 2025-07-23 14:59:51 +03:00
parent 691c91504b
commit b9459a19bb
13 changed files with 116 additions and 29 deletions

View File

@ -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

6
examples/div_mod.reid Normal file
View File

@ -0,0 +1,6 @@
// Arithmetic, function calls and imports!
fn main() -> u16 {
return (50 / 5 + 52 % 5);
}

View File

@ -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,

View File

@ -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")?)?,
})
}

View File

@ -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),

View File

@ -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()),

View File

@ -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<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, 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 {

View File

@ -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, "%"),
}
}
}

View File

@ -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<TypeKind> {
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<bool> {
Some(self.num_value()? == 0)
pub fn is_zero(&self) -> Result<Option<bool>, ErrorKind> {
if let Some(val) = self.num_value()? {
Ok(Some(val == 0))
} else {
Ok(None)
}
}
pub fn num_value(&self) -> Option<i128> {
match &self.0 {
pub fn num_value(&self) -> Result<Option<i128>, 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<T>(lhs: Option<i128>, rhs: Option<i128>, fun: T) -> Option<i128>
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,

View File

@ -218,6 +218,8 @@ pub enum BinaryOperator {
Add,
Minus,
Mult,
Div,
Mod,
And,
Cmp(CmpOperator),
}

View File

@ -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));
}

View File

@ -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,

View File

@ -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() {