diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 581485d..4f795b1 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -91,7 +91,8 @@ pub enum Expr { #[derive(Debug, DebugPls)] pub enum Stmt { - Labeled(Box>), + Decl(Decl), + Labeled{label: Ident, stmt: Box>}, Compound(Vec>), If { cond: Expr, @@ -232,7 +233,7 @@ pub struct Declarator { #[derive(Debug, DebugPls)] pub struct FunctionDef { pub decl: Decl, - pub body: Vec, + pub body: Vec>, } #[derive(Debug, DebugPls)] diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 7eca236..69acf09 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -4,10 +4,10 @@ use peekmore::PeekMoreIterator; use crate::{ ast::{ Decl, DeclAttr, DeclSpec, Declarator, DirectDeclarator, ExternalDecl, FunctionDef, - FunctionParamDecl, Ident, InitDecl, IntTy, IntTyKind, IntTySignedness, NormalDecl, + FunctionParamDecl, Ident, InitDecl, IntTy, IntTyKind, IntTySignedness, NormalDecl, Stmt, TypeSpecifier, }, - pre::Punctuator as Punct, + pre::Punctuator as P, token::{Keyword as Kw, Token as Tok}, Span, Spanned, }; @@ -109,10 +109,7 @@ fn is_tok_start_of_ty(tok: &Tok<'_>) -> bool { /// Can be called for the start of a sequence of tokens that could be a type. fn is_tok_start_of_declarator(tok: &Tok<'_>) -> bool { - matches!( - tok, - Tok::Ident(_) | Tok::Punct(Punct::ParenOpen | Punct::Asterisk) - ) + matches!(tok, Tok::Ident(_) | Tok::Punct(P::ParenOpen | P::Asterisk)) } impl<'src, I> Parser<'src, I> @@ -164,7 +161,7 @@ where } fn is_peek_comma(&mut self) -> bool { - matches!(self.peek_t(), Ok((Tok::Punct(Punct::Comma), _))) + matches!(self.peek_t(), Ok((Tok::Punct(P::Comma), _))) } // ----------------------- @@ -174,6 +171,8 @@ 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)) { return Err(ParserError::unsupported(span, &tok)); @@ -202,17 +201,18 @@ where /// init-declarator: /// declarator /// declarator = initializer + /// fn init_declarator_list(&mut self) -> Result>> { let mut init_decls = Vec::new(); let mut first = true; while self.is_peek_tok_start_of_declarator() || self.is_peek_comma() { if !first { - expect!(self, Tok::Punct(Punct::Comma)); + expect!(self, Tok::Punct(P::Comma)); } first = false; let (declarator, span) = self.declarator()?; - let init = if eat!(self, Tok::Punct(Punct::Eq)).is_some() { + let init = if eat!(self, Tok::Punct(P::Eq)).is_some() { let expr = self.expr()?; Some(expr) } else { @@ -328,7 +328,9 @@ where "cannot specify signedness twice".to_string(), )); } - if let Ok((Tok::Kw(Kw::Char | Kw::Short| Kw::Int | Kw::Long), _)) = self.peek_t() { + if let Ok((Tok::Kw(Kw::Char | Kw::Short | Kw::Int | Kw::Long), _)) = + self.peek_t() + { // the signed is an integer qualifier signedness = Some(IntTySignedness::Signed); continue; @@ -345,7 +347,9 @@ where "cannot specify signedness twice".to_string(), )); } - if let Ok((Tok::Kw(Kw::Char | Kw::Short| Kw::Int | Kw::Long), _)) = self.peek_t() { + if let Ok((Tok::Kw(Kw::Char | Kw::Short | Kw::Int | Kw::Long), _)) = + self.peek_t() + { // the unsigned is an integer qualifier signedness = Some(IntTySignedness::Unsigned); continue; @@ -378,7 +382,7 @@ where /// facing. For example: `int uwu` vs `int uwu()`. The parentheses indicate a function /// declaration. Therefore, we have no idea what we're parsing before entering this function. fn declarator(&mut self) -> Result> { - let pointer_span = if let Some((_, span)) = eat!(self, Tok::Punct(Punct::Asterisk)) { + let pointer_span = if let Some((_, span)) = eat!(self, Tok::Punct(P::Asterisk)) { Some(span) } else { None @@ -408,14 +412,14 @@ where fn direct_declarator(&mut self) -> Result> { let (ident, span) = self.ident()?; - if (eat!(self, Tok::Punct(Punct::ParenOpen))).is_some() { + if (eat!(self, Tok::Punct(P::ParenOpen))).is_some() { let mut params = Vec::new(); let mut first = true; while self.is_peek_tok_start_of_ty() || self.is_peek_comma() { if first { // the wrong way around because borrowing - if let (Tok::Punct(Punct::ParenClose), _) = self.peek_t_n(1)? { + if let (Tok::Punct(P::ParenClose), _) = self.peek_t_n(1)? { if let &(ref tok @ Tok::Kw(Kw::Void), span) = self.peek_t()? { return Err(ParserError::unsupported(span, tok)); } @@ -423,7 +427,7 @@ where } if !first { - expect!(self, Tok::Punct(Punct::Comma)); + expect!(self, Tok::Punct(P::Comma)); } first = false; @@ -439,7 +443,7 @@ where } // nothing in the params supported yet. - expect!(self, Tok::Punct(Punct::ParenClose)); + expect!(self, Tok::Punct(P::ParenClose)); return Ok(( DirectDeclarator::WithParams { ident: (ident, span), @@ -452,6 +456,57 @@ where Ok((DirectDeclarator::Ident((ident, span)), span)) } + // ----------------------- + // Statements + // ----------------------- + + /// (6.8.2) block-item: + /// declaration + /// statement + /// + /// (6.8) statement: + /// labeled-statement + /// compound-statement + /// expression-statement + /// selection-statement + /// iteration-statement + /// jump-statement + fn statement(&mut self) -> Result> { + if self.is_peek_tok_start_of_ty() { + let (decl, span) = self.declaration()?; + let span2 = expect!(self, Tok::Punct(P::Semicolon)); + return Ok((Stmt::Decl(decl), span.extend(span2))); + } + todo!() + } + + /// (6.8.2) compound-statement: + /// { block-item-listopt } + /// + /// The leading `{` must already have been eaten + fn compound_statement(&mut self, brace_span: Span) -> Result>>> { + let mut stmts = Vec::new(); + let end_span = loop { + // the end of the block + if let Some((_, span)) = eat!(self, Tok::Punct(P::BraceClose)) { + break span; + } + + // Empty expression statements + // (6.8.3) expression-statement: + // expression.opt ; + if eat!(self, Tok::Punct(P::Semicolon)).is_some() { + continue; + } + + // TODO: recover here + let stmt = self.statement()?; + + stmts.push(stmt); + }; + Ok((stmts, brace_span.extend(end_span))) + } + // ----------------------- // External definitions // ----------------------- @@ -463,18 +518,18 @@ where let (declaration, span) = self.declaration()?; // the declaration might be a function definition - if eat!(self, Tok::Punct(Punct::BraceOpen)).is_some() { - let span2 = expect!(self, Tok::Punct(Punct::BraceClose)); + if let Some((_, brace_span)) = eat!(self, Tok::Punct(P::BraceOpen)) { + let (body, span2) = self.compound_statement(brace_span)?; Ok(( ExternalDecl::FunctionDef(FunctionDef { decl: declaration, - body: Vec::new(), + body, }), span.extend(span2), )) } else { - expect!(self, Tok::Punct(Punct::Semicolon)); + expect!(self, Tok::Punct(P::Semicolon)); Ok((ExternalDecl::Decl(declaration), span)) } } @@ -482,6 +537,7 @@ where fn external_declarations(&mut self) -> Result>> { let mut decls = Vec::new(); while self.peek_t().is_ok() { + // TODO recover let decl = self.external_declaration()?; decls.push(decl); } diff --git a/parser/src/parser/snapshots/parser__parser__tests__decl_stmts.snap b/parser/src/parser/snapshots/parser__parser__tests__decl_stmts.snap new file mode 100644 index 0000000..ae7f4fb --- /dev/null +++ b/parser/src/parser/snapshots/parser__parser__tests__decl_stmts.snap @@ -0,0 +1,90 @@ +--- +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: [ + ( + Decl( + Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Integer(IntTy { sign: Signed, kind: Int }), + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: Ident(("i", 22..23)), + pointer: false, + }, + init: Some((Atom(Int(0)), 26..27)), + }, + 22..23, + ), + ], + }), + ), + 18..28, + ), + ( + Decl( + Normal(NormalDecl { + decl_spec: DeclSpec { + ty: Float, + attrs: "(empty)", + }, + init_declarators: [ + ( + InitDecl { + declarator: Declarator { + decl: Ident(("f", 39..40)), + pointer: false, + }, + init: Some(( + Binary(ExprBinary { + lhs: (Atom(Int(1)), 43..44), + rhs: (Atom(Int(32)), 47..49), + op: Arith(Add), + }), + 43..49, + )), + }, + 39..40, + ), + ], + }), + ), + 33..50, + ), + ], + }), + 1..52, + ), + ]), + "int main() {\n int i = 0;\n float f = (1 + 32);\n}\n", +) diff --git a/parser/src/parser/tests.rs b/parser/src/parser/tests.rs index 6252bcb..e4dbcb3 100644 --- a/parser/src/parser/tests.rs +++ b/parser/src/parser/tests.rs @@ -121,3 +121,15 @@ unsigned long long int z = 26; "# ); } + +#[test] +fn decl_stmts() { + parse_test!( + r#" +int main() { + int i = 0; + float f = 1 + 32; +} + "# + ); +} \ No newline at end of file diff --git a/parser/src/pretty.rs b/parser/src/pretty.rs index e1431e2..9ed6571 100644 --- a/parser/src/pretty.rs +++ b/parser/src/pretty.rs @@ -6,10 +6,10 @@ use crate::{ ast::{ ArithOpKind, Atom, BinaryOp, ComparisonKind, Decl, DeclAttr, DeclSpec, Declarator, DirectDeclarator, Expr, ExprBinary, ExprUnary, ExternalDecl, FunctionDef, - FunctionParamDecl, InitDecl, IntTyKind, IntTySignedness, NormalDecl, TypeSpecifier, + FunctionParamDecl, InitDecl, IntTyKind, IntTySignedness, NormalDecl, Stmt, TypeSpecifier, UnaryOp, }, - Spanned, + Span, Spanned, }; pub struct PrettyPrinter { @@ -39,7 +39,6 @@ impl PrettyPrinter { fn linebreak(&mut self) -> Result { self.string("\n")?; - self.print_indent()?; Ok(()) } @@ -61,23 +60,126 @@ impl PrettyPrinter { fn external_decl(&mut self, decl: &ExternalDecl) -> Result { match decl { ExternalDecl::FunctionDef(def) => self.function_def(def), - ExternalDecl::Decl(decl) => self.decl(decl, false), + ExternalDecl::Decl(decl) => { + self.decl(decl, false)?; + self.linebreak()?; + Ok(()) + } } } fn function_def(&mut self, def: &FunctionDef) -> Result { self.decl(&def.decl, true)?; - self.string(" {")?; - self.linebreak()?; - self.indent(); - // TODO: body - self.dedent(); - self.string("}")?; + self.string(" ")?; + self.block(&def.body)?; self.linebreak()?; Ok(()) } + /// prints a block at the current location, stops right after the last `}` + fn block(&mut self, body: &[(Stmt, Span)]) -> Result { + self.string("{")?; + self.linebreak()?; + self.indent(); + for (stmt, _) in body { + self.print_indent()?; + self.stmt(stmt)?; + self.linebreak()?; + } + self.dedent(); + self.string("}")?; + Ok(()) + } + + fn stmt(&mut self, stmt: &Stmt) -> Result { + match stmt { + Stmt::Decl(decl) => self.decl(decl, false), + Stmt::Labeled { label, stmt } => { + self.string(&label.0)?; + self.string(":")?; + self.linebreak()?; + self.print_indent()?; + self.stmt(&stmt.0)?; + Ok(()) + } + Stmt::Compound(body) => self.block(body), + Stmt::If { + cond, + then, + otherwise, + } => { + self.string("if (")?; + self.expr(cond)?; + self.string(") ")?; + self.block(then)?; + if let Some(block) = otherwise { + self.string(" else ")?; + self.block(block)?; + } + Ok(()) + } + Stmt::Switch => todo!(), + Stmt::While { cond, body } => { + self.string("while (")?; + self.expr(cond)?; + self.string(") ")?; + self.block(body)?; + Ok(()) + } + Stmt::For { + init_decl, + init_expr, + cond, + post, + body, + } => { + if init_expr.is_some() { + todo!() + } + + self.string("for (")?; + if let Some((decl, _)) = init_decl { + self.decl(decl, false)?; + } else { + self.string(";")?; + } + + if let Some((cond, _)) = cond { + self.string(" ")?; + self.expr(cond)?; + } + self.string(";")?; + + if let Some((post, _)) = post { + self.string(" ")?; + self.expr(post)?; + } + + self.string(") ")?; + self.block(body)?; + + Ok(()) + } + Stmt::Goto((label, _)) => { + self.string("goto ")?; + self.string(label)?; + Ok(()) + } + Stmt::Continue => self.string("continue"), + Stmt::Break => self.string("break"), + Stmt::Return(expr) => { + self.string("return")?; + if let Some((expr, _)) = expr { + self.string(" ")?; + self.expr(expr)?; + } + Ok(()) + } + Stmt::Expr(expr) => self.expr(expr), + } + } + fn decl(&mut self, decl: &Decl, func: bool) -> Result { match decl { Decl::StaticAssert => todo!(), @@ -85,7 +187,6 @@ impl PrettyPrinter { } if !func { self.string(";")?; - self.linebreak()?; } Ok(()) }