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 - Loops
- Debug Information (PARTIALLY DONE) - Debug Information (PARTIALLY DONE)
- Ability to specify types in literals and variable definitions - Ability to specify types in literals and variable definitions
- Intrinsic functions
- Not-Unary
Big features that I want later but are not necessary: Big features that I want later but are not necessary:
- Associated functions - Associated functions
- User-defined binary operations - User-defined binary operations
- Asymmetric binary operations (e.g. string + u32) - Asymmetric binary operations (e.g. string + u32)
- Error handling
- Lexing & parsing of whitespace and comments as well
- LSP implementation
Smaller features: Smaller features:
- Hex-numbers - 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, Add,
Minus, Minus,
Mult, Mult,
Div,
Mod,
And, And,
LT, LT,
@ -95,9 +97,11 @@ impl BinaryOperator {
pub fn get_precedence(&self) -> i8 { pub fn get_precedence(&self) -> i8 {
use BinaryOperator::*; use BinaryOperator::*;
match &self { match &self {
Minus => 5,
Add => 10, Add => 10,
Minus => 10, Mult => 15,
Mult => 20, Div => 20,
Mod => 20,
And => 100, And => 100,
LT => 100, LT => 100,
LE => 100, LE => 100,

View File

@ -350,6 +350,8 @@ impl Parse for BinaryOperator {
(Some(Token::Plus), _) => BinaryOperator::Add, (Some(Token::Plus), _) => BinaryOperator::Add,
(Some(Token::Minus), _) => BinaryOperator::Minus, (Some(Token::Minus), _) => BinaryOperator::Minus,
(Some(Token::Star), _) => BinaryOperator::Mult, (Some(Token::Star), _) => BinaryOperator::Mult,
(Some(Token::Slash), _) => BinaryOperator::Div,
(Some(Token::Percent), _) => BinaryOperator::Mod,
(_, _) => Err(stream.expected_err("expected operator")?)?, (_, _) => Err(stream.expected_err("expected operator")?)?,
}) })
} }

View File

@ -286,6 +286,8 @@ impl ast::BinaryOperator {
ast::BinaryOperator::Minus => mir::BinaryOperator::Minus, ast::BinaryOperator::Minus => mir::BinaryOperator::Minus,
ast::BinaryOperator::Mult => mir::BinaryOperator::Mult, ast::BinaryOperator::Mult => mir::BinaryOperator::Mult,
ast::BinaryOperator::And => mir::BinaryOperator::And, 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::LT => mir::BinaryOperator::Cmp(mir::CmpOperator::LT),
ast::BinaryOperator::LE => mir::BinaryOperator::Cmp(mir::CmpOperator::LE), ast::BinaryOperator::LE => mir::BinaryOperator::Cmp(mir::CmpOperator::LE),
ast::BinaryOperator::GT => mir::BinaryOperator::Cmp(mir::CmpOperator::GT), 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::And, _, _) => Instr::And(lhs, rhs),
(mir::BinaryOperator::Cmp(i), _, false) => Instr::ICmp(i.predicate(), 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::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( Some(StackValue(
StackValueKind::Immutable(scope.block.build(instr).unwrap()), 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']; 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 { pub enum Token {
/// Values /// Values
Identifier(String), Identifier(String),
@ -56,6 +56,10 @@ pub enum Token {
Star, Star,
/// `-` /// `-`
Minus, Minus,
/// `/`
Slash,
/// `%`
Percent,
/// `>` /// `>`
GreaterThan, GreaterThan,
@ -83,6 +87,8 @@ pub enum Token {
/// `.` /// `.`
Dot, Dot,
Unknown(char),
Eof, Eof,
} }
@ -149,6 +155,18 @@ impl ToString for Token {
Token::Comma => String::from(','), Token::Comma => String::from(','),
Token::Dot => String::from('.'), Token::Dot => String::from('.'),
Token::Eof => String::new(), 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 { impl Debug for FullToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!( f.write_fmt(format_args!(
"{:?} (Ln {}, Col {})", "Token({:?}) (Ln {}, Col {})",
self.token, self.position.1, self.position.0 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::BraceClose,
',' => Token::Comma, ',' => Token::Comma,
'.' => Token::Dot, '.' => Token::Dot,
// Invalid token '/' => Token::Slash,
_ => Err(Error::InvalidToken(*character, cursor.position))?, '%' => Token::Percent,
// Unknown token
value => Token::Unknown(*value),
}; };
tokens.push(FullToken { tokens.push(FullToken {

View File

@ -299,6 +299,8 @@ impl Display for BinaryOperator {
BinaryOperator::Mult => write!(f, "*"), BinaryOperator::Mult => write!(f, "*"),
BinaryOperator::And => write!(f, "&&"), BinaryOperator::And => write!(f, "&&"),
BinaryOperator::Cmp(op) => Display::fmt(op, 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::Mult => self.clone(),
BinaryOperator::And => TypeKind::Bool, BinaryOperator::And => TypeKind::Bool,
BinaryOperator::Cmp(_) => 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> { pub fn binop_hint(&self, op: &BinaryOperator) -> Option<TypeKind> {
match op { match op {
BinaryOperator::Add | BinaryOperator::Minus | BinaryOperator::Mult => { BinaryOperator::Add
Some(self.clone()) | BinaryOperator::Minus
} | BinaryOperator::Mult
| BinaryOperator::Div
| BinaryOperator::Mod => Some(self.clone()),
BinaryOperator::And => None, BinaryOperator::And => None,
BinaryOperator::Cmp(_) => None, BinaryOperator::Cmp(_) => None,
} }
@ -465,12 +471,16 @@ impl Expression {
} }
} }
pub fn is_zero(&self) -> Option<bool> { pub fn is_zero(&self) -> Result<Option<bool>, ErrorKind> {
Some(self.num_value()? == 0) if let Some(val) = self.num_value()? {
Ok(Some(val == 0))
} else {
Ok(None)
}
} }
pub fn num_value(&self) -> Option<i128> { pub fn num_value(&self) -> Result<Option<i128>, ErrorKind> {
match &self.0 { Ok(match &self.0 {
ExprKind::Variable(_) => None, ExprKind::Variable(_) => None,
ExprKind::Indexed(..) => None, ExprKind::Indexed(..) => None,
ExprKind::Accessed(..) => None, ExprKind::Accessed(..) => None,
@ -478,22 +488,46 @@ impl Expression {
ExprKind::Struct(..) => None, ExprKind::Struct(..) => None,
ExprKind::Literal(literal) => literal.num_value(), ExprKind::Literal(literal) => literal.num_value(),
ExprKind::BinOp(op, lhs, rhs) => match op { ExprKind::BinOp(op, lhs, rhs) => match op {
BinaryOperator::Add => Some(lhs.num_value()? + rhs.num_value()?), BinaryOperator::Add => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a + b),
BinaryOperator::Minus => Some(lhs.num_value()? - rhs.num_value()?), BinaryOperator::Minus => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a - b),
BinaryOperator::Mult => Some(lhs.num_value()? * rhs.num_value()?), BinaryOperator::Mult => maybe(lhs.num_value()?, rhs.num_value()?, |a, b| a * b),
BinaryOperator::And => None, BinaryOperator::And => None,
BinaryOperator::Cmp(_) => 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::FunctionCall(..) => None,
ExprKind::If(_) => None, ExprKind::If(_) => None,
ExprKind::Block(_) => None, ExprKind::Block(_) => None,
ExprKind::Borrow(_, _) => None, ExprKind::Borrow(_, _) => None,
ExprKind::Deref(_) => 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 { impl IfExpression {
pub fn return_type( pub fn return_type(
&self, &self,

View File

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

View File

@ -68,6 +68,8 @@ pub enum ErrorKind {
NegativeUnsignedValue(TypeKind), NegativeUnsignedValue(TypeKind),
#[error("Cannot cast type {0} into type {1}!")] #[error("Cannot cast type {0} into type {1}!")]
NotCastableTo(TypeKind, TypeKind), NotCastableTo(TypeKind, TypeKind),
#[error("Cannot divide by zero")]
DivideZero,
} }
/// Struct used to implement a type-checking pass that can be performed on the /// 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)?; let both_t = lhs_type.collapse_into(&rhs_type)?;
if *op == BinaryOperator::Minus && !lhs_type.signed() { 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 { if lhs_val < rhs_val {
return Err(ErrorKind::NegativeUnsignedValue(lhs_type)); 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)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error { pub enum Error {
#[error("Expected {} got \"{}\"", .0, .1.to_string())] #[error("Expected {} got \"{:?}\"", .0, .1)]
Expected(String, Token, TokenRange), Expected(String, Token, TokenRange),
#[error("Source file contains no tokens")] #[error("Source file contains no tokens")]
FileEmpty, 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 ARRAY: &str = include_str!("../../examples/array.reid");
pub static FIBONACCI: &str = include_str!("../../reid_src/fibonacci.reid"); pub static FIBONACCI: &str = include_str!("../../examples/fibonacci.reid");
pub static HELLO_WORLD: &str = include_str!("../../reid_src/hello_world.reid"); pub static HELLO_WORLD: &str = include_str!("../../examples/hello_world.reid");
pub static MUTABLE: &str = include_str!("../../reid_src/mutable.reid"); pub static MUTABLE: &str = include_str!("../../examples/mutable.reid");
pub static STRINGS: &str = include_str!("../../reid_src/strings.reid"); pub static STRINGS: &str = include_str!("../../examples/strings.reid");
pub static STRUCTS: &str = include_str!("../../reid_src/struct.reid"); pub static STRUCTS: &str = include_str!("../../examples/struct.reid");
pub static ARRAY_STRUCTS: &str = include_str!("../../reid_src/array_structs.reid"); pub static ARRAY_STRUCTS: &str = include_str!("../../examples/array_structs.reid");
pub static BORROW: &str = include_str!("../../reid_src/borrow.reid"); pub static BORROW: &str = include_str!("../../examples/borrow.reid");
pub static ARITHMETIC: &str = include_str!("../../reid_src/arithmetic.reid"); pub static ARITHMETIC: &str = include_str!("../../examples/arithmetic.reid");
#[test] #[test]
fn array_compiles_well() { fn array_compiles_well() {