Skip to content

Commit

Permalink
✨ (parser): Enforce ; after expression statements
Browse files Browse the repository at this point in the history
semver: minor
  • Loading branch information
Somfic committed Jun 8, 2024
1 parent 9d1e3c8 commit 15e3e0a
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 39 deletions.
3 changes: 1 addition & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ pub mod parser;
pub mod scanner;

fn main() -> Result<()> {
let code = "'1' * 10 + 1 + 1 - 1";
let code = "'1' * 10 + 1 + 1 - 1;";
let tokens = scanner::Scanner::new(code.to_owned()).collect::<Vec<_>>();
println!("{:#?}", tokens);

let mut parser = parser::Parser::new(tokens);
let parsed = parser.parse();

println!("{:#?}", parsed);

Ok(())
Expand Down
43 changes: 15 additions & 28 deletions src/parser/expression.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::scanner::lexeme::{Lexeme, Token};

use super::{lookup::BindingPower, BinaryOperation, Expression, Parser};
use super::{lookup::BindingPower, Expression, Parser};
use crate::scanner::lexeme::Lexeme;

pub fn parse(
parser: &Parser,
Expand All @@ -27,13 +26,24 @@ pub fn parse(
Lexeme::Invalid(_) => break,
};

let token_binding_power = parser.lookup.binding_power_lookup.get(token)?;
let token_binding_power = parser
.lookup
.binding_power_lookup
.get(token)
.unwrap_or(&BindingPower::None);

if binding_power > token_binding_power {
break;
}

let left_expression_handler = parser.lookup.left_expression_lookup.get(token)?;
let left_expression_handler = parser.lookup.left_expression_lookup.get(token);

if left_expression_handler.is_none() {
break;
}

let left_expression_handler = left_expression_handler.unwrap();

let (right_hand_side, new_cursor) =
left_expression_handler(parser, cursor, left_hand_side, binding_power)?;

Expand All @@ -43,26 +53,3 @@ pub fn parse(

Some((left_hand_side, cursor))
}

pub fn parse_binary(
parser: &Parser,
cursor: usize,
left_hand_side: Box<Expression>,
binding_power: &BindingPower,
) -> Option<(Expression, usize)> {
let operator = parser.lexemes.get(cursor)?;
let operator = match operator {
Lexeme::Valid(Token::Plus, _) => BinaryOperation::Plus,
Lexeme::Valid(Token::Minus, _) => BinaryOperation::Minus,
Lexeme::Valid(Token::Star, _) => BinaryOperation::Times,
Lexeme::Valid(Token::Slash, _) => BinaryOperation::Divide,
_ => return None,
};

let (right_hand_side, cursor) = parse(parser, cursor, binding_power)?;

Some((
Expression::Binary(left_hand_side, operator, Box::new(right_hand_side)),
cursor,
))
}
9 changes: 8 additions & 1 deletion src/parser/lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,14 @@ impl Default for Lookup {

lookup.add_statement_handler(Token::Semicolon, |parser, cursor| {
let (expression, cursor) = expression::parse(parser, cursor, &BindingPower::Primary)?;
Some((Statement::Expression(expression), cursor))
// Expect a semicolon after the expression
let (tokens, cursor) = expect_tokens!(parser, cursor, (Token::Semicolon))?;
let semicolon = tokens.first().unwrap();
if let Lexeme::Valid(Token::Semicolon, _) = semicolon {
Some((Statement::Expression(expression), cursor))
} else {
None
}
});

lookup
Expand Down
12 changes: 6 additions & 6 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ mod tests {

#[test]
fn parses_addition() {
let code = "123 + 456";
let code = "123 + 456;";
let lexemes = Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = Parser::new(lexemes);
let result = parser.parse();
Expand All @@ -99,7 +99,7 @@ mod tests {

#[test]
fn parses_subtraction() {
let code = "123 - 456";
let code = "123 - 456;";
let lexemes = Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = Parser::new(lexemes);
let result = parser.parse();
Expand All @@ -118,7 +118,7 @@ mod tests {

#[test]
fn parses_multiplication() {
let code = "123 * 456";
let code = "123 * 456;";
let lexemes = Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = Parser::new(lexemes);
let result = parser.parse();
Expand All @@ -137,7 +137,7 @@ mod tests {

#[test]
fn parses_division() {
let code = "123 / 456";
let code = "123 / 456;";
let lexemes = Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = Parser::new(lexemes);
let result = parser.parse();
Expand All @@ -156,7 +156,7 @@ mod tests {

#[test]
fn parses_long_expression() {
let code = "123 + 456 - 789 + 101";
let code = "123 + 456 - 789 + 101;";
let lexemes = Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = Parser::new(lexemes);
let result = parser.parse();
Expand All @@ -183,7 +183,7 @@ mod tests {

#[test]
fn gives_precedence_to_multiplication() {
let code = "123 * 456 + 789";
let code = "123 * 456 + 789;";
let lexemes = Scanner::new(code.to_owned()).collect::<Vec<_>>();
let mut parser = Parser::new(lexemes);
let result = parser.parse();
Expand Down
13 changes: 11 additions & 2 deletions src/parser/statement.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
use crate::scanner::lexeme::Lexeme;
use crate::scanner::lexeme::{Lexeme, Token};

use super::{expression, lookup::BindingPower, Parser, Statement};

pub fn parse(parser: &Parser, cursor: usize) -> Option<(Statement, usize)> {
let mut cursor = cursor;
let lexeme = parser.lexemes.get(cursor)?;
if let Lexeme::Invalid(_) = lexeme {
return None;
}

let (expression, cursor) = expression::parse(parser, cursor, &BindingPower::None)?;
let (expression, new_cursor) = expression::parse(parser, cursor, &BindingPower::None)?;

// Expect a semicolon
let lexeme = parser.lexemes.get(new_cursor);
if let Some(Lexeme::Valid(Token::Semicolon, _)) = lexeme {
cursor = new_cursor + 1;
} else {
return None;
}

Some((Statement::Expression(expression), cursor))
}
12 changes: 12 additions & 0 deletions src/scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@ mod tests {
);
}

#[test]
fn parses_semicolons() {
test_scanner(
"123;456",
vec![
Lexeme::valid(Token::Integer(123), 0, 3),
Lexeme::valid(Token::Semicolon, 3, 1),
Lexeme::valid(Token::Integer(456), 4, 3),
],
);
}

fn test_scanner(input: &str, expected: Vec<Lexeme>) {
let lexemes = Scanner::new(input.to_string()).collect::<Vec<_>>();

Expand Down

0 comments on commit 15e3e0a

Please sign in to comment.