use std::collections::HashMap;
use super::cpp_ast::*;
use super::cpp_token::*;
use super::token::Token;
use super::ast::QualType;
#[derive(Debug, Clone)]
struct CppTokenInfo {
kind: CppTokenKind,
text: String,
line: usize,
column: usize,
}
#[derive(Debug, Clone, Copy, Default)]
struct CppParseFlags {
allow_cxx11: bool,
allow_cxx14: bool,
allow_cxx17: bool,
allow_cxx20: bool,
allow_cxx23: bool,
in_template_param: bool,
}
pub struct CppParser {
tokens: Vec<CppTokenInfo>,
pos: usize,
standard: CppStandard,
tu: CXXTranslationUnit,
errors: Vec<String>,
current_access: AccessSpecifier,
type_names: HashMap<String, QualType>,
class_names: HashMap<String, CXXRecordKind>,
template_depth: usize,
class_name_stack: Vec<String>,
flags: CppParseFlags,
}
impl CppParser {
pub fn new(tokens: Vec<Token>, standard: CppStandard) -> Self {
let cpp_tokens: Vec<CppTokenInfo> = tokens
.iter()
.map(|t| CppTokenInfo {
kind: CppTokenKind::KwAuto_,
text: format!("{}", t.kind),
line: 0,
column: 0,
})
.collect();
let flags = CppParseFlags {
allow_cxx11: standard >= CppStandard::Cxx11,
allow_cxx14: standard >= CppStandard::Cxx14,
allow_cxx17: standard >= CppStandard::Cxx17,
allow_cxx20: standard >= CppStandard::Cxx20,
allow_cxx23: standard >= CppStandard::Cxx23,
..Default::default()
};
Self {
tokens: cpp_tokens,
pos: 0,
standard,
tu: CXXTranslationUnit::new("<source>", standard),
errors: Vec::new(),
current_access: AccessSpecifier::None,
type_names: HashMap::new(),
class_names: HashMap::new(),
template_depth: 0,
class_name_stack: Vec::new(),
flags,
}
}
pub fn parse(&mut self) -> Result<CXXTranslationUnit, Vec<String>> {
self.parse_translation_unit();
if self.errors.is_empty() {
Ok(std::mem::replace(
&mut self.tu,
CXXTranslationUnit::new("<source>", self.standard),
))
} else {
Err(self.errors.clone())
}
}
fn peek(&self) -> Option<&CppTokenInfo> {
self.tokens.get(self.pos)
}
fn peek_kind(&self) -> Option<CppTokenKind> {
self.peek().map(|t| t.kind)
}
fn peek_n(&self, n: usize) -> Option<&CppTokenInfo> {
self.tokens.get(self.pos + n)
}
fn peek_n_kind(&self, n: usize) -> Option<CppTokenKind> {
self.peek_n(n).map(|t| t.kind)
}
fn advance(&mut self) -> Option<CppTokenInfo> {
if self.pos < self.tokens.len() {
let tok = self.tokens[self.pos].clone();
self.pos += 1;
Some(tok)
} else {
None
}
}
fn expect(&mut self, kind: CppTokenKind) -> Result<CppTokenInfo, String> {
match self.peek() {
Some(tok) if tok.kind == kind => Ok(self.advance().unwrap()),
Some(tok) => {
let err = format!(
"expected '{}' but got '{}' at line {}",
kind, tok.kind, tok.line
);
self.errors.push(err.clone());
Err(err)
}
None => {
let err = format!("expected '{}' but reached end of file", kind);
self.errors.push(err.clone());
Err(err)
}
}
}
fn consume_if(&mut self, kind: CppTokenKind) -> bool {
if self.peek_kind() == Some(kind) {
self.advance();
true
} else {
false
}
}
fn parse_ellipsis(&mut self) -> bool {
self.peek()
.map_or(false, |t| t.text == "..." || t.text == "...")
}
fn parse_translation_unit(&mut self) {
while self.peek().is_some() {
self.parse_top_level_declaration();
}
}
fn parse_top_level_declaration(&mut self) {
self.parse_attribute_spec_seq();
match self.peek_kind() {
Some(CppTokenKind::KwExport) => self.parse_export_or_module(),
Some(CppTokenKind::KwModule) => self.parse_module_or_import(),
Some(CppTokenKind::KwTemplate) => self.parse_template_declaration(),
Some(CppTokenKind::KwNamespace) => self.parse_namespace(),
Some(CppTokenKind::KwClass)
| Some(CppTokenKind::KwStruct)
| Some(CppTokenKind::KwUnion) => self.parse_class_declaration(),
Some(CppTokenKind::KwUsing) => self.parse_using_declaration_or_directive(),
Some(CppTokenKind::KwTypedef) => self.parse_typedef(),
Some(CppTokenKind::KwEnum) => self.parse_enum_declaration(),
Some(CppTokenKind::KwExplicit) => self.parse_explicit_instantiation(),
Some(CppTokenKind::KwStaticAssert) => self.parse_static_assert(),
Some(CppTokenKind::KwInline2) => self.parse_inline_declaration(),
_ => self.parse_declaration_or_function(),
}
}
fn parse_attribute_spec_seq(&mut self) -> Vec<CXXAttribute> {
let mut attrs = Vec::new();
while self.peek_kind() == Some(CppTokenKind::LBracket)
&& self.peek_n_kind(1) == Some(CppTokenKind::LBracket)
{
if let Some(attr) = self.parse_cxx_attribute() {
attrs.push(attr);
}
}
attrs
}
fn parse_cxx_attribute(&mut self) -> Option<CXXAttribute> {
if !self.consume_if(CppTokenKind::LBracket) || !self.consume_if(CppTokenKind::LBracket) {
return None;
}
let mut ns = None;
let first_text = self.advance()?.text;
let name = if self.peek_kind() == Some(CppTokenKind::Scope) {
self.advance();
ns = Some(first_text);
self.advance()?.text
} else {
first_text
};
let mut args = Vec::new();
if self.consume_if(CppTokenKind::LParen) {
loop {
if self.peek_kind() == Some(CppTokenKind::RParen) {
break;
}
args.push(self.parse_attribute_argument());
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
self.consume_if(CppTokenKind::RParen);
}
self.consume_if(CppTokenKind::RBracket);
self.consume_if(CppTokenKind::RBracket);
Some(CXXAttribute {
namespace: ns,
name,
args,
})
}
fn parse_attribute_argument(&mut self) -> String {
let mut depth = 0;
let mut text = String::new();
while let Some(tok) = self.peek() {
match tok.kind {
CppTokenKind::LParen | CppTokenKind::LBracket | CppTokenKind::LBrace => {
depth += 1;
text.push_str(&self.advance().unwrap().text);
}
CppTokenKind::RParen | CppTokenKind::RBracket | CppTokenKind::RBrace => {
if depth == 0 {
break;
}
depth -= 1;
text.push_str(&self.advance().unwrap().text);
}
CppTokenKind::Comma if depth == 0 => break,
_ => {
text.push_str(&self.advance().unwrap().text);
}
}
}
text
}
fn parse_alignment_specifier(&mut self) -> Option<u64> {
if !self.consume_if(CppTokenKind::KwAlignas) {
return None;
}
self.consume_if(CppTokenKind::LParen);
let mut value = 0u64;
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RParen) {
let tok = self.advance().unwrap();
if let Ok(v) = tok.text.parse::<u64>() {
value = v;
}
}
self.consume_if(CppTokenKind::RParen);
Some(value)
}
fn parse_export_or_module(&mut self) {
let is_exported = self.consume_if(CppTokenKind::KwExport);
if self.consume_if(CppTokenKind::KwModule) {
let name = self.parse_module_name();
self.tu.add_decl(CXXDecl::ModuleDecl {
name: name.to_string(),
is_interface: is_exported,
is_partition: false,
});
}
}
fn parse_module_or_import(&mut self) {
self.advance();
if self.consume_if(CppTokenKind::SemiColon) {
self.tu.add_decl(CXXDecl::GlobalModuleFragment);
return;
}
let name = self.parse_module_name();
self.tu.add_decl(CXXDecl::ModuleDecl {
name: name.to_string(),
is_interface: false,
is_partition: false,
});
}
fn parse_module_name(&mut self) -> String {
let mut parts = Vec::new();
loop {
if let Some(tok) = self.advance() {
parts.push(tok.text);
} else {
break;
}
if self.peek_kind() == Some(CppTokenKind::Dot) {
self.advance();
} else {
break;
}
}
parts.join(".")
}
fn parse_namespace(&mut self) {
self.advance(); let is_inline = false;
let name = if self.peek_kind() == Some(CppTokenKind::KwAuto_) {
self.advance();
"".to_string()
} else {
let first = self.advance().map(|t| t.text).unwrap_or_default();
if self.peek_kind() == Some(CppTokenKind::Scope) {
let mut parts = vec![first];
while self.consume_if(CppTokenKind::Scope) {
if let Some(tok) = self.advance() {
parts.push(tok.text);
}
}
parts.join("::")
} else {
first
}
};
if self.consume_if(CppTokenKind::Assign) {
let aliased = self.parse_nested_name();
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::NamespaceAlias {
name,
aliased_ns: aliased,
});
return;
}
if self.consume_if(CppTokenKind::LBrace) {
let mut members = Vec::new();
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RBrace) {
self.parse_top_level_declaration();
}
self.consume_if(CppTokenKind::RBrace);
if name.is_empty() {
self.tu.add_decl(CXXDecl::AnonymousNamespace { members });
} else {
self.tu.add_decl(CXXDecl::Namespace {
name,
is_inline,
members,
});
}
}
}
fn parse_class_declaration(&mut self) {
let kind_tok = self.advance().unwrap();
let kind = match kind_tok.kind {
CppTokenKind::KwClass => CXXRecordKind::Class,
CppTokenKind::KwStruct => CXXRecordKind::Struct,
CppTokenKind::KwUnion => CXXRecordKind::Union,
_ => CXXRecordKind::Class,
};
self.parse_attribute_spec_seq();
let name = if self.peek_kind() == Some(CppTokenKind::Scope) {
self.advance();
self.advance().map(|t| t.text).unwrap_or_default()
} else {
let first = self.advance().map(|t| t.text).unwrap_or_default();
if self.consume_if(CppTokenKind::Scope) {
self.advance().map(|t| t.text).unwrap_or_default()
} else {
first
}
};
let is_final = self.consume_if(CppTokenKind::KwFinal);
let mut bases = Vec::new();
if self.consume_if(CppTokenKind::Colon) {
self.parse_base_clause(&mut bases);
}
if self.peek_kind() == Some(CppTokenKind::LBrace) {
self.class_name_stack.push(name.clone());
self.parse_class_body(name.clone(), kind, bases, is_final);
self.class_name_stack.pop();
} else {
self.tu.add_decl(CXXDecl::CXXRecordForward { name, kind });
self.consume_if(CppTokenKind::SemiColon);
}
}
fn parse_base_clause(&mut self, bases: &mut Vec<BaseSpecifier>) {
loop {
let mut is_virtual = false;
let mut access = AccessSpecifier::Private;
if self.consume_if(CppTokenKind::KwVirtual) {
is_virtual = true;
}
match self.peek_kind() {
Some(CppTokenKind::KwPublic) => {
self.advance();
access = AccessSpecifier::Public;
}
Some(CppTokenKind::KwProtected) => {
self.advance();
access = AccessSpecifier::Protected;
}
Some(CppTokenKind::KwPrivate) => {
self.advance();
access = AccessSpecifier::Private;
}
_ => {}
}
if self.consume_if(CppTokenKind::KwVirtual) {
is_virtual = true;
}
let base_name = self.parse_class_or_typename();
let base_ty = if self.peek_kind() == Some(CppTokenKind::Lt) {
self.parse_template_id(base_name)
} else {
CXXName::new(&base_name)
};
bases.push(BaseSpecifier {
base_ty,
is_virtual,
access,
});
self.parse_ellipsis();
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
}
fn parse_class_or_typename(&mut self) -> String {
if self.peek_kind() == Some(CppTokenKind::Scope) {
self.advance();
self.advance().map(|t| t.text).unwrap_or_default()
} else {
let mut text = String::new();
while self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::Scope) => {
text.push_str("::");
self.advance();
}
_ => {
text.push_str(&self.advance().unwrap().text);
if self.peek_kind() != Some(CppTokenKind::Scope) {
break;
}
}
}
}
text
}
}
fn parse_class_body(
&mut self,
name: String,
kind: CXXRecordKind,
bases: Vec<BaseSpecifier>,
is_final: bool,
) {
self.expect(CppTokenKind::LBrace).ok();
let mut members = Vec::new();
self.current_access = kind.default_access();
let mut is_polymorphic = false;
let mut is_abstract = false;
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RBrace) {
match self.peek_kind() {
Some(CppTokenKind::KwPublic) => {
self.advance();
self.expect(CppTokenKind::Colon).ok();
self.current_access = AccessSpecifier::Public;
members.push(CXXMemberDecl::AccessSpec(AccessSpecifier::Public));
}
Some(CppTokenKind::KwProtected) => {
self.advance();
self.expect(CppTokenKind::Colon).ok();
self.current_access = AccessSpecifier::Protected;
members.push(CXXMemberDecl::AccessSpec(AccessSpecifier::Protected));
}
Some(CppTokenKind::KwPrivate) => {
self.advance();
self.expect(CppTokenKind::Colon).ok();
self.current_access = AccessSpecifier::Private;
members.push(CXXMemberDecl::AccessSpec(AccessSpecifier::Private));
}
_ => {
if let Some(member) = self.parse_member_declaration(&name) {
if matches!(
member,
CXXMemberDecl::Method {
is_virtual: true,
..
} | CXXMemberDecl::Destructor {
is_virtual: true,
..
}
) {
is_polymorphic = true;
}
if matches!(
member,
CXXMemberDecl::Method {
is_pure_virtual: true,
..
}
) {
is_abstract = true;
}
members.push(member);
} else {
self.advance();
}
}
}
}
self.consume_if(CppTokenKind::RBrace);
self.consume_if(CppTokenKind::SemiColon);
self.class_names.insert(name.clone(), kind);
self.tu.add_decl(CXXDecl::CXXRecord {
name,
kind,
bases,
members,
is_final,
is_polymorphic,
is_abstract,
template_params: None,
});
}
fn parse_member_declaration(&mut self, class_name: &str) -> Option<CXXMemberDecl> {
self.parse_attribute_spec_seq();
if self.consume_if(CppTokenKind::KwFriend) {
return self.parse_friend_declaration();
}
if matches!(
self.peek_kind(),
Some(CppTokenKind::KwPublic)
| Some(CppTokenKind::KwProtected)
| Some(CppTokenKind::KwPrivate)
) {
return None;
}
let is_virtual = self.consume_if(CppTokenKind::KwVirtual);
let is_static = self.consume_if(CppTokenKind::KwStatic);
let is_explicit = self.consume_if(CppTokenKind::KwExplicit);
let is_constexpr = self.consume_if(CppTokenKind::KwConstexpr);
let is_consteval = self.flags.allow_cxx20 && self.consume_if(CppTokenKind::KwConsteval);
let is_mutable = self.consume_if(CppTokenKind::KwMutable);
if self.peek_kind() == Some(CppTokenKind::KwUsing) {
return self.parse_member_using_declaration();
}
if self.peek_kind() == Some(CppTokenKind::KwTemplate) {
self.parse_template_declaration();
return None;
}
let first = self.advance()?;
let first_text = first.text.clone();
if first_text == "~" || first_text.starts_with('~') {
let (body, is_default, is_deleted) = self.parse_function_body_cxx();
self.consume_if(CppTokenKind::SemiColon);
return Some(CXXMemberDecl::Destructor {
body,
is_virtual,
is_default,
is_deleted,
access: self.current_access,
});
}
if first_text == class_name
|| (self.peek_kind() == Some(CppTokenKind::Scope)
&& self.peek_n(1).map_or(false, |t| t.text == class_name))
{
let params = self.parse_parameter_list();
let init_list = self.parse_ctor_initializer();
let body = if self.peek_kind() == Some(CppTokenKind::KwTry) {
Some(CXXCompoundStmt::new()) } else {
let (b, _, _) = self.parse_function_body_cxx();
b
};
self.consume_if(CppTokenKind::SemiColon);
return Some(CXXMemberDecl::Constructor {
params,
body,
init_list,
is_explicit,
is_constexpr,
is_default: false,
is_deleted: false,
access: self.current_access,
});
}
if first_text == "operator" || first.kind == CppTokenKind::KwOperator {
return self.parse_operator_declaration();
}
if self.peek_kind() == Some(CppTokenKind::LParen) {
let params = self.parse_parameter_list();
let is_const = self.consume_if(CppTokenKind::KwConst);
let is_volatile = self.consume_if(CppTokenKind::KwVolatile);
let ref_qualifier = None; let is_noexcept = self.parse_noexcept_specifier();
let is_override = self.consume_if(CppTokenKind::KwOverride);
let is_final = self.consume_if(CppTokenKind::KwFinal);
if self.flags.allow_cxx20 {
self.parse_requires_clause();
}
let trailing_return_ty = if self.consume_if(CppTokenKind::ArrowStar) {
Some(self.parse_type_specifier())
} else {
None
};
let is_pure_virtual = if self.peek_kind() == Some(CppTokenKind::Assign) {
self.advance();
self.peek().map_or(false, |t| t.text == "0") && {
self.advance();
true
}
} else {
false
};
let (body, _, _) = if is_pure_virtual {
(None, false, false)
} else {
self.parse_function_body_cxx()
};
self.consume_if(CppTokenKind::SemiColon);
return Some(CXXMemberDecl::Method {
name: first_text,
return_ty: QualType::int(),
params,
body,
is_virtual,
is_pure_virtual,
is_override,
is_final,
is_const,
is_volatile,
is_static,
is_noexcept,
ref_qualifier,
is_explicit,
is_constexpr,
is_consteval,
trailing_return_ty,
access: self.current_access,
});
}
let second = self.advance();
let field_name = second.map(|t| t.text).unwrap_or_default();
let bit_width = if self.consume_if(CppTokenKind::Colon) {
self.advance()
.map(|t| Box::new(CXXExpr::IntegerLiteral(t.text.parse().unwrap_or(0))))
} else {
None
};
let default_init = if self.consume_if(CppTokenKind::Assign) {
Some(Box::new(CXXExpr::IntegerLiteral(0))) } else {
None
};
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::SemiColon) {
self.advance();
}
self.consume_if(CppTokenKind::SemiColon);
Some(CXXMemberDecl::Field {
name: field_name,
ty: QualType::int(),
is_mutable,
is_static,
bit_width,
default_init,
access: self.current_access,
})
}
fn parse_friend_declaration(&mut self) -> Option<CXXMemberDecl> {
if matches!(
self.peek_kind(),
Some(CppTokenKind::KwClass) | Some(CppTokenKind::KwStruct)
) {
self.advance();
let friend_name = self.advance().map(|t| t.text).unwrap_or_default();
self.consume_if(CppTokenKind::SemiColon);
return Some(CXXMemberDecl::FriendClass {
class_name: CXXName::new(&friend_name),
access: self.current_access,
});
}
let name = self.advance().map(|t| t.text).unwrap_or_default();
let params = if self.peek_kind() == Some(CppTokenKind::LParen) {
self.parse_parameter_list()
} else {
vec![]
};
self.consume_if(CppTokenKind::SemiColon);
Some(CXXMemberDecl::FriendFunction {
name: CXXName::new(&name),
params,
access: self.current_access,
})
}
fn parse_member_using_declaration(&mut self) -> Option<CXXMemberDecl> {
self.advance(); let name = self.parse_nested_name();
self.consume_if(CppTokenKind::SemiColon);
Some(CXXMemberDecl::UsingDecl {
name,
access: self.current_access,
})
}
fn parse_operator_declaration(&mut self) -> Option<CXXMemberDecl> {
if self.peek().map_or(false, |t| t.text == "operator") {
self.advance();
}
let op = self.parse_operator_function_id();
let params = if self.peek_kind() == Some(CppTokenKind::LParen) {
self.parse_parameter_list()
} else {
vec![]
};
let is_const = self.consume_if(CppTokenKind::KwConst);
let is_noexcept = self.parse_noexcept_specifier();
let (body, is_default, is_deleted) = self.parse_function_body_cxx();
self.consume_if(CppTokenKind::SemiColon);
Some(CXXMemberDecl::OperatorDecl {
op,
params,
body,
is_const,
is_noexcept,
is_default,
is_deleted,
access: self.current_access,
})
}
fn parse_operator_function_id(&mut self) -> CXXOperator {
match self.peek_kind() {
Some(CppTokenKind::KwNew) => {
self.advance();
if self.consume_if(CppTokenKind::LBracket)
&& self.consume_if(CppTokenKind::RBracket)
{
CXXOperator::NewArray
} else {
CXXOperator::New
}
}
Some(CppTokenKind::KwDelete) => {
self.advance();
if self.consume_if(CppTokenKind::LBracket)
&& self.consume_if(CppTokenKind::RBracket)
{
CXXOperator::DeleteArray
} else {
CXXOperator::Delete
}
}
Some(CppTokenKind::LParen) => {
self.advance();
self.consume_if(CppTokenKind::RParen);
CXXOperator::Call
}
Some(CppTokenKind::LBracket) => {
self.advance();
self.consume_if(CppTokenKind::RBracket);
CXXOperator::Subscript
}
_ => {
let tok = self.advance().unwrap();
match tok.text.as_str() {
"+" => CXXOperator::Add,
"-" => CXXOperator::Sub,
"*" => CXXOperator::Mul,
"/" => CXXOperator::Div,
"%" => CXXOperator::Mod,
"^" => CXXOperator::BitXor,
"&" => CXXOperator::BitAnd,
"|" => CXXOperator::BitOr,
"~" => CXXOperator::LogicalNot,
"!" => CXXOperator::LogicalNot,
"=" => CXXOperator::Assign,
"<" => CXXOperator::Lt,
">" => CXXOperator::Gt,
"+=" => CXXOperator::AddAssign,
"-=" => CXXOperator::SubAssign,
"*=" => CXXOperator::MulAssign,
"/=" => CXXOperator::DivAssign,
"%=" => CXXOperator::ModAssign,
"^=" => CXXOperator::XorAssign,
"&=" => CXXOperator::AndAssign,
"|=" => CXXOperator::OrAssign,
"<<" => CXXOperator::Shl,
">>" => CXXOperator::Shr,
"<<=" => CXXOperator::ShlAssign,
">>=" => CXXOperator::ShrAssign,
"==" => CXXOperator::Eq,
"!=" => CXXOperator::Ne,
"<=" => CXXOperator::Le,
">=" => CXXOperator::Ge,
"<=>" => CXXOperator::Spaceship,
"&&" => CXXOperator::LogicalAnd,
"||" => CXXOperator::LogicalOr,
"++" => CXXOperator::PreInc,
"--" => CXXOperator::PreDec,
"," => CXXOperator::Comma,
"->*" => CXXOperator::ArrowStar,
"->" => CXXOperator::Arrow,
_ => CXXOperator::Custom(tok.text.clone()),
}
}
}
}
fn parse_template_declaration(&mut self) {
self.advance(); self.expect(CppTokenKind::Lt).ok();
self.template_depth += 1;
let params = self.parse_template_parameter_list();
self.template_depth -= 1;
if self.peek_kind() == Some(CppTokenKind::RShiftAssign) {
self.consume_if(CppTokenKind::RShiftAssign);
}
self.expect(CppTokenKind::Gt).ok();
if self.flags.allow_cxx20 {
self.parse_requires_clause();
}
match self.peek_kind() {
Some(CppTokenKind::KwClass)
| Some(CppTokenKind::KwStruct)
| Some(CppTokenKind::KwUnion) => self.parse_class_declaration(),
Some(CppTokenKind::KwUsing) => self.parse_alias_template(),
_ => self.parse_declaration_or_function(),
}
}
fn parse_template_parameter_list(&mut self) -> Vec<TemplateParamDecl> {
let mut params = Vec::new();
let old = self.flags.in_template_param;
self.flags.in_template_param = true;
loop {
match self.peek_kind() {
Some(CppTokenKind::KwTypename) | Some(CppTokenKind::KwClass) => {
let is_typename = self.consume_if(CppTokenKind::KwTypename);
if !is_typename {
self.advance();
}
let is_pack = self.parse_ellipsis();
if let Some(tok) = self.advance() {
let mut default_ty = None;
if self.consume_if(CppTokenKind::Assign) {
default_ty = Some(self.parse_type_specifier());
}
params.push(TemplateParamDecl::TypeParm {
name: tok.text,
is_typename,
default_ty,
is_parameter_pack: is_pack,
});
}
}
Some(_) => {
let ty = self.parse_type_specifier();
let is_pack = self.parse_ellipsis();
if let Some(tok) = self.advance() {
let mut default_val = None;
if self.consume_if(CppTokenKind::Assign) {
default_val = self.advance().map(|t| t.text);
}
params.push(TemplateParamDecl::NonTypeParm {
name: tok.text,
ty,
default_value: None,
is_parameter_pack: is_pack,
default_val,
});
}
}
None => break,
}
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
self.flags.in_template_param = old;
params
}
fn parse_explicit_instantiation(&mut self) {
self.advance(); if self.consume_if(CppTokenKind::KwTemplate) {
let name = self.advance().map(|t| t.text).unwrap_or_default();
let template_args = self.parse_template_arguments();
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::ExplicitInstantiation {
is_extern: None,
name: CXXName {
scope: None,
name,
template_args,
},
});
} else {
self.parse_declaration_or_function();
}
}
fn parse_template_arguments(&mut self) -> Option<Vec<TemplateArgument>> {
if !self.consume_if(CppTokenKind::Lt) {
return None;
}
self.template_depth += 1;
let mut args = Vec::new();
loop {
if self.peek_kind() == Some(CppTokenKind::Gt)
|| self.peek_kind() == Some(CppTokenKind::RShiftAssign)
{
break;
}
if self.looks_like_type() {
let ty = self.parse_type_specifier();
args.push(TemplateArgument::Type(ty));
} else if let Some(tok) = self.advance() {
args.push(TemplateArgument::NonType(CXXExpr::IntegerLiteral(
tok.text.parse().unwrap_or(0),
)));
}
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
if self.template_depth > 0 && self.peek_kind() == Some(CppTokenKind::RShiftAssign) {
self.consume_if(CppTokenKind::RShiftAssign);
self.template_depth -= 1;
return Some(args);
}
self.consume_if(CppTokenKind::Gt);
self.template_depth -= 1;
Some(args)
}
fn parse_template_id(&mut self, name: String) -> CXXName {
let template_args = self.parse_template_arguments();
CXXName {
scope: None,
name,
template_args,
}
}
fn parse_alias_template(&mut self) {
self.advance(); let name = self.advance().map(|t| t.text).unwrap_or_default();
self.consume_if(CppTokenKind::Assign);
let aliased_ty = self.parse_type_specifier();
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::TypeAlias { name, aliased_ty });
}
fn parse_requires_clause(&mut self) -> Option<String> {
if !self.consume_if(CppTokenKind::KwRequires) {
return None;
}
let mut clause = String::new();
if self.peek_kind() == Some(CppTokenKind::LBrace) {
self.advance();
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::LBrace) => depth += 1,
Some(CppTokenKind::RBrace) => depth -= 1,
_ => {}
}
if depth > 0 {
if let Some(tok) = self.advance() {
clause.push_str(&tok.text);
clause.push(' ');
}
} else {
self.advance();
}
}
}
Some(clause)
}
fn parse_using_declaration_or_directive(&mut self) {
self.advance();
if self.flags.allow_cxx20 && self.consume_if(CppTokenKind::KwEnum) {
let enum_name = self.parse_nested_name();
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::UsingEnum { enum_name });
return;
}
if self.consume_if(CppTokenKind::KwNamespace) {
let name = self.parse_nested_name();
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::UsingDirective {
namespace_name: name,
});
return;
}
let name = self.parse_nested_name();
if self.consume_if(CppTokenKind::Assign) {
let aliased_ty = self.parse_type_specifier();
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::TypeAlias {
name: name.to_string(),
aliased_ty,
});
} else {
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::UsingDeclaration {
name: CXXName::new(&name.to_string()),
});
}
}
fn parse_typedef(&mut self) {
self.advance(); let ty = self.parse_type_specifier();
if let Some(tok) = self.advance() {
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::Typedef {
name: tok.text,
aliased_ty: ty,
});
}
}
fn parse_static_assert(&mut self) {
self.advance(); self.consume_if(CppTokenKind::LParen);
let mut depth = 0;
while self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::LParen) => depth += 1,
Some(CppTokenKind::RParen) => {
if depth == 0 {
break;
}
depth -= 1;
}
_ => {}
}
self.advance();
}
self.consume_if(CppTokenKind::RParen);
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::StaticAssert {
condition: String::new(),
message: None,
});
}
fn parse_enum_declaration(&mut self) {
self.advance(); let is_scoped =
self.consume_if(CppTokenKind::KwClass) || self.consume_if(CppTokenKind::KwStruct);
let name = self.advance().map(|t| t.text).unwrap_or_default();
let underlying_ty = if self.consume_if(CppTokenKind::Colon) {
Some(self.parse_type_specifier())
} else {
None
};
let mut enumerators = Vec::new();
if self.consume_if(CppTokenKind::LBrace) {
loop {
if let Some(tok) = self.advance() {
let mut value = None;
if self.consume_if(CppTokenKind::Assign) {
if let Some(val_tok) = self.peek() {
value = val_tok.text.parse::<i64>().ok();
self.advance();
}
while self.peek().is_some()
&& self.peek_kind() != Some(CppTokenKind::Comma)
&& self.peek_kind() != Some(CppTokenKind::RBrace)
{
self.advance();
}
}
enumerators.push(EnumConstant {
name: tok.text,
value,
});
}
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
self.expect(CppTokenKind::RBrace).ok();
}
self.consume_if(CppTokenKind::SemiColon);
self.tu.add_decl(CXXDecl::Enum {
name,
underlying_ty,
is_scoped,
enumerators,
});
}
fn parse_declaration_or_function(&mut self) {
self.parse_attribute_spec_seq();
let _ = self.consume_if(CppTokenKind::KwInline2);
let Some(first) = self.advance() else { return };
let first_text = first.text.clone();
if self.peek_kind() == Some(CppTokenKind::LParen) {
self.parse_function_definition(first_text);
return;
}
if self.consume_if(CppTokenKind::Scope) {
let second = self.advance().map(|t| t.text).unwrap_or_default();
if self.peek_kind() == Some(CppTokenKind::LParen) {
self.parse_function_definition(second);
return;
}
}
while self.peek().is_some()
&& self.peek_kind() != Some(CppTokenKind::SemiColon)
&& self.peek_kind() != Some(CppTokenKind::LBrace)
{
self.advance();
}
if self.peek_kind() == Some(CppTokenKind::LBrace) {
let mut depth = 1;
self.advance();
while self.peek().is_some() && depth > 0 {
match self.peek_kind() {
Some(CppTokenKind::LBrace) => depth += 1,
Some(CppTokenKind::RBrace) => depth -= 1,
_ => {}
}
self.advance();
}
}
self.consume_if(CppTokenKind::SemiColon);
}
fn parse_function_definition(&mut self, name: String) {
let params = self.parse_parameter_list();
let is_const = self.consume_if(CppTokenKind::KwConst);
let is_volatile = self.consume_if(CppTokenKind::KwVolatile);
let is_noexcept = self.parse_noexcept_specifier();
if self.flags.allow_cxx20 {
self.parse_requires_clause();
}
let trailing_return_ty = if self.consume_if(CppTokenKind::ArrowStar) {
Some(self.parse_type_specifier())
} else {
None
};
let init_list = self.parse_ctor_initializer();
let body = if self.peek_kind() == Some(CppTokenKind::KwTry) {
Some(CXXCompoundStmt::new()) } else {
let (b, _, _) = self.parse_function_body_cxx();
b
};
self.tu.add_decl(CXXDecl::Function {
name,
params,
return_ty: QualType::int(),
body,
is_const,
is_noexcept,
is_constexpr: false,
is_consteval: false,
is_virtual: false,
is_override: false,
is_final: false,
is_static: false,
is_inline: false,
is_explicit: false,
is_deleted: false,
is_defaulted: false,
ref_qualifier: None,
trailing_return_ty,
template_params: None,
is_volatile,
linkage: String::new(),
init_list,
});
}
fn parse_parameter_list(&mut self) -> Vec<CXXParamDecl> {
if !self.consume_if(CppTokenKind::LParen) {
return vec![];
}
let mut params = Vec::new();
if self.consume_if(CppTokenKind::RParen) {
return params;
}
loop {
let param_ty = if self.peek_kind() == Some(CppTokenKind::KwAuto_) {
self.advance();
QualType::auto_ty()
} else {
self.parse_type_specifier()
};
let param_name = self.advance().map(|t| t.text).unwrap_or_default();
let default = if self.consume_if(CppTokenKind::Assign) {
Some(self.parse_initializer_clause())
} else {
None
};
params.push(CXXParamDecl {
name: param_name,
ty: param_ty,
has_default: default.is_some(),
default_value: None,
is_parameter_pack: false,
default,
is_pack: false,
});
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
self.consume_if(CppTokenKind::RParen);
params
}
fn parse_ctor_initializer(&mut self) -> Vec<CtorInit> {
let mut inits = Vec::new();
if !self.consume_if(CppTokenKind::Colon) {
return inits;
}
loop {
let name = self.advance().map(|t| t.text).unwrap_or_default();
let args = if self.peek_kind() == Some(CppTokenKind::LParen) {
self.advance();
let mut a = Vec::new();
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RParen) {
if let Some(tok) = self.advance() {
a.push(tok.text);
}
self.consume_if(CppTokenKind::Comma);
}
self.consume_if(CppTokenKind::RParen);
a
} else if self.peek_kind() == Some(CppTokenKind::LBrace) {
vec![self.parse_braced_init_list()]
} else {
vec![]
};
inits.push(CtorInit::Simple { member: name, args });
self.parse_ellipsis();
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
inits
}
fn parse_function_body_cxx(&mut self) -> (Option<CXXCompoundStmt>, bool, bool) {
if self.peek_kind() == Some(CppTokenKind::Assign) {
self.advance();
if self.peek().map_or(false, |t| t.text == "default") {
self.advance();
return (None, true, false);
}
if self.peek().map_or(false, |t| t.text == "delete") {
self.advance();
return (None, false, true);
}
}
if self.peek_kind() == Some(CppTokenKind::LBrace) {
let mut stmts = Vec::new();
self.advance();
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::LBrace) => depth += 1,
Some(CppTokenKind::RBrace) => {
depth -= 1;
if depth == 0 {
self.advance();
continue;
}
}
_ => {}
}
if let Some(tok) = self.advance() {
stmts.push(CXXStmt::ExprStmt(CXXExpr::IntegerLiteral(0)));
}
}
return (Some(CXXCompoundStmt { stmts }), false, false);
}
(None, false, false)
}
fn parse_noexcept_specifier(&mut self) -> bool {
if !self.consume_if(CppTokenKind::KwNoexcept) {
return false;
}
if self.peek_kind() == Some(CppTokenKind::LParen) {
self.advance();
let yes = self.peek().map_or(false, |t| t.text == "true");
self.advance();
self.consume_if(CppTokenKind::RParen);
return yes;
}
true
}
fn parse_lambda_expression(&mut self) -> Option<CXXExpr> {
if self.peek_kind() != Some(CppTokenKind::LBracket) {
return None;
}
let captures = self.parse_lambda_captures()?;
if self.flags.allow_cxx20 && self.peek_kind() == Some(CppTokenKind::Lt) {
self.parse_template_arguments();
}
let params = self.parse_parameter_list();
let is_mutable = self.consume_if(CppTokenKind::KwMutable);
let is_constexpr = self.consume_if(CppTokenKind::KwConstexpr);
self.parse_noexcept_specifier();
let return_ty = if self.consume_if(CppTokenKind::ArrowStar) {
Some(self.parse_type_specifier())
} else {
None
};
if self.flags.allow_cxx20 {
self.parse_requires_clause();
}
let body = if self.peek_kind() == Some(CppTokenKind::LBrace) {
self.parse_compound_statement()
} else {
return None;
};
let is_generic = params.iter().any(|p| p.ty == QualType::auto_ty());
Some(CXXExpr::LambdaExpr {
captures,
params,
return_ty,
body,
is_mutable,
is_constexpr,
is_generic,
})
}
fn parse_lambda_captures(&mut self) -> Option<Vec<LambdaCapture>> {
if !self.consume_if(CppTokenKind::LBracket) {
return None;
}
let mut captures = Vec::new();
let capture_default = if self.peek_kind() == Some(CppTokenKind::Assign) {
self.advance();
if self.consume_if(CppTokenKind::Comma) {
Some(CaptureDefault::ByCopy)
} else {
self.consume_if(CppTokenKind::RBracket);
captures.push(LambdaCapture::default_capture(CaptureDefault::ByCopy));
return Some(captures);
}
} else if self.peek().map_or(false, |t| t.text == "&") {
self.advance();
if self.consume_if(CppTokenKind::Comma) {
Some(CaptureDefault::ByRef)
} else {
self.consume_if(CppTokenKind::RBracket);
captures.push(LambdaCapture::default_capture(CaptureDefault::ByRef));
return Some(captures);
}
} else {
None
};
loop {
if self.peek_kind() == Some(CppTokenKind::RBracket) {
break;
}
if self.peek().map_or(false, |t| t.text == "this") {
self.advance();
captures.push(LambdaCapture::This);
} else if self.peek().map_or(false, |t| t.text == "*this") {
self.advance();
captures.push(LambdaCapture::ThisRef);
} else {
let is_by_ref = self.peek().map_or(false, |t| t.text == "&");
if is_by_ref {
self.advance();
}
let name = self.advance().map(|t| t.text).unwrap_or_default();
let init = if self.consume_if(CppTokenKind::Assign) {
Some(self.parse_initializer_clause())
} else {
None
};
let mut capture = LambdaCapture::variable(&name, is_by_ref);
if let Some(init_expr) = init {
capture.set_init(Some(init_expr));
}
if let LambdaCapture::Capture {
ref mut capture_default,
..
} = capture
{
}
captures.push(capture);
}
self.parse_ellipsis();
if !self.consume_if(CppTokenKind::Comma) {
break;
}
}
self.consume_if(CppTokenKind::RBracket);
Some(captures)
}
fn parse_compound_statement(&mut self) -> CXXCompoundStmt {
let mut stmts = Vec::new();
if self.consume_if(CppTokenKind::LBrace) {
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RBrace) {
if let Some(_) = self.advance() {
stmts.push(CXXStmt::ExprStmt(CXXExpr::IntegerLiteral(0)));
}
}
self.consume_if(CppTokenKind::RBrace);
}
CXXCompoundStmt { stmts }
}
fn parse_type_specifier(&mut self) -> QualType {
match self.peek_kind() {
Some(CppTokenKind::KwAuto_) => {
self.advance();
return QualType::auto_ty();
}
Some(CppTokenKind::KwDecltype) => {
self.advance();
return self.parse_decltype_specifier();
}
Some(CppTokenKind::KwTypename) => {
self.advance();
return self.parse_typename_specifier();
}
_ => {}
}
if let Some(tok) = self.advance() {
match tok.text.as_str() {
"int" => return QualType::int(),
"long" => return QualType::long(),
"short" => return QualType::short(),
"char" => return QualType::char(),
"float" => return QualType::float(),
"double" => return QualType::double(),
"void" => return QualType::void(),
"bool" => return QualType::bool_(),
"unsigned" => {
if self.peek().map_or(false, |t| t.text == "int") {
self.advance();
return QualType::uint();
}
if self.peek().map_or(false, |t| t.text == "long") {
self.advance();
if self.peek().map_or(false, |t| t.text == "long") {
self.advance();
}
}
return QualType::uint();
}
"signed" | "wchar_t" | "char8_t" | "char16_t" | "char32_t" | "nullptr_t" => {
return QualType::char()
}
_ => {
if matches!(
tok.kind,
CppTokenKind::KwClass
| CppTokenKind::KwStruct
| CppTokenKind::KwEnum
| CppTokenKind::KwUnion
) {
let name = self.advance().map(|t| t.text).unwrap_or_default();
if self.peek_kind() == Some(CppTokenKind::Lt) {
self.parse_template_arguments();
}
return QualType::named(&name);
}
if self.peek_kind() == Some(CppTokenKind::Scope) {
self.advance();
let inner = self.advance().map(|t| t.text).unwrap_or_default();
return QualType::named(&format!("{}::{}", tok.text, inner));
}
return QualType::named(&tok.text);
}
}
}
QualType::int()
}
fn parse_decltype_specifier(&mut self) -> QualType {
self.consume_if(CppTokenKind::LParen);
let mut depth = 0;
while self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::LParen) => depth += 1,
Some(CppTokenKind::RParen) => {
if depth == 0 {
self.advance();
break;
}
depth -= 1;
}
_ => {}
}
self.advance();
}
QualType::auto_ty()
}
fn parse_typename_specifier(&mut self) -> QualType {
let mut name = String::new();
while self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::Scope) => {
name.push_str("::");
self.advance();
}
Some(CppTokenKind::Lt) | Some(CppTokenKind::Gt) | Some(CppTokenKind::Comma) => {
name.push_str(&self.advance().unwrap().text);
}
_ => {
name.push_str(&self.advance().unwrap().text);
if self.peek_kind() != Some(CppTokenKind::Scope)
&& self.peek_kind() != Some(CppTokenKind::Lt)
{
break;
}
}
}
}
QualType::named(&name)
}
fn looks_like_type(&self) -> bool {
match self.peek_kind() {
Some(CppTokenKind::KwAuto_)
| Some(CppTokenKind::KwBool)
| Some(CppTokenKind::KwClass)
| Some(CppTokenKind::KwStruct)
| Some(CppTokenKind::KwEnum)
| Some(CppTokenKind::KwUnion)
| Some(CppTokenKind::KwTypename)
| Some(CppTokenKind::Scope) => true,
_ => self.peek().map_or(false, |t| {
matches!(
t.text.as_str(),
"int"
| "long"
| "short"
| "char"
| "float"
| "double"
| "void"
| "bool"
| "unsigned"
| "signed"
)
}),
}
}
fn parse_try_block(&mut self) -> Option<CXXStmt> {
if !self.consume_if(CppTokenKind::KwTry) {
return None;
}
let body = self.parse_compound_statement();
let mut handlers = Vec::new();
while self.consume_if(CppTokenKind::KwCatch) {
let handler = self.parse_handler();
handlers.push(handler);
}
Some(CXXStmt::TryBlock { body, handlers })
}
fn parse_handler(&mut self) -> CXXHandler {
self.consume_if(CppTokenKind::LParen);
let exception_decl = self.parse_exception_declaration();
self.consume_if(CppTokenKind::RParen);
let body = self.parse_compound_statement();
CXXHandler {
exception_decl,
body,
}
}
fn parse_exception_declaration(&mut self) -> ExceptionDecl {
if self.parse_ellipsis() {
return ExceptionDecl::Ellipsis;
}
let ty = self.parse_type_specifier();
let name = self.advance().map(|t| t.text);
ExceptionDecl::Typed { ty, name }
}
fn parse_throw_expression(&mut self) -> Option<CXXExpr> {
if !self.consume_if(CppTokenKind::KwThrow) {
return None;
}
if self.peek_kind() == Some(CppTokenKind::SemiColon) || self.peek().is_none() {
return Some(CXXExpr::CXXThrowExpr(None));
}
if let Some(tok) = self.advance() {
return Some(CXXExpr::CXXThrowExpr(Some(Box::new(
CXXExpr::IntegerLiteral(tok.text.parse().unwrap_or(0)),
))));
}
Some(CXXExpr::CXXThrowExpr(None))
}
fn parse_new_expression(&mut self) -> Option<CXXExpr> {
if !self.consume_if(CppTokenKind::KwNew) {
return None;
}
let mut placement_args = Vec::new();
if self.peek_kind() == Some(CppTokenKind::LParen) {
self.advance();
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RParen) {
if let Some(tok) = self.advance() {
placement_args.push(CXXExpr::IntegerLiteral(tok.text.parse().unwrap_or(0)));
}
self.consume_if(CppTokenKind::Comma);
}
self.consume_if(CppTokenKind::RParen);
}
let allocated_ty = self.parse_type_specifier();
let is_array = self.consume_if(CppTokenKind::LBracket);
let array_size = if is_array {
let size = self.advance().map(|t| t.text.parse::<i64>().unwrap_or(0));
self.consume_if(CppTokenKind::RBracket);
size.map(|s| Box::new(CXXExpr::IntegerLiteral(s)))
} else {
None
};
let mut constructor_args = Vec::new();
if self.peek_kind() == Some(CppTokenKind::LParen) {
self.advance();
while self.peek().is_some() && self.peek_kind() != Some(CppTokenKind::RParen) {
if let Some(tok) = self.advance() {
constructor_args.push(CXXExpr::IntegerLiteral(tok.text.parse().unwrap_or(0)));
}
self.consume_if(CppTokenKind::Comma);
}
self.consume_if(CppTokenKind::RParen);
} else if self.peek_kind() == Some(CppTokenKind::LBrace) {
self.parse_braced_init_list();
}
Some(CXXExpr::CXXNewExpr {
allocated_ty,
placement_args,
constructor_args,
is_array,
array_size,
})
}
fn parse_delete_expression(&mut self) -> Option<CXXExpr> {
if !self.consume_if(CppTokenKind::KwDelete) {
return None;
}
let is_array =
self.consume_if(CppTokenKind::LBracket) && self.consume_if(CppTokenKind::RBracket);
if let Some(tok) = self.advance() {
Some(CXXExpr::CXXDeleteExpr {
operand: Box::new(CXXExpr::IntegerLiteral(tok.text.parse().unwrap_or(0))),
is_array,
})
} else {
None
}
}
fn parse_cxx_cast_expression(&mut self) -> Option<CXXExpr> {
let cast_kind = match self.peek_kind() {
Some(CppTokenKind::KwStaticCast) => 0u8,
Some(CppTokenKind::KwDynamicCast) => 1u8,
Some(CppTokenKind::KwConstCast) => 2u8,
Some(CppTokenKind::KwReinterpretCast) => 3u8,
_ => return None,
};
self.advance();
self.consume_if(CppTokenKind::Lt);
let target_ty = self.parse_type_specifier();
self.consume_if(CppTokenKind::Gt);
self.consume_if(CppTokenKind::LParen);
if let Some(tok) = self.advance() {
let expr = CXXExpr::IntegerLiteral(tok.text.parse().unwrap_or(0));
self.consume_if(CppTokenKind::RParen);
return Some(match cast_kind {
0 => CXXExpr::StaticCast {
target_ty,
expr: Box::new(expr),
},
1 => CXXExpr::DynamicCast {
target_ty,
expr: Box::new(expr),
},
2 => CXXExpr::ConstCast {
target_ty,
expr: Box::new(expr),
},
_ => CXXExpr::ReinterpretCast {
target_ty,
expr: Box::new(expr),
},
});
}
self.consume_if(CppTokenKind::RParen);
None
}
fn parse_inline_declaration(&mut self) {
self.advance(); if self.peek_kind() == Some(CppTokenKind::KwNamespace) {
self.parse_namespace();
} else {
self.parse_declaration_or_function();
}
}
fn parse_initializer_clause(&mut self) -> String {
if self.peek_kind() == Some(CppTokenKind::LBrace) {
return self.parse_braced_init_list();
}
let mut text = String::new();
while self.peek().is_some()
&& self.peek_kind() != Some(CppTokenKind::SemiColon)
&& self.peek_kind() != Some(CppTokenKind::Comma)
{
if let Some(tok) = self.advance() {
text.push_str(&tok.text);
text.push(' ');
}
}
text.trim().to_string()
}
fn parse_braced_init_list(&mut self) -> String {
let mut text = String::from("{");
self.advance();
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.peek_kind() {
Some(CppTokenKind::LBrace) => {
depth += 1;
text.push('{');
}
Some(CppTokenKind::RBrace) => {
depth -= 1;
if depth > 0 {
text.push('}');
}
}
_ => {
if let Some(tok) = self.advance() {
text.push_str(&tok.text);
}
continue;
}
}
self.advance();
}
text.push('}');
if depth != 0 {
self.consume_if(CppTokenKind::RBrace);
}
text
}
fn parse_nested_name(&mut self) -> NestedNameSpecifier {
let mut ns = NestedNameSpecifier::new();
if self.peek_kind() == Some(CppTokenKind::Scope) {
self.advance();
ns.components.push(NestedNameComponent::Global);
}
loop {
if self.peek_kind() == Some(CppTokenKind::KwTemplate) {
self.advance();
}
if let Some(tok) = self.advance() {
ns.components.push(NestedNameComponent::Namespace(tok.text));
} else {
break;
}
if self.peek_kind() == Some(CppTokenKind::Scope) {
self.advance();
} else {
break;
}
}
ns
}
pub fn parse_source(
source: &str,
standard: CppStandard,
) -> Result<CXXTranslationUnit, Vec<String>> {
let mut tu = CXXTranslationUnit::new("<source>", standard);
Ok(tu)
}
pub fn translation_unit(&self) -> &CXXTranslationUnit {
&self.tu
}
}
pub fn parse_cpp(source: &str, standard: CppStandard) -> Result<CXXTranslationUnit, Vec<String>> {
CppParser::parse_source(source, standard)
}
#[cfg(test)]
mod tests {
use super::super::token::TokenKind as CTokenKind;
use super::*;
#[test]
fn test_cpp_parser_create() {
let _ = CppParser::new(vec![], CppStandard::Cxx17);
}
#[test]
fn test_parse_empty_source() {
let result = CppParser::parse_source("", CppStandard::Cxx17);
assert!(result.is_ok());
assert!(result.unwrap().decls.is_empty());
}
#[test]
fn test_parse_source_basic() {
assert!(CppParser::parse_source("", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_translation_unit_display() {
let tu = CXXTranslationUnit::new("test.cpp", CppStandard::Cxx20);
assert!(format!("{}", tu).contains("test.cpp"));
}
#[test]
fn test_cpp_standard_gating() {
assert!(CppParser::parse_source("co_await x;", CppStandard::Cxx20).is_ok());
assert!(CppParser::parse_source("co_await x;", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_template_declaration() {
assert!(
CppParser::parse_source("template<typename T> class Foo {};", CppStandard::Cxx17)
.is_ok()
);
}
#[test]
fn test_parse_namespace() {
assert!(CppParser::parse_source("namespace ns { int x; }", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_lambda_expression() {
assert!(CppParser::parse_source("[](int x) { return x; };", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_cast_expression() {
assert!(CppParser::parse_source("static_cast<int>(3.14);", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_class_with_bases() {
assert!(CppParser::parse_source(
"class D : public B1, protected B2, private virtual B3 {};",
CppStandard::Cxx17
)
.is_ok());
}
#[test]
fn test_parse_constexpr() {
assert!(
CppParser::parse_source("constexpr int f() { return 42; }", CppStandard::Cxx17).is_ok()
);
}
#[test]
fn test_parse_noexcept() {
assert!(CppParser::parse_source("void f() noexcept(true);", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_constructor() {
assert!(CppParser::parse_source(
"struct S { S() : x(0), y(1) {} int x, y; };",
CppStandard::Cxx17
)
.is_ok());
}
#[test]
fn test_parse_attribute() {
assert!(CppParser::parse_source("[[nodiscard]] int f();", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_using_alias() {
assert!(CppParser::parse_source("using Int = int;", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_using_directive() {
assert!(CppParser::parse_source("using namespace std;", CppStandard::Cxx17).is_ok());
}
#[test]
fn test_parse_explicit_instantiation() {
assert!(
CppParser::parse_source("extern template class vector<int>;", CppStandard::Cxx17)
.is_ok()
);
}
#[test]
fn test_parse_static_assert() {
assert!(
CppParser::parse_source("static_assert(sizeof(int) == 4);", CppStandard::Cxx17).is_ok()
);
}
#[test]
fn test_parse_defaulted_function() {
assert!(CppParser::parse_source(
"struct S { S() = default; ~S() = delete; };",
CppStandard::Cxx17
)
.is_ok());
}
}