binary expressions are working fully now

This commit is contained in:
2024-11-22 17:24:58 -07:00
parent e9675cd5d2
commit b5ee169a75
9 changed files with 203 additions and 50 deletions

View File

@@ -1,3 +1,5 @@
#![feature(error_generic_member_access)]
mod parser;
mod tokenizer;
@@ -19,13 +21,13 @@ enum StationlangError {
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// What file should be compiled. If not set, input will be read from stdin
/// What file should be compiled. If not set, input will be read from stdin.
#[arg(short, long)]
input_file: Option<String>,
/// The default stack size for the program
/// The stack size for the compiled program. Compilation will fail if the compiler detects that the program will exceed this stack size.
#[arg(short, long, default_value_t = 512)]
stack_size: usize,
/// The output file for the compiled program. If not set, output will go to stdout
/// The output file for the compiled program. If not set, output will go to stdout.
#[arg(short, long)]
output_file: Option<String>,
}

View File

@@ -4,7 +4,10 @@ use crate::tokenizer::{
token::{Keyword, Symbol, Token, TokenType},
Tokenizer, TokenizerBuffer, TokenizerError,
};
use std::io::SeekFrom;
use std::{
backtrace::{self, Backtrace},
io::SeekFrom,
};
use thiserror::Error;
use tree_node::*;
@@ -13,7 +16,11 @@ pub enum ParseError {
#[error(transparent)]
TokenizerError(#[from] TokenizerError),
#[error("Unexpected token\n\nLine: {0}, Column: {1}\nToken: {2}\n", token.line, token.column, token.token_type)]
UnexpectedToken { token: Token },
UnexpectedToken {
token: Token,
#[backtrace]
backtrace: std::backtrace::Backtrace,
},
#[error("Duplicated Identifer\n\nLine: {0}, Column: {1}\nToken: {2}\n", token.line, token.column, token.token_type)]
DuplicateIdentifier { token: Token },
#[error("Invalid Syntax\n\nLine: {0}, Column: {1}\nReason: {reason}", token.line, token.column)]
@@ -49,6 +56,7 @@ macro_rules! extract_token_data {
_ => {
return Err(ParseError::UnexpectedToken {
token: $token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
})
}
}
@@ -59,6 +67,7 @@ macro_rules! extract_token_data {
_ => {
return Err(ParseError::UnexpectedToken {
token: $token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
})
}
}
@@ -194,6 +203,7 @@ impl Parser {
_ => {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
})
}
});
@@ -202,11 +212,16 @@ impl Parser {
return Ok(None);
};
// check if the next or current token is an operator
if self_matches_peek!(self, TokenType::Symbol(s) if s.is_operator()) {
return Ok(Some(Expression::BinaryExpression(self.binary(expr)?)));
}
// step 2: check if the next token is an operator and if we should parse a binary expression with the previous expression
// This is an edge case. We need to move back one token if the current token is an operator
// so the binary expression can pick up the operator
else if self_matches_current!(self, TokenType::Symbol(s) if s.is_operator()) {
self.tokenizer.seek(SeekFrom::Current(-1))?;
return Ok(Some(Expression::BinaryExpression(self.binary(expr)?)));
}
Ok(Some(expr))
}
@@ -235,22 +250,13 @@ impl Parser {
}
_ => Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
}),
}
}
/// Handles mathmatical expressions in the explicit order of PEMDAS
fn binary(&mut self, previous: Expression) -> Result<BinaryExpression, ParseError> {
macro_rules! min {
($a:expr, $b:expr) => {
if $a < $b {
$a
} else {
$b
}
};
}
// We cannot use recursion here, as we need to handle the precedence of the operators
// We need to use a loop to parse the binary expressions.
@@ -285,6 +291,7 @@ impl Parser {
operators.push(operator);
self.assign_next()?;
expressions.push(self.get_binary_child_node()?);
current_token = token_from_option!(self.get_next()?).clone();
}
@@ -296,40 +303,49 @@ impl Parser {
});
}
// Every time we find a valid operator, we pop 2 off the expressions and add one back.
// This means that we need to keep track of the current iteration to ensure we are
// removing the correct expressions from the vector
let mut current_iteration = 0;
// Loop through operators, and build the binary expressions for exponential operators only
for (i, operator) in operators.iter().enumerate() {
if operator == &Symbol::Caret {
let left = expressions.remove(min!(i, expressions.len() - 1));
let right = expressions.remove(min!(i, expressions.len() - 1));
if operator == &Symbol::Exp {
let index = i - current_iteration;
let left = expressions.remove(index);
let right = expressions.remove(index);
expressions.insert(
min!(i, expressions.len()),
index,
Expression::BinaryExpression(BinaryExpression::Exponent(
Box::new(left),
Box::new(right),
)),
);
current_iteration += 1;
}
}
// remove all the exponential operators from the operators vector
operators.retain(|symbol| symbol != &Symbol::Caret);
operators.retain(|symbol| symbol != &Symbol::Exp);
current_iteration = 0;
// Loop through operators, and build the binary expressions for multiplication and division operators
for (i, operator) in operators.iter().enumerate() {
if operator == &Symbol::Asterisk || operator == &Symbol::Slash {
let left = expressions.remove(min!(i, expressions.len() - 1));
let right = expressions.remove(min!(i, expressions.len() - 1));
let index = i - current_iteration;
let left = expressions.remove(index);
let right = expressions.remove(index);
match operator {
Symbol::Asterisk => expressions.insert(
min!(i, expressions.len()),
index,
Expression::BinaryExpression(BinaryExpression::Multiply(
Box::new(left),
Box::new(right),
)),
),
Symbol::Slash => expressions.insert(
min!(i, expressions.len()),
index,
Expression::BinaryExpression(BinaryExpression::Divide(
Box::new(left),
Box::new(right),
@@ -338,28 +354,31 @@ impl Parser {
// safety: we have already checked for the operator
_ => unreachable!(),
}
current_iteration += 1;
}
}
// remove all the multiplication and division operators from the operators vector
operators.retain(|symbol| symbol != &Symbol::Asterisk && symbol != &Symbol::Slash);
current_iteration = 0;
// Loop through operators, and build the binary expressions for addition and subtraction operators
for (i, operator) in operators.iter().enumerate() {
if operator == &Symbol::Plus || operator == &Symbol::Minus {
let left = expressions.remove(min!(i, expressions.len() - 1));
let right = expressions.remove(min!(i, expressions.len() - 1));
let index = i - current_iteration;
let left = expressions.remove(index);
let right = expressions.remove(index);
match operator {
Symbol::Plus => expressions.insert(
min!(i, expressions.len()),
index,
Expression::BinaryExpression(BinaryExpression::Add(
Box::new(left),
Box::new(right),
)),
),
Symbol::Minus => expressions.insert(
min!(i, expressions.len()),
index,
Expression::BinaryExpression(BinaryExpression::Subtract(
Box::new(left),
Box::new(right),
@@ -368,6 +387,7 @@ impl Parser {
// safety: we have already checked for the operator
_ => unreachable!(),
}
current_iteration += 1;
}
}
@@ -382,8 +402,11 @@ impl Parser {
});
}
// Edge case. If the current token is a semi-colon, we need to set current token to the previous token
if token_matches!(current_token, TokenType::Symbol(Symbol::Semicolon)) {
// Edge case. If the current token is a semi-colon, RParen, we need to set current token to the previous token
if token_matches!(
current_token,
TokenType::Symbol(Symbol::Semicolon) | TokenType::Symbol(Symbol::RParen)
) {
self.tokenizer.seek(SeekFrom::Current(-1))?;
}
@@ -399,16 +422,17 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
});
}
let expression = self.parse()?.ok_or(ParseError::UnexpectedEOF)?;
// make sure the next token is a right parenthesis
let current_token = token_from_option!(self.get_next()?);
if !token_matches!(current_token, TokenType::Symbol(Symbol::RParen)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
});
}
@@ -427,6 +451,7 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: std::backtrace::Backtrace::capture(),
});
}
@@ -455,6 +480,7 @@ impl Parser {
{
return Err(ParseError::UnexpectedToken {
token: token_from_option!(self.get_next()?).clone(),
backtrace: backtrace::Backtrace::capture(),
});
}
@@ -481,6 +507,7 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::LBrace)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: backtrace::Backtrace::capture(),
});
}
@@ -500,6 +527,7 @@ impl Parser {
if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: backtrace::Backtrace::capture(),
});
}
let identifier = extract_token_data!(
@@ -513,6 +541,7 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::Assign)) {
return Err(ParseError::UnexpectedToken {
token: current_token,
backtrace: backtrace::Backtrace::capture(),
});
}
@@ -523,6 +552,7 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::Semicolon)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: backtrace::Backtrace::capture(),
});
}
@@ -540,6 +570,7 @@ impl Parser {
_ => {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: backtrace::Backtrace::capture(),
})
}
};
@@ -553,6 +584,7 @@ impl Parser {
if !self_matches_current!(self, TokenType::Keyword(Keyword::Fn)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: Backtrace::capture(),
});
}
@@ -567,6 +599,7 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: Backtrace::capture(),
});
}
@@ -596,6 +629,7 @@ impl Parser {
{
return Err(ParseError::UnexpectedToken {
token: token_from_option!(self.get_next()?).clone(),
backtrace: Backtrace::capture(),
});
}
@@ -613,6 +647,7 @@ impl Parser {
if !token_matches!(current_token, TokenType::Symbol(Symbol::LBrace)) {
return Err(ParseError::UnexpectedToken {
token: current_token.clone(),
backtrace: Backtrace::capture(),
});
};
@@ -747,18 +782,15 @@ mod tests {
}
#[test]
fn test_binary() -> Result<()> {
let expr = parser!("1 + 3 ^ 5").parse()?.unwrap();
assert_eq!("(1 + (3 ^ 5))", expr.to_string());
fn test_binary_expression() -> Result<()> {
let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap();
assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string());
let input = "4 ^ 2 + 3 ^ 2";
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!(input).parse()?.unwrap();
println!("Original: {}\nTranscribed: {}", input, expr.to_string());
let expr = parser!("12 - 1 + 3 * 5").parse()?.unwrap();
assert_eq!("((12 - 1) + (3 * 5))", expr.to_string());
let expr = parser!("(5 - 2) * 10").parse()?.unwrap();
assert_eq!("(((5 - 2)) * 10)", expr.to_string());
Ok(())
}

View File

@@ -31,7 +31,7 @@ impl std::fmt::Display for BinaryExpression {
BinaryExpression::Multiply(l, r) => write!(f, "({} * {})", l, r),
BinaryExpression::Divide(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),
}
}
}
@@ -138,6 +138,18 @@ impl std::fmt::Display for InvocationExpression {
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct PropertyAccessorExpression {
pub object: Box<Expression>,
pub property: String,
}
impl std::fmt::Display for PropertyAccessorExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.object, self.property)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Expression {
Literal(Literal),
@@ -151,6 +163,7 @@ pub enum Expression {
BlockExpression(BlockExpression),
InvocationExpression(InvocationExpression),
PriorityExpression(Box<Expression>),
PropertyAccessorExpression(PropertyAccessorExpression)
}
impl std::fmt::Display for Expression {
@@ -167,6 +180,7 @@ impl std::fmt::Display for Expression {
Expression::InvocationExpression(e) => write!(f, "{}", e),
Expression::Variable(id) => write!(f, "{}", id),
Expression::PriorityExpression(e) => write!(f, "({})", e),
Expression::PropertyAccessorExpression(e) => write!(f, "{}", e),
}
}
}

View File

@@ -209,7 +209,7 @@ impl Tokenizer {
'+' => symbol!(Plus),
'-' => symbol!(Minus),
'/' => symbol!(Slash),
'*' => symbol!(Asterisk),
'.' => symbol!(Dot),
'^' => symbol!(Caret),
@@ -238,6 +238,12 @@ impl Tokenizer {
}
'!' => symbol!(LogicalNot),
'*' if self.peek_next_char()? == Some('*') => {
self.next_char()?;
symbol!(Exp)
}
'*' => symbol!(Asterisk),
'&' if self.peek_next_char()? == Some('&') => {
self.next_char()?;
symbol!(LogicalAnd)
@@ -246,6 +252,7 @@ impl Tokenizer {
self.next_char()?;
symbol!(LogicalOr)
}
_ => Err(TokenizerError::UnknownSymbolError(
first_symbol,
self.line,
@@ -674,7 +681,7 @@ This is a skippable line"#,
#[test]
fn test_symbol_parse() -> Result<()> {
let mut tokenizer = Tokenizer::from(String::from(
"^ ! () [] {} , . ; : + - * / < > = != && || >= <=",
"^ ! () [] {} , . ; : + - * / < > = != && || >= <=**",
));
let expected_tokens = vec![
@@ -702,6 +709,7 @@ This is a skippable line"#,
TokenType::Symbol(Symbol::LogicalOr),
TokenType::Symbol(Symbol::GreaterThanOrEqual),
TokenType::Symbol(Symbol::LessThanOrEqual),
TokenType::Symbol(Symbol::Exp),
];
for expected_token in expected_tokens {

View File

@@ -122,12 +122,14 @@ pub enum Symbol {
LessThanOrEqual,
/// Represents the `>=` symbol
GreaterThanOrEqual,
/// Represents the `**` symbol
Exp,
}
impl Symbol {
pub fn is_operator(&self) -> bool {
match self {
Symbol::Plus | Symbol::Minus | Symbol::Asterisk | Symbol::Slash | Symbol::Caret => true,
Symbol::Plus | Symbol::Minus | Symbol::Asterisk | Symbol::Slash | Symbol::Exp => true,
_ => false,
}
}