Added support for the mod operator
This commit is contained in:
@@ -494,6 +494,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
BinaryExpression::Divide(l, r) => ("div", l, r),
|
BinaryExpression::Divide(l, r) => ("div", l, r),
|
||||||
BinaryExpression::Subtract(l, r) => ("sub", l, r),
|
BinaryExpression::Subtract(l, r) => ("sub", l, r),
|
||||||
BinaryExpression::Exponent(l, r) => ("pow", l, r),
|
BinaryExpression::Exponent(l, r) => ("pow", l, r),
|
||||||
|
BinaryExpression::Modulo(l, r) => ("mod", l, r),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compile LHS
|
// Compile LHS
|
||||||
@@ -747,4 +748,3 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
pub mod sys_call;
|
pub mod sys_call;
|
||||||
pub mod tree_node;
|
pub mod tree_node;
|
||||||
|
|
||||||
@@ -409,7 +412,7 @@ impl Parser {
|
|||||||
|
|
||||||
// Loop through operators, and build the binary expressions for multiplication and division operators
|
// Loop through operators, and build the binary expressions for multiplication and division operators
|
||||||
for (i, operator) in operators.iter().enumerate() {
|
for (i, operator) in operators.iter().enumerate() {
|
||||||
if operator == &Symbol::Asterisk || operator == &Symbol::Slash {
|
if matches!(operator, Symbol::Slash | Symbol::Asterisk | Symbol::Percent) {
|
||||||
let index = i - current_iteration;
|
let index = i - current_iteration;
|
||||||
let left = expressions.remove(index);
|
let left = expressions.remove(index);
|
||||||
let right = expressions.remove(index);
|
let right = expressions.remove(index);
|
||||||
@@ -423,6 +426,10 @@ impl Parser {
|
|||||||
index,
|
index,
|
||||||
Expression::Binary(BinaryExpression::Divide(boxed!(left), boxed!(right))),
|
Expression::Binary(BinaryExpression::Divide(boxed!(left), boxed!(right))),
|
||||||
),
|
),
|
||||||
|
Symbol::Percent => expressions.insert(
|
||||||
|
index,
|
||||||
|
Expression::Binary(BinaryExpression::Modulo(boxed!(left), boxed!(right))),
|
||||||
|
),
|
||||||
// safety: we have already checked for the operator
|
// safety: we have already checked for the operator
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@@ -431,7 +438,8 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove all the multiplication and division operators from the operators vector
|
// remove all the multiplication and division operators from the operators vector
|
||||||
operators.retain(|symbol| symbol != &Symbol::Asterisk && symbol != &Symbol::Slash);
|
operators
|
||||||
|
.retain(|symbol| !matches!(symbol, Symbol::Asterisk | Symbol::Percent | Symbol::Slash));
|
||||||
current_iteration = 0;
|
current_iteration = 0;
|
||||||
|
|
||||||
// Loop through operators, and build the binary expressions for addition and subtraction operators
|
// Loop through operators, and build the binary expressions for addition and subtraction operators
|
||||||
@@ -458,7 +466,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove all the addition and subtraction operators from the operators vector
|
// remove all the addition and subtraction operators from the operators vector
|
||||||
operators.retain(|symbol| symbol != &Symbol::Plus && symbol != &Symbol::Minus);
|
operators.retain(|symbol| !matches!(symbol, Symbol::Plus | Symbol::Minus));
|
||||||
|
|
||||||
// Ensure there is only one expression left in the expressions vector, and no operators left
|
// Ensure there is only one expression left in the expressions vector, and no operators left
|
||||||
if expressions.len() != 1 || !operators.is_empty() {
|
if expressions.len() != 1 || !operators.is_empty() {
|
||||||
@@ -921,137 +929,3 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
macro_rules! parser {
|
|
||||||
($input:expr) => {
|
|
||||||
Parser::new(Tokenizer::from($input.to_owned()))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unsupported_keywords() -> Result<()> {
|
|
||||||
let mut parser = parser!("enum x;");
|
|
||||||
assert!(parser.parse().is_err());
|
|
||||||
|
|
||||||
let mut parser = parser!("if x {}");
|
|
||||||
assert!(parser.parse().is_err());
|
|
||||||
|
|
||||||
let mut parser = parser!("else {}");
|
|
||||||
assert!(parser.parse().is_err());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_declarations() -> Result<()> {
|
|
||||||
let input = r#"
|
|
||||||
let x = 5;
|
|
||||||
// The below line should fail
|
|
||||||
let y = 234
|
|
||||||
"#;
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
|
||||||
let mut parser = Parser::new(tokenizer);
|
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
|
||||||
|
|
||||||
assert_eq!("(let x = 5)", expression.to_string());
|
|
||||||
|
|
||||||
assert!(parser.parse().is_err());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_block() -> Result<()> {
|
|
||||||
let input = r#"
|
|
||||||
{
|
|
||||||
let x = 5;
|
|
||||||
let y = 10;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
|
||||||
let mut parser = Parser::new(tokenizer);
|
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
|
||||||
|
|
||||||
assert_eq!("{ (let x = 5); (let y = 10); }", expression.to_string());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_function_expression() -> Result<()> {
|
|
||||||
let input = r#"
|
|
||||||
// This is a function. The parser is starting to get more complex
|
|
||||||
fn add(x, y) {
|
|
||||||
let z = x;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
|
||||||
let mut parser = Parser::new(tokenizer);
|
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
"(fn add(x, y) { { (let z = x); } })",
|
|
||||||
expression.to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_function_invocation() -> Result<()> {
|
|
||||||
let input = r#"
|
|
||||||
add();
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
|
||||||
let mut parser = Parser::new(tokenizer);
|
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
|
||||||
|
|
||||||
assert_eq!("add()", expression.to_string());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_priority_expression() -> Result<()> {
|
|
||||||
let input = r#"
|
|
||||||
let x = (4);
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let tokenizer = Tokenizer::from(input.to_owned());
|
|
||||||
let mut parser = Parser::new(tokenizer);
|
|
||||||
|
|
||||||
let expression = parser.parse()?.unwrap();
|
|
||||||
|
|
||||||
assert_eq!("(let x = (4))", expression.to_string());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_binary_expression() -> Result<()> {
|
|
||||||
let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap();
|
|
||||||
assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string());
|
|
||||||
|
|
||||||
let expr = parser!("2 ** 3 ** 4").parse()?.unwrap();
|
|
||||||
assert_eq!("(2 ** (3 ** 4))", expr.to_string());
|
|
||||||
|
|
||||||
let expr = parser!("45 * 2 - 15 / 5 + 5 ** 2").parse()?.unwrap();
|
|
||||||
assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string());
|
|
||||||
|
|
||||||
let expr = parser!("(5 - 2) * 10").parse()?.unwrap();
|
|
||||||
assert_eq!("(((5 - 2)) * 10)", expr.to_string());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
21
libs/parser/src/test/blocks.rs
Normal file
21
libs/parser/src/test/blocks.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use tokenizer::Tokenizer;
|
||||||
|
|
||||||
|
use crate::Parser;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_block() -> anyhow::Result<()> {
|
||||||
|
let mut parser = crate::parser!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
let x = 5;
|
||||||
|
let y = 10;
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let expression = parser.parse()?.unwrap();
|
||||||
|
|
||||||
|
assert_eq!("{ (let x = 5); (let y = 10); }", expression.to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
115
libs/parser/src/test/mod.rs
Normal file
115
libs/parser/src/test/mod.rs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! parser {
|
||||||
|
($input:expr) => {
|
||||||
|
Parser::new(Tokenizer::from($input.to_owned()))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mod blocks;
|
||||||
|
use super::Parser;
|
||||||
|
use super::Tokenizer;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unsupported_keywords() -> Result<()> {
|
||||||
|
let mut parser = parser!("enum x;");
|
||||||
|
assert!(parser.parse().is_err());
|
||||||
|
|
||||||
|
let mut parser = parser!("if x {}");
|
||||||
|
assert!(parser.parse().is_err());
|
||||||
|
|
||||||
|
let mut parser = parser!("else {}");
|
||||||
|
assert!(parser.parse().is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_declarations() -> Result<()> {
|
||||||
|
let input = r#"
|
||||||
|
let x = 5;
|
||||||
|
// The below line should fail
|
||||||
|
let y = 234
|
||||||
|
"#;
|
||||||
|
let tokenizer = Tokenizer::from(input.to_owned());
|
||||||
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
|
let expression = parser.parse()?.unwrap();
|
||||||
|
|
||||||
|
assert_eq!("(let x = 5)", expression.to_string());
|
||||||
|
|
||||||
|
assert!(parser.parse().is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_expression() -> Result<()> {
|
||||||
|
let input = r#"
|
||||||
|
// This is a function. The parser is starting to get more complex
|
||||||
|
fn add(x, y) {
|
||||||
|
let z = x;
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let tokenizer = Tokenizer::from(input.to_owned());
|
||||||
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
|
let expression = parser.parse()?.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"(fn add(x, y) { { (let z = x); } })",
|
||||||
|
expression.to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_invocation() -> Result<()> {
|
||||||
|
let input = r#"
|
||||||
|
add();
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let tokenizer = Tokenizer::from(input.to_owned());
|
||||||
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
|
let expression = parser.parse()?.unwrap();
|
||||||
|
|
||||||
|
assert_eq!("add()", expression.to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_priority_expression() -> Result<()> {
|
||||||
|
let input = r#"
|
||||||
|
let x = (4);
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let tokenizer = Tokenizer::from(input.to_owned());
|
||||||
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
|
let expression = parser.parse()?.unwrap();
|
||||||
|
|
||||||
|
assert_eq!("(let x = (4))", expression.to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_binary_expression() -> Result<()> {
|
||||||
|
let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap();
|
||||||
|
assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string());
|
||||||
|
|
||||||
|
let expr = parser!("2 ** 3 ** 4").parse()?.unwrap();
|
||||||
|
assert_eq!("(2 ** (3 ** 4))", expr.to_string());
|
||||||
|
|
||||||
|
let expr = parser!("45 * 2 - 15 / 5 + 5 ** 2").parse()?.unwrap();
|
||||||
|
assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string());
|
||||||
|
|
||||||
|
let expr = parser!("(5 - 2) * 10").parse()?.unwrap();
|
||||||
|
assert_eq!("(((5 - 2)) * 10)", expr.to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ pub enum BinaryExpression {
|
|||||||
Divide(Box<Expression>, Box<Expression>),
|
Divide(Box<Expression>, Box<Expression>),
|
||||||
Subtract(Box<Expression>, Box<Expression>),
|
Subtract(Box<Expression>, Box<Expression>),
|
||||||
Exponent(Box<Expression>, Box<Expression>),
|
Exponent(Box<Expression>, Box<Expression>),
|
||||||
|
Modulo(Box<Expression>, Box<Expression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for BinaryExpression {
|
impl std::fmt::Display for BinaryExpression {
|
||||||
@@ -33,6 +34,7 @@ impl std::fmt::Display for BinaryExpression {
|
|||||||
BinaryExpression::Divide(l, r) => write!(f, "({} / {})", l, r),
|
BinaryExpression::Divide(l, r) => write!(f, "({} / {})", l, r),
|
||||||
BinaryExpression::Subtract(l, r) => write!(f, "({} - {})", l, r),
|
BinaryExpression::Subtract(l, r) => write!(f, "({} - {})", l, r),
|
||||||
BinaryExpression::Exponent(l, r) => write!(f, "({} ** {})", l, r),
|
BinaryExpression::Exponent(l, r) => write!(f, "({} ** {})", l, r),
|
||||||
|
BinaryExpression::Modulo(l, r) => write!(f, "({} % {})", l, r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ impl Tokenizer {
|
|||||||
|
|
||||||
'.' => symbol!(Dot),
|
'.' => symbol!(Dot),
|
||||||
'^' => symbol!(Caret),
|
'^' => symbol!(Caret),
|
||||||
|
'%' => symbol!(Percent),
|
||||||
|
|
||||||
// multi-character symbols
|
// multi-character symbols
|
||||||
'<' if self.peek_next_char()? == Some('=') => {
|
'<' if self.peek_next_char()? == Some('=') => {
|
||||||
@@ -736,7 +737,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_symbol_parse() -> Result<()> {
|
fn test_symbol_parse() -> Result<()> {
|
||||||
let mut tokenizer = Tokenizer::from(String::from(
|
let mut tokenizer = Tokenizer::from(String::from(
|
||||||
"^ ! () [] {} , . ; : + - * / < > = != && || >= <=**",
|
"^ ! () [] {} , . ; : + - * / < > = != && || >= <=**%",
|
||||||
));
|
));
|
||||||
|
|
||||||
let expected_tokens = vec![
|
let expected_tokens = vec![
|
||||||
@@ -765,6 +766,7 @@ mod tests {
|
|||||||
TokenType::Symbol(Symbol::GreaterThanOrEqual),
|
TokenType::Symbol(Symbol::GreaterThanOrEqual),
|
||||||
TokenType::Symbol(Symbol::LessThanOrEqual),
|
TokenType::Symbol(Symbol::LessThanOrEqual),
|
||||||
TokenType::Symbol(Symbol::Exp),
|
TokenType::Symbol(Symbol::Exp),
|
||||||
|
TokenType::Symbol(Symbol::Percent),
|
||||||
];
|
];
|
||||||
|
|
||||||
for expected_token in expected_tokens {
|
for expected_token in expected_tokens {
|
||||||
|
|||||||
@@ -158,6 +158,8 @@ pub enum Symbol {
|
|||||||
Dot,
|
Dot,
|
||||||
/// Represents the `^` symbol
|
/// Represents the `^` symbol
|
||||||
Caret,
|
Caret,
|
||||||
|
/// Represents the `%` symbol
|
||||||
|
Percent,
|
||||||
|
|
||||||
// Double Character Symbols
|
// Double Character Symbols
|
||||||
/// Represents the `==` symbol
|
/// Represents the `==` symbol
|
||||||
@@ -180,7 +182,12 @@ impl Symbol {
|
|||||||
pub fn is_operator(&self) -> bool {
|
pub fn is_operator(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Symbol::Plus | Symbol::Minus | Symbol::Asterisk | Symbol::Slash | Symbol::Exp
|
Symbol::Plus
|
||||||
|
| Symbol::Minus
|
||||||
|
| Symbol::Asterisk
|
||||||
|
| Symbol::Slash
|
||||||
|
| Symbol::Exp
|
||||||
|
| Symbol::Percent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user