diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 67a0763..ee636be 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -42,6 +42,7 @@ lazy_static! { terminal!(m, Colon, ":"); terminal!(m, Comma, ","); terminal!(m, Dot, "."); + terminal!(m, ExclamationMark, "!"); terminal!(m, SmallRightArrow, "->"); terminal!(m, BigRightArrow, "=>"); terminal!(m, Backslash, "\\"); diff --git a/src/lexer/token_kind.rs b/src/lexer/token_kind.rs index 258d4c1..322bd73 100644 --- a/src/lexer/token_kind.rs +++ b/src/lexer/token_kind.rs @@ -148,4 +148,8 @@ pub enum TokenKind { StructKeyword { position: Position, }, + #[terminal] + ExclamationMark { + position: Position, + }, } diff --git a/src/parser/ast/expression/mod.rs b/src/parser/ast/expression/mod.rs index 0ab29f7..1438f04 100644 --- a/src/parser/ast/expression/mod.rs +++ b/src/parser/ast/expression/mod.rs @@ -6,6 +6,7 @@ mod if_expression; mod lambda; mod num; mod postfix; +mod prefix; mod struct_initialisation; pub use self::array::*; @@ -16,6 +17,7 @@ pub use self::if_expression::*; pub use self::lambda::*; pub use self::num::*; pub use self::postfix::*; +pub use self::prefix::*; pub use self::struct_initialisation::*; use crate::lexer::Tokens; @@ -49,6 +51,7 @@ pub enum Expression { Multiplication(Box, Box), Parens(Box), Postfix(Postfix), + Prefix(Prefix), Comparison { lhs: Box, rhs: Box, @@ -60,40 +63,67 @@ pub enum Expression { impl FromTokens for Expression { fn parse(tokens: &mut Tokens) -> Result { - let mut expr = if let Some(TokenKind::LParen { .. }) = tokens.peek() { - let matcher = Comb::LPAREN >> Comb::EXPR >> Comb::RPAREN; - let result = matcher.parse(tokens)?; - let expr = match result.first() { - Some(AstNode::Expression(rhs)) => rhs.clone(), - None | Some(_) => unreachable!(), - }; - Expression::Parens(Box::new(expr)) - } else { - let matcher = Comb::FUNCTION - | Comb::IF - | Comb::NUM - | Comb::STRUCT_INITILISATION - | Comb::ID - | Comb::LAMBDA - | Comb::BLOCK - | Comb::ARRAY; - let result = matcher.parse(tokens)?; - match result.first() { - Some(AstNode::Id(id)) => Expression::Id(id.clone()), - Some(AstNode::Num(num)) => Expression::Num(num.clone()), - Some(AstNode::Function(func)) => { - return Ok(Expression::Function(func.clone()).into()) - } - Some(AstNode::Lambda(lambda)) => { - return Ok(Expression::Lambda(lambda.clone()).into()) - } - Some(AstNode::If(if_expression)) => Expression::If(if_expression.clone()), - Some(AstNode::Block(block)) => Expression::Block(block.clone()), - Some(AstNode::Array(array)) => Expression::Array(array.clone()), - Some(AstNode::StructInitialisation(initialisation)) => { - Expression::StructInitialisation(initialisation.clone()) + let mut expr = match tokens.peek() { + Some(TokenKind::LParen { .. }) => { + let matcher = Comb::LPAREN >> Comb::EXPR >> Comb::RPAREN; + let result = matcher.parse(tokens)?; + let expr = match result.first() { + Some(AstNode::Expression(rhs)) => rhs.clone(), + None | Some(_) => unreachable!(), + }; + Expression::Parens(Box::new(expr)) + } + Some(TokenKind::Minus { .. }) => { + let matcher = Comb::MINUS >> Comb::EXPR; + let result = matcher.parse(tokens)?; + + let Some(AstNode::Expression(expr)) = result.first() else { + unreachable!(); + }; + + Expression::Prefix(Prefix::Minus { + expr: Box::new(expr.clone()), + }) + } + Some(TokenKind::ExclamationMark { .. }) => { + let matcher = Comb::EXCLAMATION_MARK >> Comb::EXPR; + let result = matcher.parse(tokens)?; + + let Some(AstNode::Expression(expr)) = result.first() else { + unreachable!(); + }; + + Expression::Prefix(Prefix::Negation { + expr: Box::new(expr.clone()), + }) + } + _ => { + let matcher = Comb::FUNCTION + | Comb::IF + | Comb::NUM + | Comb::STRUCT_INITILISATION + | Comb::ID + | Comb::LAMBDA + | Comb::BLOCK + | Comb::ARRAY; + let result = matcher.parse(tokens)?; + match result.first() { + Some(AstNode::Id(id)) => Expression::Id(id.clone()), + Some(AstNode::Num(num)) => Expression::Num(num.clone()), + Some(AstNode::Function(func)) => { + return Ok(Expression::Function(func.clone()).into()) + } + Some(AstNode::Lambda(lambda)) => { + return Ok(Expression::Lambda(lambda.clone()).into()) + } + Some(AstNode::If(if_expression)) => Expression::If(if_expression.clone()), + Some(AstNode::Block(block)) => Expression::Block(block.clone()), + Some(AstNode::Array(array)) => Expression::Array(array.clone()), + Some(AstNode::StructInitialisation(initialisation)) => { + Expression::StructInitialisation(initialisation.clone()) + } + None | Some(_) => unreachable!(), } - None | Some(_) => unreachable!(), } }; @@ -588,4 +618,76 @@ mod tests { result ); } + + #[test] + fn test_simple_minus() { + let mut tokens = Lexer::new("-42").lex().expect("something is wrong").into(); + + let result = Expression::parse(&mut tokens); + + assert_eq!( + Ok(Expression::Prefix(Prefix::Minus { + expr: Box::new(Expression::Num(Num(42))) + }) + .into()), + result + ); + } + + #[test] + fn test_complex_minus() { + let mut tokens = Lexer::new("-someFunction()") + .lex() + .expect("something is wrong") + .into(); + + let result = Expression::parse(&mut tokens); + + assert_eq!( + Ok(Expression::Prefix(Prefix::Minus { + expr: Box::new(Expression::Postfix(Postfix::Call { + expr: Box::new(Expression::Id(Id("someFunction".into()))), + args: vec![] + })) + }) + .into()), + result + ); + } + + #[test] + fn test_simple_negation() { + let mut tokens = Lexer::new("!42").lex().expect("something is wrong").into(); + + let result = Expression::parse(&mut tokens); + + assert_eq!( + Ok(Expression::Prefix(Prefix::Negation { + expr: Box::new(Expression::Num(Num(42))) + }) + .into()), + result + ); + } + + #[test] + fn test_complex_negation() { + let mut tokens = Lexer::new("!someFunction()") + .lex() + .expect("something is wrong") + .into(); + + let result = Expression::parse(&mut tokens); + + assert_eq!( + Ok(Expression::Prefix(Prefix::Negation { + expr: Box::new(Expression::Postfix(Postfix::Call { + expr: Box::new(Expression::Id(Id("someFunction".into()))), + args: vec![] + })) + }) + .into()), + result + ); + } } diff --git a/src/parser/ast/expression/prefix.rs b/src/parser/ast/expression/prefix.rs new file mode 100644 index 0000000..986ec96 --- /dev/null +++ b/src/parser/ast/expression/prefix.rs @@ -0,0 +1,7 @@ +use super::Expression; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Prefix { + Negation { expr: Box }, + Minus { expr: Box }, +} diff --git a/src/parser/combinators.rs b/src/parser/combinators.rs index fe15534..a846ce3 100644 --- a/src/parser/combinators.rs +++ b/src/parser/combinators.rs @@ -177,6 +177,10 @@ impl<'a> Comb<'a, TokenKind, Terminal, AstNode> { terminal_comb!(RETURN_KEYWORD, ReturnKeyword); + terminal_comb!(MINUS, Minus); + + terminal_comb!(EXCLAMATION_MARK, ExclamationMark); + terminal_comb!(COLON, Colon); terminal_comb!(COMMA, Comma);