use crate::{
lexer::{Lexer, token::Token},
parser::{
ast::{Line, PrintItem, Program, Statement},
expression::{BinaryOperator, Expression},
},
};
pub mod ast;
pub mod expression;
#[allow(dead_code)]
pub struct Parser<'a> {
lexer: Lexer<'a>,
position: usize,
}
#[allow(dead_code)]
impl<'a> Parser<'a> {
pub fn from_input(input: &'a str) -> Self {
let lexer = Lexer::new(input);
Self { lexer, position: 0 }
}
pub fn parse_program(&mut self) -> Program {
let mut lines = Vec::new();
while self.lexer.peek() != Token::Eof {
lines.push(self.parse_line());
}
Program { lines }
}
pub fn parse_line(&mut self) -> Line {
let statements = self.parse_statement_list();
Line {
number: None,
statements,
}
}
pub fn parse_statement_list(&mut self) -> Vec<Statement> {
let mut statements = Vec::new();
while !matches!(self.lexer.peek(), Token::Eof | Token::Semicolon) {
statements.push(self.parse_statement());
if self.lexer.peek() == Token::Colon {
self.lexer.next_token();
} else {
break;
}
}
if self.lexer.peek() == Token::Semicolon {
self.lexer.next_token();
}
statements
}
fn parse_statement(&mut self) -> Statement {
match self.lexer.peek() {
Token::End => {
self.lexer.next_token();
Statement::End
}
Token::Return => {
self.lexer.next_token();
Statement::Return
}
Token::Rem => {
self.lexer.next_token();
Statement::Rem(None)
}
Token::Print => self.parse_print_statement(),
_ => Statement::Expression(self.parse_expression()),
}
}
fn parse_print_statement(&mut self) -> Statement {
self.lexer.next_token();
let mut items = Vec::new();
while !matches!(
self.lexer.peek(),
Token::Colon | Token::Eof | Token::Semicolon
) {
let item = match self.lexer.peek() {
Token::String(_) => match self.lexer.next_token() {
Token::String(value) => PrintItem::String(value),
_ => unreachable!(),
},
Token::Char(_) => match self.lexer.next_token() {
Token::Char(value) => PrintItem::Char(value),
_ => unreachable!(),
},
Token::Comma => {
self.lexer.next_token();
continue;
}
_ => PrintItem::Expression(self.parse_expression()),
};
items.push(item);
if matches!(self.lexer.peek(), Token::Comma | Token::Semicolon) {
self.lexer.next_token();
}
}
Statement::Print { items }
}
fn parse_expression(&mut self) -> Expression {
self.parse_binary_expression(0)
}
fn parse_binary_expression(&mut self, minimum_precedence: u8) -> Expression {
let mut left = self.parse_primary_expression();
while let Some(operator) = BinaryOperator::from_token(&self.lexer.peek()) {
let precedence = operator.precedence();
if precedence < minimum_precedence {
break;
}
self.lexer.next_token();
let right = self.parse_binary_expression(precedence + 1);
left = Expression::binary(left, operator, right);
}
left
}
fn parse_primary_expression(&mut self) -> Expression {
match self.lexer.next_token() {
Token::Integer(value) => Expression::integer(value),
Token::Float(value) => Expression::float(value),
Token::String(value) => Expression::string(value),
Token::Char(value) => Expression::char(value),
Token::Identifier(value) => Expression::identifier(value),
Token::LParen => {
let expression = self.parse_expression();
assert_eq!(
self.lexer.next_token(),
Token::RParen,
"Expected right paren"
);
Expression::grouping(expression)
}
token => panic!("Expected expression, got {token:?}"),
}
}
}
#[cfg(test)]
mod tests {
use crate::parser::{
Parser,
ast::{Line, PrintItem, Program, Statement},
expression::{BinaryOperator, Expression},
};
fn parse_statement_list(input: &'static str) -> Vec<Statement> {
Parser::from_input(input).parse_statement_list()
}
#[test]
fn parse_single_statement_list() {
assert_eq!(parse_statement_list("end;"), vec![Statement::End]);
}
#[test]
fn parse_empty_statement_list_at_semicolon() {
assert_eq!(parse_statement_list(";"), Vec::new());
}
#[test]
fn parse_empty_statement_list_at_eof() {
assert_eq!(parse_statement_list(""), Vec::new());
}
#[test]
fn parse_colon_separated_statement_list() {
assert_eq!(
parse_statement_list("print \"HI\": return: end;"),
vec![
Statement::Print {
items: vec![PrintItem::String("\"HI\"".to_string())]
},
Statement::Return,
Statement::End,
]
);
}
#[test]
fn parse_expression_statement_list() {
assert_eq!(
parse_statement_list("x;"),
vec![Statement::Expression(Expression::identifier(
"x".to_string()
))]
);
}
#[test]
fn parse_integer_expression_statement_list() {
assert_eq!(
parse_statement_list("123;"),
vec![Statement::Expression(Expression::integer(123))]
);
}
#[test]
fn parse_string_expression_statement_list() {
assert_eq!(
parse_statement_list("\"ABC\";"),
vec![Statement::Expression(Expression::string(
"\"ABC\"".to_string()
))]
);
}
#[test]
fn parse_char_expression_statement_list() {
assert_eq!(
parse_statement_list("'a';"),
vec![Statement::Expression(Expression::char('a'))]
);
}
#[test]
fn parse_add_expression_statement_list() {
assert_eq!(
parse_statement_list("1 + 2;"),
vec![Statement::Expression(Expression::binary(
Expression::integer(1),
BinaryOperator::Plus,
Expression::integer(2),
))]
);
}
#[test]
fn parse_binary_expression_precedence() {
assert_eq!(
parse_statement_list("1 + 2 * 3;"),
vec![Statement::Expression(Expression::binary(
Expression::integer(1),
BinaryOperator::Plus,
Expression::binary(
Expression::integer(2),
BinaryOperator::Multiply,
Expression::integer(3),
),
))]
);
}
#[test]
fn parse_grouped_binary_expression() {
assert_eq!(
parse_statement_list("(1 + 2) * 3;"),
vec![Statement::Expression(Expression::binary(
Expression::grouping(Expression::binary(
Expression::integer(1),
BinaryOperator::Plus,
Expression::integer(2),
)),
BinaryOperator::Multiply,
Expression::integer(3),
))]
);
}
#[test]
fn parse_comparison_expression() {
assert_eq!(
parse_statement_list("1 <= 2;"),
vec![Statement::Expression(Expression::binary(
Expression::integer(1),
BinaryOperator::LessEqual,
Expression::integer(2),
))]
);
}
#[test]
fn parse_return_statement_list() {
assert_eq!(parse_statement_list("return;"), vec![Statement::Return]);
}
#[test]
fn parse_rem_statement_list() {
assert_eq!(parse_statement_list("rem;"), vec![Statement::Rem(None)]);
}
#[test]
fn parse_empty_print_statement_list() {
assert_eq!(
parse_statement_list("print;"),
vec![Statement::Print { items: Vec::new() }]
);
}
#[test]
fn parse_print_string_statement_list() {
assert_eq!(
parse_statement_list("print \"HELLO\";"),
vec![Statement::Print {
items: vec![PrintItem::String("\"HELLO\"".to_string())]
}]
);
}
#[test]
fn parse_print_char_statement_list() {
assert_eq!(
parse_statement_list("print 'x';"),
vec![Statement::Print {
items: vec![PrintItem::Char('x')]
}]
);
}
#[test]
fn parse_print_integer_statement_list() {
assert_eq!(
parse_statement_list("print 42;"),
vec![Statement::Print {
items: vec![PrintItem::Expression(Expression::integer(42))]
}]
);
}
#[test]
fn parse_print_expression_statement_list() {
assert_eq!(
parse_statement_list("print 1 + 2;"),
vec![Statement::Print {
items: vec![PrintItem::Expression(Expression::binary(
Expression::integer(1),
BinaryOperator::Plus,
Expression::integer(2),
))]
}]
);
}
#[test]
fn parse_print_identifier_statement_list() {
assert_eq!(
parse_statement_list("print answer;"),
vec![Statement::Print {
items: vec![PrintItem::Expression(Expression::identifier(
"answer".to_string()
))]
}]
);
}
#[test]
fn parse_print_comma_separated_items() {
assert_eq!(
parse_statement_list("print \"A\", 1, x;"),
vec![Statement::Print {
items: vec![
PrintItem::String("\"A\"".to_string()),
PrintItem::Expression(Expression::integer(1)),
PrintItem::Expression(Expression::identifier("x".to_string())),
]
}]
);
}
#[test]
fn parse_print_stops_before_colon() {
assert_eq!(
parse_statement_list("print \"A\": end;"),
vec![
Statement::Print {
items: vec![PrintItem::String("\"A\"".to_string())]
},
Statement::End,
]
);
}
#[test]
fn parse_program_from_print_fixture() {
let mut parser = Parser::from_input(include_str!("../tests/input/print.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![Statement::Print {
items: vec![PrintItem::String("\"HELLO WORLD\"".to_string())]
}]
}]
}
);
}
#[test]
fn parse_program_from_statement_list_fixture() {
let mut parser = Parser::from_input(include_str!("../tests/input/statement_list.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![
Statement::Print {
items: vec![PrintItem::String("\"A\"".to_string())]
},
Statement::Return,
Statement::End,
]
}]
}
);
}
#[test]
fn parse_program_from_print_items_fixture() {
let mut parser = Parser::from_input(include_str!("../tests/input/print_items.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![Statement::Print {
items: vec![
PrintItem::String("\"NAME\"".to_string()),
PrintItem::Expression(Expression::identifier("username".to_string())),
PrintItem::Expression(Expression::integer(7)),
PrintItem::Char('!'),
]
}]
}]
}
);
}
#[test]
fn parse_program_from_expression_statements_fixture() {
let mut parser =
Parser::from_input(include_str!("../tests/input/expression_statements.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![
Line {
number: None,
statements: vec![Statement::Expression(Expression::identifier(
"answer".to_string()
))]
},
Line {
number: None,
statements: vec![Statement::Expression(Expression::string(
"\"forty two\"".to_string()
))]
},
Line {
number: None,
statements: vec![Statement::Expression(Expression::char('z'))]
},
]
}
);
}
#[test]
fn parse_program_from_control_markers_fixture() {
let mut parser = Parser::from_input(include_str!("../tests/input/control_markers.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![Statement::Rem(None), Statement::Return, Statement::End]
}]
}
);
}
#[test]
fn parse_program_from_empty_print_fixture() {
let mut parser = Parser::from_input(include_str!("../tests/input/empty_print.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![Statement::Print { items: Vec::new() }]
}]
}
);
}
#[test]
fn parse_program_from_basic_math_fixture() {
let mut parser = Parser::from_input(include_str!("../tests/input/basic_math.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![Statement::Expression(Expression::binary(
Expression::integer(1),
BinaryOperator::Plus,
Expression::integer(2),
))]
}]
}
);
}
#[test]
fn parse_program_from_complicated_expression_fixture() {
let mut parser =
Parser::from_input(include_str!("../tests/input/complicated_expression.bsc"));
assert_eq!(
parser.parse_program(),
Program {
lines: vec![Line {
number: None,
statements: vec![Statement::Expression(Expression::binary(
Expression::grouping(Expression::binary(
Expression::binary(
Expression::grouping(Expression::binary(
Expression::integer(1),
BinaryOperator::Plus,
Expression::integer(2),
)),
BinaryOperator::Multiply,
Expression::grouping(Expression::binary(
Expression::integer(3),
BinaryOperator::Plus,
Expression::integer(4),
)),
),
BinaryOperator::Minus,
Expression::binary(
Expression::integer(5),
BinaryOperator::Divide,
Expression::grouping(Expression::binary(
Expression::integer(6),
BinaryOperator::Minus,
Expression::integer(1),
)),
),
)),
BinaryOperator::GreaterEqual,
Expression::integer(20),
))]
}]
}
);
}
}