Add div/mod parsing
This commit is contained in:
parent
691c91504b
commit
b9459a19bb
@ -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
6
examples/div_mod.reid
Normal file
@ -0,0 +1,6 @@
|
||||
// Arithmetic, function calls and imports!
|
||||
|
||||
fn main() -> u16 {
|
||||
|
||||
return (50 / 5 + 52 % 5);
|
||||
}
|
@ -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,
|
||||
|
@ -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")?)?,
|
||||
})
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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()),
|
||||
|
@ -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 {
|
||||
|
@ -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, "%"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -218,6 +218,8 @@ pub enum BinaryOperator {
|
||||
Add,
|
||||
Minus,
|
||||
Mult,
|
||||
Div,
|
||||
Mod,
|
||||
And,
|
||||
Cmp(CmpOperator),
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user