Skip to content

Commit

Permalink
feat: infix operator parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
viddrobnic committed May 26, 2024
1 parent e9db862 commit bedc39e
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 5 deletions.
2 changes: 1 addition & 1 deletion parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub enum NodeValue {
operator: PrefixOperatorKind,
right: Box<Node>,
},
BinaryOperator {
InfixOperator {
operator: InfixOperatorKind,
left: Box<Node>,
right: Box<Node>,
Expand Down
6 changes: 5 additions & 1 deletion parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fmt::Display;

use thiserror::Error;

use crate::{position::Position, token::TokenKind};
use crate::{ast::NodeKind, position::Position, token::TokenKind};

#[derive(Debug, PartialEq)]
pub enum ErrorKind {
Expand All @@ -12,6 +12,7 @@ pub enum ErrorKind {
InvalidChar(char),
InvalidExpression(TokenKind),
ExpectedEol,
InvalidNodeKind { expected: NodeKind, got: NodeKind },
}

#[derive(Debug, Error, PartialEq)]
Expand All @@ -37,6 +38,9 @@ impl Display for ErrorKind {
ErrorKind::InvalidChar(ch) => write!(f, "invalid char '{ch}'"),
ErrorKind::InvalidExpression(token) => write!(f, "not a valid expression: {:?}", token),
ErrorKind::ExpectedEol => write!(f, "expression must end with new line"),
ErrorKind::InvalidNodeKind { expected, got } => {
write!(f, "invalid node kind, expected: {expected:?}, got: {got:?}")
}
}
}
}
96 changes: 93 additions & 3 deletions parser/src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
ast::{self, PrefixOperatorKind},
ast::{self, InfixOperatorKind, NodeKind, PrefixOperatorKind},
error::{Error, ErrorKind, Result},
lexer::Lexer,
position::Position,
Expand Down Expand Up @@ -171,15 +171,105 @@ impl Parser<'_> {
})
}

fn parse_infix(&mut self, _left: ast::Node) -> Result<ast::Node> {
todo!()
fn parse_infix(&mut self, left: ast::Node) -> Result<ast::Node> {
let Some(token) = self.lexer.next() else {
return Err(Error {
kind: ErrorKind::UnexpectedEof,
position: Position::default(),
});
};
let token = token?;
let Token {
kind: tkn_kind,
position,
} = &token;

let node_value = match tkn_kind {
TokenKind::LSquare | TokenKind::Dot => todo!("parse index"),
TokenKind::LBracket => todo!("parse function call"),
TokenKind::Le => self.parse_infix_operation(left, InfixOperatorKind::Le, &token)?,
TokenKind::Leq => self.parse_infix_operation(left, InfixOperatorKind::Leq, &token)?,
TokenKind::Ge => self.parse_infix_operation(left, InfixOperatorKind::Ge, &token)?,
TokenKind::Geq => self.parse_infix_operation(left, InfixOperatorKind::Geq, &token)?,
TokenKind::Eq => self.parse_infix_operation(left, InfixOperatorKind::Eq, &token)?,
TokenKind::Neq => self.parse_infix_operation(left, InfixOperatorKind::Neq, &token)?,
TokenKind::Plus => self.parse_infix_operation(left, InfixOperatorKind::Add, &token)?,
TokenKind::Minus => {
self.parse_infix_operation(left, InfixOperatorKind::Subtract, &token)?
}
TokenKind::Mult => {
self.parse_infix_operation(left, InfixOperatorKind::Multiply, &token)?
}
TokenKind::Div => {
self.parse_infix_operation(left, InfixOperatorKind::Divide, &token)?
}
TokenKind::Modulo => {
self.parse_infix_operation(left, InfixOperatorKind::Modulo, &token)?
}
TokenKind::And => self.parse_infix_operation(left, InfixOperatorKind::And, &token)?,
TokenKind::Or => self.parse_infix_operation(left, InfixOperatorKind::Or, &token)?,
TokenKind::Assign => todo!("parse assign"),

_ => return Ok(left),
};

Ok(ast::Node {
value: node_value,
position: *position,
})
}

fn parse_prefix_operator(&mut self, operator: PrefixOperatorKind) -> Result<ast::NodeValue> {
let right = self.parse_node(Precedence::Prefix)?;

if right.kind() != NodeKind::Expression {
return Err(Error {
kind: ErrorKind::InvalidNodeKind {
expected: NodeKind::Expression,
got: right.kind(),
},
position: right.position,
});
}

Ok(ast::NodeValue::PrefixOperator {
operator,
right: Box::new(right),
})
}

fn parse_infix_operation(
&mut self,
left: ast::Node,
operator: InfixOperatorKind,
token: &Token,
) -> Result<ast::NodeValue> {
let right = self.parse_node(token.into())?;

if left.kind() != NodeKind::Expression {
return Err(Error {
kind: ErrorKind::InvalidNodeKind {
expected: NodeKind::Expression,
got: left.kind(),
},
position: left.position,
});
}

if right.kind() != NodeKind::Expression {
return Err(Error {
kind: ErrorKind::InvalidNodeKind {
expected: NodeKind::Expression,
got: right.kind(),
},
position: right.position,
});
}

Ok(ast::NodeValue::InfixOperator {
operator,
left: Box::new(left),
right: Box::new(right),
})
}
}
76 changes: 76 additions & 0 deletions parser/src/parser/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,79 @@ fn prefix_operator() -> Result<()> {

Ok(())
}

#[test]
fn infix_opeartor() -> Result<()> {
let tests = [
(
"1+2",
ast::Node {
value: ast::NodeValue::InfixOperator {
operator: ast::InfixOperatorKind::Add,
left: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(1),
position: Position::new(0, 0),
}),
right: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(2),
position: Position::new(0, 2),
}),
},
position: Position::new(0, 1),
},
),
(
"1 & 2",
ast::Node {
value: ast::NodeValue::InfixOperator {
operator: ast::InfixOperatorKind::And,
left: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(1),
position: Position::new(0, 0),
}),
right: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(2),
position: Position::new(0, 4),
}),
},
position: Position::new(0, 2),
},
),
(
"1 & 2 + 3",
ast::Node {
value: ast::NodeValue::InfixOperator {
operator: ast::InfixOperatorKind::And,
left: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(1),
position: Position::new(0, 0),
}),
right: Box::new(ast::Node {
value: ast::NodeValue::InfixOperator {
operator: ast::InfixOperatorKind::Add,
left: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(2),
position: Position::new(0, 4),
}),
right: Box::new(ast::Node {
value: ast::NodeValue::IntegerLiteral(3),
position: Position::new(0, 8),
}),
},
position: Position::new(0, 6),
}),
},
position: Position::new(0, 2),
},
),
];

for (input, expected) in tests {
let program = parse(input)?;

assert_eq!(program.statements.len(), 1);
assert_eq!(program.statements[0], expected);
}

Ok(())
}

0 comments on commit bedc39e

Please sign in to comment.