diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 4f795b1..e41c86c 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -78,11 +78,27 @@ pub struct ExprBinary { pub op: BinaryOp, } +#[derive(Debug, DebugPls)] +pub enum PostfixOp { + Call(Vec>), + Member(Ident), + ArrowMember(Ident), + Increment, + Decrement, +} + +#[derive(Debug, DebugPls)] +pub struct ExprPostfix { + pub lhs: Box>, + pub op: PostfixOp, +} + #[derive(Debug, DebugPls)] pub enum Expr { Atom(Atom), Unary(ExprUnary), Binary(ExprBinary), + Postfix(ExprPostfix), } // @@ -92,7 +108,10 @@ pub enum Expr { #[derive(Debug, DebugPls)] pub enum Stmt { Decl(Decl), - Labeled{label: Ident, stmt: Box>}, + Labeled { + label: Ident, + stmt: Box>, + }, Compound(Vec>), If { cond: Expr, diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 69acf09..f7df693 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -171,7 +171,7 @@ where /// (6.7) declaration: /// declaration-specifiers init-declarator-list.opt ; /// static_assert-declaration - /// + /// /// This does NOT eat the semicolon! fn declaration(&mut self) -> Result> { if let Some((tok, span)) = eat!(self, Tok::Kw(Kw::StaticAssert)) { @@ -201,7 +201,7 @@ where /// init-declarator: /// declarator /// declarator = initializer - /// + /// fn init_declarator_list(&mut self) -> Result>> { let mut init_decls = Vec::new(); let mut first = true; @@ -477,7 +477,11 @@ where let span2 = expect!(self, Tok::Punct(P::Semicolon)); return Ok((Stmt::Decl(decl), span.extend(span2))); } - todo!() + // all other stmts are indicated by keywords ... + + // it must be an expression stmt + let (expr, span) = self.expr()?; + Ok((Stmt::Expr(expr), span)) } /// (6.8.2) compound-statement: diff --git a/parser/src/parser/expr.rs b/parser/src/parser/expr.rs index 6464fa0..c521ba3 100644 --- a/parser/src/parser/expr.rs +++ b/parser/src/parser/expr.rs @@ -3,8 +3,11 @@ //! For more information, see https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html use crate::{ - ast::{ArithOpKind, Atom, BinaryOp, ComparisonKind, Expr, ExprBinary, ExprUnary, UnaryOp}, - parser::{expect, Parser, ParserError, Result}, + ast::{ + ArithOpKind, Atom, BinaryOp, ComparisonKind, Expr, ExprBinary, ExprPostfix, ExprUnary, + PostfixOp, UnaryOp, + }, + parser::{expect, Parser, ParserError, Result, eat}, pre::Punctuator as P, token::{Constant, Token as Tok}, Span, Spanned, @@ -33,7 +36,9 @@ where return lhs; } &(Tok::Punct(punct), span) => { - let r_bp = prefix_binding_power(&Tok::Punct(punct)); + let r_bp = prefix_binding_power(&Tok::Punct(punct)).ok_or_else(|| { + ParserError::new(span, format!("expected expression, found {punct}")) + })?; let Some(op) = unary_op_from_token(&Tok::Punct(punct)) else { panic!() }; let rhs = self.expr_bp(r_bp)?; @@ -64,7 +69,7 @@ where #[allow(clippy::while_let_loop)] // idc loop { - let (tok, _) = match self.peek_t() { + let (tok, tok_span) = match self.peek_t() { Ok(&tok) => tok, Err(_) => break, }; @@ -74,18 +79,48 @@ where 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, - ); + match tok { + Tok::Punct(P::BracketOpen) => { + 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, + ); + } + Tok::Punct(P::ParenOpen) => { + let mut arguments = Vec::new(); + let mut first = true; + let last_span; + loop { + if let Some((_, span)) = eat!(self, Tok::Punct(P::ParenClose)) { + last_span = span; + break; + } + if !first { + expect!(self, Tok::Punct(P::Comma)); + } + first = false; + + let arg = self.expr_bp(0)?; + arguments.push(arg); + } + let span = tok_span.extend(last_span); + + lhs = ( + Expr::Postfix(ExprPostfix { + lhs: Box::new(lhs), + op: PostfixOp::Call(arguments), + }), + span, + ) + } + _ => {} } continue; } @@ -189,13 +224,13 @@ mod powers { pub const POSTFIX: u8 = 29; } -fn prefix_binding_power(tok: &Tok<'_>) -> u8 { - match tok { +fn prefix_binding_power(tok: &Tok<'_>) -> Option { + Some(match tok { Tok::Punct(P::Ampersand | P::Asterisk | P::Plus | P::Minus | P::Tilde | P::Bang) => { powers::UNARY_OPERATOR } - _ => panic!("invalid token in expression! {tok:?}"), - } + _ => return None, + }) } fn infix_binding_power(tok: &Tok<'_>) -> (u8, u8) { @@ -233,7 +268,12 @@ fn infix_binding_power(tok: &Tok<'_>) -> (u8, u8) { fn postfix_binding_power(tok: &Tok<'_>) -> Option { match tok { - Tok::Punct(P::BracketOpen) => Some(45), + Tok::Punct(P::BracketOpen) => Some(powers::POSTFIX), + Tok::Punct(P::ParenOpen) => Some(powers::POSTFIX), + Tok::Punct(P::Dot) => Some(powers::POSTFIX), + Tok::Punct(P::Arrow) => Some(powers::POSTFIX), + Tok::Punct(P::PlusPlus) => Some(powers::POSTFIX), + Tok::Punct(P::MinusMinus) => Some(powers::POSTFIX), _ => None, } } diff --git a/parser/src/parser/snapshots/parser__parser__tests__expr_stmt.snap b/parser/src/parser/snapshots/parser__parser__tests__expr_stmt.snap new file mode 100644 index 0000000..32690b2 --- /dev/null +++ b/parser/src/parser/snapshots/parser__parser__tests__expr_stmt.snap @@ -0,0 +1,48 @@ +--- +source: parser/src/parser/tests.rs +expression: "(parsed_pretty, pretty_printed_source)" +--- +( + Ok([ + ( + FunctionDef(FunctionDef { + decl: Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Integer(IntTy { sign: Signed, kind: Int }), + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: WithParams { + ident: ("main", 5..9), + params: [], + }, + pointer: false, + }, + init: None, + }, + 5..9, + ), + ], + }), + body: [ + ( + Expr( + Binary(ExprBinary { + lhs: (Atom(Int(1)), 18..19), + rhs: (Atom(Int(1)), 22..23), + op: Arith(Add), + }), + ), + 18..23, + ), + (Expr(Atom(String("hello world!"))), 29..42), + ], + }), + 1..46, + ), + ]), + "int main() {\n (1 + 1)\n \"hello world!\"\n}\n", +) diff --git a/parser/src/parser/snapshots/parser__parser__tests__hello_world.snap b/parser/src/parser/snapshots/parser__parser__tests__hello_world.snap new file mode 100644 index 0000000..b6feedb --- /dev/null +++ b/parser/src/parser/snapshots/parser__parser__tests__hello_world.snap @@ -0,0 +1,46 @@ +--- +source: parser/src/parser/tests.rs +expression: "(parsed_pretty, pretty_printed_source)" +--- +( + Ok([ + ( + FunctionDef(FunctionDef { + decl: Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Integer(IntTy { sign: Signed, kind: Int }), + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: WithParams { + ident: ("main", 5..9), + params: [], + }, + pointer: false, + }, + init: None, + }, + 5..9, + ), + ], + }), + body: [ + ( + Expr( + Postfix(ExprPostfix { + lhs: (Atom(Ident(("puts", 18..22))), 18..22), + op: Call([(Atom(String("Hello, world!")), 23..37)]), + }), + ), + 22..39, + ), + ], + }), + 1..42, + ), + ]), + "int main() {\n puts(\"Hello, world!\")\n}\n", +) diff --git a/parser/src/parser/tests.rs b/parser/src/parser/tests.rs index e4dbcb3..72d7595 100644 --- a/parser/src/parser/tests.rs +++ b/parser/src/parser/tests.rs @@ -132,4 +132,27 @@ int main() { } "# ); -} \ No newline at end of file +} + +#[test] +fn expr_stmt() { + parse_test!( + r#" +int main() { + 1 + 1; + "hello world!"; +} + "# + ); +} + +#[test] +fn hello_world() { + parse_test!( + r#" +int main() { + puts("Hello, world!"); +} + "# + ); +} diff --git a/parser/src/pretty.rs b/parser/src/pretty.rs index 9ed6571..810dac7 100644 --- a/parser/src/pretty.rs +++ b/parser/src/pretty.rs @@ -5,9 +5,9 @@ type Result = std::io::Result<()>; use crate::{ ast::{ ArithOpKind, Atom, BinaryOp, ComparisonKind, Decl, DeclAttr, DeclSpec, Declarator, - DirectDeclarator, Expr, ExprBinary, ExprUnary, ExternalDecl, FunctionDef, - FunctionParamDecl, InitDecl, IntTyKind, IntTySignedness, NormalDecl, Stmt, TypeSpecifier, - UnaryOp, + DirectDeclarator, Expr, ExprBinary, ExprPostfix, ExprUnary, ExternalDecl, FunctionDef, + FunctionParamDecl, InitDecl, IntTyKind, IntTySignedness, NormalDecl, PostfixOp, Stmt, + TypeSpecifier, UnaryOp, }, Span, Spanned, }; @@ -335,6 +335,36 @@ impl PrettyPrinter { Ok(()) } Expr::Binary(binary) => self.binary(binary), + Expr::Postfix(ExprPostfix { lhs, op }) => { + self.expr(&lhs.0)?; + match op { + PostfixOp::Call(args) => { + self.string("(")?; + let mut first = true; + for (expr, _) in args { + if !first { + self.string(", ")?; + } + first = false; + self.expr(expr)?; + } + self.string(")")?; + Ok(()) + } + PostfixOp::Member((ident, _)) => { + self.string(".")?; + self.string(ident)?; + Ok(()) + } + PostfixOp::ArrowMember((ident, _)) => { + self.string("->")?; + self.string(ident)?; + Ok(()) + } + PostfixOp::Increment => self.string("++"), + PostfixOp::Decrement => self.string("--"), + } + } } }