diff --git a/parser/src/ast.rs b/parser/src/ast.rs index d889bae..2649a3c 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -33,6 +33,7 @@ pub enum BinaryOp { Add, Sub, Comma, + Index, // lhs[rhs] } #[derive(Debug, DebugPls)] diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 66bdd37..0163132 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -51,7 +51,8 @@ where lex: PeekMoreIterator, } -macro_rules! expect { +// HACK: It's called `_parser` as a workaround this being ambiguous with the `#[expect]` attribute +macro_rules! expect_parser { ($self:ident, $pat:pat) => { match $self.next_t()? { ($pat, span) => span, @@ -68,6 +69,8 @@ macro_rules! expect { }; } +use expect_parser as expect; + macro_rules! eat { ($self:ident, $pat:pat) => { match $self.peek_t() { @@ -77,6 +80,8 @@ macro_rules! eat { }; } +use eat; + /// Can be called for the start of a sequence of tokens that could be a type. #[rustfmt::skip] fn is_tok_start_of_ty(tok: &Tok<'_>) -> bool { diff --git a/parser/src/parser/expr.rs b/parser/src/parser/expr.rs index 75ebef8..e83cb5f 100644 --- a/parser/src/parser/expr.rs +++ b/parser/src/parser/expr.rs @@ -4,7 +4,7 @@ use crate::{ ast::{Atom, BinaryOp, Expr, ExprBinary, ExprUnary, UnaryOp}, - parser::{Parser, ParserError, Result}, + parser::{expect, Parser, ParserError, Result}, pre::Punctuator as P, token::{Constant, Token as Tok}, Span, Spanned, @@ -25,6 +25,13 @@ where &(Tok::Constant(Constant::Int(int)), span) => (Atom::Int(int), span), &(Tok::Constant(Constant::Float(float)), span) => (Atom::Float(float), span), &(Tok::Constant(Constant::Char(char)), span) => (Atom::Char(char), span), + &(Tok::Punct(P::ParenOpen), _) => { + // TODO: casts... yikes + self.next_t()?; + let lhs = self.expr_bp(0); + expect!(self, Tok::Punct(P::ParenClose)); + return lhs; + } &(Tok::Punct(punct), span) => { let r_bp = prefix_binding_power(&Tok::Punct(punct)); let Some(op) = unary_op_from_token(&Tok::Punct(punct)) else { panic!() }; @@ -61,27 +68,52 @@ where Ok(&tok) => tok, Err(_) => break, }; - let Some(op) = binary_op_from_token(&tok) else { break; }; - self.next_t()?; - - let (l_bp, r_bp) = infix_binding_power(&tok); - if l_bp < min_bp { - break; + if let Some(l_bp) = postfix_binding_power(&tok) { + if l_bp < min_bp { + break; + } + let (tok, _) = self.next_t()?; + if let Tok::Punct(P::BracketOpen) = tok { + let rhs = self.expr_bp(0)?; + let span = expect!(self, Tok::Punct(P::BracketClose)); + let span = lhs.1.extend(span); + lhs = ( + Expr::Binary(ExprBinary { + lhs: Box::new(lhs), + rhs: Box::new(rhs), + op: BinaryOp::Index, + }), + span, + ); + } + continue; } - let rhs = self.expr_bp(r_bp)?; + if let Some(op) = binary_op_from_token(&tok) { + let (l_bp, r_bp) = infix_binding_power(&tok); + if l_bp < min_bp { + break; + } - let span = lhs.1.extend(rhs.1); + self.next_t()?; - lhs = ( - Expr::Binary(ExprBinary { - lhs: Box::new(lhs), - rhs: Box::new(rhs), - op, - }), - span, - ) + let rhs = self.expr_bp(r_bp)?; + + let span = lhs.1.extend(rhs.1); + + lhs = ( + Expr::Binary(ExprBinary { + lhs: Box::new(lhs), + rhs: Box::new(rhs), + op, + }), + span, + ); + continue; + } + + break; } Ok(lhs) @@ -122,3 +154,10 @@ fn infix_binding_power(tok: &Tok<'_>) -> (u8, u8) { _ => panic!("invalid token in expression! {tok:?}"), } } + +fn postfix_binding_power(tok: &Tok<'_>) -> Option { + match tok { + Tok::Punct(P::BracketOpen) => Some(45), + _ => None, + } +} diff --git a/parser/src/parser/snapshots/parser__parser__tests__small_expressions.snap b/parser/src/parser/snapshots/parser__parser__tests__small_expressions.snap new file mode 100644 index 0000000..b24bb21 --- /dev/null +++ b/parser/src/parser/snapshots/parser__parser__tests__small_expressions.snap @@ -0,0 +1,103 @@ +--- +source: parser/src/parser/tests.rs +expression: parsed_pretty +--- +Ok([ + ( + Decl( + Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Int, + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: Ident(("x", 5..6)), + pointer: false, + }, + init: Some(( + Binary(ExprBinary { + lhs: (Atom(Int(1)), 9..10), + rhs: (Atom(Int(1)), 13..14), + op: Add, + }), + 9..14, + )), + }, + 5..6, + ), + ], + }), + ), + 1..6, + ), + ( + Decl( + Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Int, + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: Ident(("y", 21..22)), + pointer: false, + }, + init: Some(( + Binary(ExprBinary { + lhs: (Atom(Int(1)), 26..27), + rhs: ( + Binary(ExprBinary { + lhs: (Atom(Int(2)), 31..32), + rhs: (Atom(Int(3)), 35..36), + op: Sub, + }), + 31..36, + ), + op: Add, + }), + 26..36, + )), + }, + 21..22, + ), + ], + }), + ), + 17..22, + ), + ( + Decl( + Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Int, + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: Ident(("z", 45..46)), + pointer: false, + }, + init: Some(( + Binary(ExprBinary { + lhs: (Atom(Ident("array")), 50..55), + rhs: (Atom(Int(9)), 56..57), + op: Index, + }), + 50..58, + )), + }, + 45..46, + ), + ], + }), + ), + 41..46, + ), +]) diff --git a/parser/src/parser/tests.rs b/parser/src/parser/tests.rs index fafcff9..17f8e27 100644 --- a/parser/src/parser/tests.rs +++ b/parser/src/parser/tests.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use super::{Parser, Tok}; +use super::Tok; use crate::Span; fn lex_and_pre(src: &str) -> impl Iterator, Span)> + '_ { @@ -63,10 +63,14 @@ int function(); } #[test] -fn small_expression() { +fn small_expressions() { parse_test!( r#" int x = 1 + 1; + +int y = (1 + (2 - 3)); + +int z = (array[9]); "# ); }