use rucc_ast::{
AttrList, Decl, DeclId, DeclSpecs, DeclSpecsId, DeclaratorId, Derived, InitDeclarator,
InitDeclaratorList, ParamKind, StrId,
};
use rucc_diag::Span;
use rucc_lex::{Keyword, Punct};
use rucc_session::Std;
use crate::parser::Parser;
use crate::recover::skip_past_declaration;
use crate::scope::IdentKind;
impl Parser<'_> {
pub(crate) fn translation_unit(&mut self) {
while !self.cursor.is_eof() && !self.stopped() {
let before = self.cursor.index();
let decl = self.external_declaration();
self.ast.add_top_level(decl);
if self.cursor.index() == before {
self.cursor.bump();
}
}
}
fn external_declaration(&mut self) -> DeclId {
let start = self.cursor.span();
if self.cursor.eat_punct(Punct::Semi) {
self.pedantic("E0414", "extra `;` outside of a function", start);
let specs = self.ast.add_specs(DeclSpecs::empty(start));
let declarators = InitDeclaratorList::EMPTY;
return self.add_decl(Decl::Var { specs, declarators }, start);
}
let leading = self.leading_attributes();
self.declaration(leading, start)
}
pub(crate) fn declaration(&mut self, leading: AttrList, start: Span) -> DeclId {
if self.cursor.at_keyword(Keyword::StaticAssert) {
let (cond, message) = self.static_assert_body();
self.expect_punct(Punct::Semi);
let span = self.span_from(start);
return self.add_decl(Decl::StaticAssert { cond, message }, span);
}
if self.cursor.at_keyword(Keyword::Asm) {
let asm = self.asm_body(start);
self.expect_punct(Punct::Semi);
let span = self.span_from(start);
return match asm {
Some(asm) => self.add_decl(Decl::Asm(asm), span),
None => self.poison_decl(span),
};
}
if !leading.is_empty() && self.cursor.eat_punct(Punct::Semi) {
let span = self.span_from(start);
return self.add_decl(Decl::Attributes(leading), span);
}
let specs = self.decl_specs_with(leading, start);
if self.cursor.eat_punct(Punct::Semi) {
let span = self.span_from(start);
let declarators = InitDeclaratorList::EMPTY;
return self.add_decl(Decl::Var { specs, declarators }, span);
}
let mut at = self.cursor.span();
let mut declarator = self.declarator();
if self.at_definition() {
return self.function_definition(specs, declarator, start);
}
let mut items = Vec::new();
loop {
let item = self.init_declarator(specs, declarator, at);
items.push(item);
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
at = self.cursor.span();
let before = self.cursor.index();
declarator = self.declarator();
if self.cursor.index() == before {
break;
}
}
if !self.expect_punct(Punct::Semi) {
skip_past_declaration(&mut self.cursor);
}
let declarators = self.ast.add_init_declarator_list(&items);
let span = self.span_from(start);
self.add_decl(Decl::Var { specs, declarators }, span)
}
fn init_declarator(
&mut self,
specs: DeclSpecsId,
declarator: DeclaratorId,
at: Span,
) -> InitDeclarator {
let asm_label = if self.cursor.at_keyword(Keyword::Asm) { self.asm_label() } else { None };
let attrs = self.attributes();
self.declare_name(specs, declarator);
let init = if self.cursor.eat_punct(Punct::Eq) {
Some(if self.ast[specs].deduces().is_some() {
self.assign_init()
} else {
self.initializer()
})
} else {
None
};
let span = self.span_from(at);
InitDeclarator { declarator, init, asm_label, attrs, span }
}
fn asm_label(&mut self) -> Option<StrId> {
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return None;
}
let name = self.string_literal();
self.expect_punct(Punct::RParen);
name
}
fn declare_name(&mut self, specs: DeclSpecsId, declarator: DeclaratorId) {
let Some(name) = self.ast[declarator].name else { return };
let kind =
if self.ast[specs].is_typedef() { IdentKind::Typedef } else { IdentKind::Ordinary };
self.scopes.declare(name, kind);
}
fn at_definition(&self) -> bool {
if self.cursor.at_keyword(Keyword::Attribute) {
return false;
}
self.cursor.at_punct(Punct::LBrace) || self.at_decl_specs()
}
fn function_definition(
&mut self,
specs: DeclSpecsId,
declarator: DeclaratorId,
start: Span,
) -> DeclId {
self.declare_name(specs, declarator);
self.scopes.push();
self.declare_params(declarator);
let params = self.old_style_params();
if self.cx.std >= Std::C23 && (!params.is_empty() || self.has_identifier_list(declarator)) {
let message = match self.ast[declarator].name {
Some(name) => {
format!(
"`{}` is defined in the old style, which C23 removed",
self.spelling(name)
)
}
None => "this is an old-style definition, which C23 removed".to_string(),
};
self.error("E0412", message, start);
}
let body = if self.cursor.at_punct(Punct::LBrace) {
self.compound_stmt()
} else {
let found = self.describe(self.cursor.current());
let at = self.cursor.span();
self.error("E0412", format!("expected a function body, found {found}"), at);
skip_past_declaration(&mut self.cursor);
self.poison_stmt(at)
};
self.scopes.pop();
let params = self.ast.add_decl_list(¶ms);
let span = self.span_from(start);
self.add_decl(Decl::Function { specs, declarator, params, body }, span)
}
fn has_identifier_list(&self, declarator: DeclaratorId) -> bool {
let derived = self.ast[declarator].derived;
match self.ast[derived].first() {
Some(Derived::Function { kind, .. }) => *kind == ParamKind::Identifiers,
_ => false,
}
}
fn old_style_params(&mut self) -> Vec<DeclId> {
let mut params = Vec::new();
while self.at_decl_specs() && !self.stopped() {
let at = self.cursor.span();
let before = self.cursor.index();
let decl = self.declaration(AttrList::EMPTY, at);
params.push(decl);
if self.cursor.index() == before {
break;
}
}
params
}
}