use chumsky::prelude::*;
use macro_rules_attribute::apply;
use crate::{ast::*, context::State, span::*, utils::*};
pub mod parser_utils {
use super::*;
pub type Token = BalancedToken;
pub type TokenStream = BalancedTokenSequence;
pub type Error<'a> = Rich<'a, BalancedToken, Span>;
pub type Extra<'a> = chumsky::extra::Full<Error<'a>, State, Context>;
#[derive(Default, Clone, Copy)]
pub struct Context {
pub no_recover: bool,
}
}
use parser_utils::*;
pub fn primary_expression<'a>() -> impl Parser<'a, Tokens<'a>, PrimaryExpression, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
generic_selection().map(PrimaryExpression::Generic),
constant().map(PrimaryExpression::Constant),
enumeration_constant().map(PrimaryExpression::EnumerationConstant),
identifier().map(PrimaryExpression::Identifier),
string_literal().map(PrimaryExpression::StringLiteral),
quoted_string().map(PrimaryExpression::QuotedString),
expression()
.parenthesized()
.map(Box::new)
.map(PrimaryExpression::Parenthesized)
.recover_with(recover_parenthesized(PrimaryExpression::Error)),
))
.labelled("primiary expression")
.as_context()
}
pub fn enumeration_constant<'a>() -> impl Parser<'a, Tokens<'a>, Identifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
identifier().try_map_with(|name, extra| {
if extra.state().ctx().is_enum_constant(&name) {
Ok(name)
} else {
Err(expected_found(
["enumeration constant"],
Some(Token::Identifier(name)),
extra.span(),
))
}
}),
))
.labelled("enumeration constant")
.as_context()
}
pub fn generic_selection<'a>() -> impl Parser<'a, Tokens<'a>, GenericSelection, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("_Generic")
.ignore_then(
assignment_expression() .map(Brand::into_inner)
.map(Box::new)
.then_ignore(punctuator(Punctuator::Comma))
.then(generic_association_list())
.parenthesized(), )
.map(|(controlling_expression, associations)| GenericSelection { controlling_expression, associations }),
))
.labelled("generic selection")
.as_context()
}
pub fn generic_association_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<GenericAssociation>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
generic_association()
.separated_by(punctuator(Punctuator::Comma))
.at_least(1)
.collect::<Vec<GenericAssociation>>(),
))
.labelled("generic association list")
.as_context()
}
pub fn generic_association<'a>() -> impl Parser<'a, Tokens<'a>, GenericAssociation, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("default")
.ignore_then(punctuator(Punctuator::Colon))
.ignore_then(assignment_expression().map(Brand::into_inner).map(Box::new))
.map(|expression| GenericAssociation::Default { expression }),
type_name()
.then_ignore(punctuator(Punctuator::Colon))
.then(assignment_expression().map(Brand::into_inner).map(Box::new))
.map(|(type_name, expression)| GenericAssociation::Type { type_name, expression }),
))
.labelled("generic association")
.as_context()
}
#[apply(cached)]
pub fn postfix_expression<'a>() -> impl Parser<'a, Tokens<'a>, PostfixExpression, Extra<'a>> + Clone {
let increment = punctuator(Punctuator::Increment);
let decrement = punctuator(Punctuator::Decrement);
let array = allow_recover(expression().bracketed().recover_with(recover_bracketed_with(|span| {
Expression::new(ExpressionKind::Error, span)
})));
let function = assignment_expression()
.map(Brand::into_inner)
.separated_by(punctuator(Punctuator::Comma))
.collect::<Vec<Expression>>()
.parenthesized(); let member_access = punctuator(Punctuator::Dot).ignore_then(identifier());
let member_access_ptr = punctuator(Punctuator::Arrow).ignore_then(identifier());
type PostfixFn = Box<dyn FnOnce(PostfixExpression) -> PostfixExpression>;
let postfix = primary_expression().map(PostfixExpression::Primary).foldl(
choice((
increment.map(|_| -> PostfixFn { Box::new(|expr| PostfixExpression::PostIncrement(Box::new(expr))) }),
decrement.map(|_| -> PostfixFn { Box::new(|expr| PostfixExpression::PostDecrement(Box::new(expr))) }),
array.map(|idx| -> PostfixFn {
Box::new(move |expr| PostfixExpression::ArrayAccess {
array: Box::new(expr),
index: Box::new(idx),
})
}),
function.map(|arguments| -> PostfixFn {
Box::new(move |expr| PostfixExpression::FunctionCall { function: Box::new(expr), arguments })
}),
member_access.map(|member| -> PostfixFn {
Box::new(move |expr| PostfixExpression::MemberAccess { object: Box::new(expr), member })
}),
member_access_ptr.map(|member| -> PostfixFn {
Box::new(move |expr| PostfixExpression::MemberAccessPtr { object: Box::new(expr), member })
}),
))
.repeated(),
|acc, f| f(acc),
);
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
compound_literal().map(PostfixExpression::CompoundLiteral),
postfix,
))
.labelled("postfix expression")
.as_context()
}
pub fn compound_literal<'a>() -> impl Parser<'a, Tokens<'a>, CompoundLiteral, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
storage_class_specifiers()
.then(type_name())
.parenthesized() .then(braced_initializer())
.map(|((storage_class_specifiers, type_name), initializer)| CompoundLiteral {
storage_class_specifiers,
type_name,
initializer,
}),
))
.labelled("compound literal")
.as_context()
}
pub fn storage_class_specifiers<'a>() -> impl Parser<'a, Tokens<'a>, Vec<StorageClassSpecifier>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
storage_class_specifier()
.repeated()
.collect::<Vec<StorageClassSpecifier>>(),
))
.labelled("storage class specifiers")
.as_context()
}
#[apply(cached)]
pub fn unary_expression<'a>() -> impl Parser<'a, Tokens<'a>, UnaryExpression, Extra<'a>> + Clone {
let pre_increment = punctuator(Punctuator::Increment)
.ignore_then(unary_expression())
.map(Box::new);
let pre_decrement = punctuator(Punctuator::Decrement)
.ignore_then(unary_expression())
.map(Box::new);
let unary_operator = select_ref! {
Token::Punctuator(Punctuator::Ampersand) => UnaryOperator::Address,
Token::Punctuator(Punctuator::Star) => UnaryOperator::Dereference,
Token::Punctuator(Punctuator::Plus) => UnaryOperator::Plus,
Token::Punctuator(Punctuator::Minus) => UnaryOperator::Minus,
Token::Punctuator(Punctuator::Bang) => UnaryOperator::LogicalNot,
Token::Punctuator(Punctuator::Tilde) => UnaryOperator::BitwiseNot,
};
let unary = unary_operator.then(cast_expression());
let sizeof_expr = keyword("sizeof").ignore_then(unary_expression()).map(Box::new);
let type_name = type_name()
.parenthesized()
.recover_with(recover_parenthesized(TypeName::Error));
let sizeof_type = keyword("sizeof").ignore_then(type_name.clone());
let alignof_type = keyword("alignof").or(keyword("_Alignof")).ignore_then(type_name);
let postfix = postfix_expression();
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
pre_increment.map(UnaryExpression::PreIncrement),
pre_decrement.map(UnaryExpression::PreDecrement),
unary.map(|(operator, operand)| UnaryExpression::Unary { operator, operand: Box::new(operand) }),
no_recover(sizeof_type.clone()).map(UnaryExpression::SizeofType), sizeof_expr.map(UnaryExpression::Sizeof),
sizeof_type.map(UnaryExpression::SizeofType),
alignof_type.map(UnaryExpression::Alignof),
postfix.map(UnaryExpression::Postfix),
))
.labelled("unary expression")
.as_context()
}
#[apply(cached)]
pub fn cast_expression<'a>() -> impl Parser<'a, Tokens<'a>, CastExpression, Extra<'a>> + Clone {
let cast = type_name()
.parenthesized()
.recover_with(recover_parenthesized(TypeName::Error))
.then(allow_recover(cast_expression().map(Box::new)))
.map(|(type_name, expression)| CastExpression::Cast { type_name, expression });
let unary = unary_expression().map(CastExpression::Unary);
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
no_recover(cast.clone()),
no_recover(unary.clone()),
cast,
unary,
))
}
pub fn binary_expression<'a>() -> impl Parser<'a, Tokens<'a>, Brand<Expression, BinaryExpression>, Extra<'a>> + Clone {
use chumsky::pratt::*;
macro_rules! op {
($punct:expr) => {{
use Punctuator::*;
punctuator($punct)
}};
}
macro_rules! binary {
($op:expr) => {
|left, _, right, extra: &mut _| {
use BinaryOperator::*;
Expression::new(
ExpressionKind::Binary(BinaryExpression {
operator: $op,
left: Box::new(left),
right: Box::new(right),
}),
extra.span(),
)
}
};
}
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
choice((
cast_expression().map_with(|c, e| Expression::new(ExpressionKind::Cast(c), e.span())),
unary_expression().map_with(|u, e| Expression::new(ExpressionKind::Unary(u), e.span())),
postfix_expression().map_with(|p, e| Expression::new(ExpressionKind::Postfix(p), e.span())),
))
.pratt((
infix(left(1000 - 30), op!(Star), binary!(Multiply)),
infix(left(1000 - 30), op!(Slash), binary!(Divide)),
infix(left(1000 - 30), op!(Percent), binary!(Modulo)),
infix(left(1000 - 40), op!(Plus), binary!(Add)),
infix(left(1000 - 40), op!(Minus), binary!(Subtract)),
infix(left(1000 - 50), op!(LeftShift), binary!(LeftShift)),
infix(left(1000 - 50), op!(RightShift), binary!(RightShift)),
infix(left(1000 - 60), op!(Less), binary!(Less)),
infix(left(1000 - 60), op!(LessEqual), binary!(LessEqual)),
infix(left(1000 - 60), op!(Greater), binary!(Greater)),
infix(left(1000 - 60), op!(GreaterEqual), binary!(GreaterEqual)),
infix(left(1000 - 70), op!(Equal), binary!(Equal)),
infix(left(1000 - 70), op!(NotEqual), binary!(NotEqual)),
infix(left(1000 - 80), op!(Ampersand), binary!(BitwiseAnd)),
infix(left(1000 - 90), op!(Caret), binary!(BitwiseXor)),
infix(left(1000 - 100), op!(Pipe), binary!(BitwiseOr)),
infix(left(1000 - 110), op!(LogicalAnd), binary!(LogicalAnd)),
infix(left(1000 - 120), op!(LogicalOr), binary!(LogicalOr)),
)),
))
.map(Brand::new)
.labelled("binary expression")
.as_context()
}
#[apply(cached)]
pub fn conditional_expression<'a>()
-> impl Parser<'a, Tokens<'a>, Brand<Expression, ConditionalExpression>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
binary_expression()
.then_ignore(punctuator(Punctuator::Question))
.then(expression())
.then_ignore(punctuator(Punctuator::Colon))
.then(conditional_expression().map(Brand::into_inner))
.map_with(|((condition, then_expr), else_expr), extra| {
Expression::new(
ExpressionKind::Conditional(ConditionalExpression {
condition: Box::new(condition.into_inner()),
then_expr: Box::new(then_expr),
else_expr: Box::new(else_expr),
}),
extra.span(),
)
}),
binary_expression().map(Brand::into_inner),
))
.map(Brand::new)
.labelled("conditional expression")
.as_context()
}
#[apply(cached)]
pub fn assignment_expression<'a>()
-> impl Parser<'a, Tokens<'a>, Brand<Expression, AssignmentExpression>, Extra<'a>> + Clone {
let assigment_opeartor = select_ref! {
Token::Punctuator(Punctuator::Assign) => AssignmentOperator::Assign,
Token::Punctuator(Punctuator::AddAssign) => AssignmentOperator::AddAssign,
Token::Punctuator(Punctuator::SubAssign) => AssignmentOperator::SubAssign,
Token::Punctuator(Punctuator::MulAssign) => AssignmentOperator::MulAssign,
Token::Punctuator(Punctuator::DivAssign) => AssignmentOperator::DivAssign,
Token::Punctuator(Punctuator::ModAssign) => AssignmentOperator::ModAssign,
Token::Punctuator(Punctuator::AndAssign) => AssignmentOperator::AndAssign,
Token::Punctuator(Punctuator::OrAssign) => AssignmentOperator::OrAssign,
Token::Punctuator(Punctuator::XorAssign) => AssignmentOperator::XorAssign,
Token::Punctuator(Punctuator::LeftShiftAssign) => AssignmentOperator::LeftShiftAssign,
Token::Punctuator(Punctuator::RightShiftAssign) => AssignmentOperator::RightShiftAssign,
};
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
unary_expression()
.map_with(|u, e| Expression::new(ExpressionKind::Unary(u), e.span()))
.then(assigment_opeartor)
.then(assignment_expression().map(Brand::into_inner))
.map_with(|((left, operator), right), extra| {
Expression::new(
ExpressionKind::Assignment(AssignmentExpression {
operator,
left: Box::new(left),
right: Box::new(right),
}),
extra.span(),
)
}),
conditional_expression().map(Brand::into_inner),
))
.map(Brand::new)
.labelled("assignment expression")
.as_context()
}
#[apply(cached)]
pub fn expression<'a>() -> impl Parser<'a, Tokens<'a>, Expression, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
assignment_expression()
.map(Brand::into_inner)
.separated_by(punctuator(Punctuator::Comma))
.at_least(1)
.collect::<Vec<Expression>>()
.map_with(|expressions, extra| {
if expressions.len() == 1 {
expressions.into_iter().next().unwrap()
} else {
Expression::new(ExpressionKind::Comma(CommaExpression { expressions }), extra.span())
}
}),
))
.labelled("expression")
.as_context()
}
#[apply(cached)]
pub fn constant_expression<'a>() -> impl Parser<'a, Tokens<'a>, ConstantExpression, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
conditional_expression()
.map(Brand::into_inner)
.map(Box::new)
.map(ConstantExpression::Expression),
))
.labelled("constant expression")
.as_context()
}
#[apply(cached)]
pub fn declaration<'a>() -> impl Parser<'a, Tokens<'a>, Declaration, Extra<'a>> + Clone {
let normal = attribute_specifier_sequence()
.then(declaration_specifiers())
.then(init_declarator_list().or_not().map(Option::unwrap_or_default))
.then_ignore(punctuator(Punctuator::Semicolon))
.map_with(|((attributes, specifiers), declarators), extra| {
Declaration::new(
DeclarationKind::Normal { attributes, specifiers, declarators },
extra.span(),
)
});
let typedef = attribute_specifier_sequence()
.then(declaration_specifiers_with_typedef())
.then(typedef_declarator_list().or_not().map(Option::unwrap_or_default))
.then_ignore(punctuator(Punctuator::Semicolon))
.map_with(|((attributes, specifiers), declarators), extra| {
Declaration::new(
DeclarationKind::Typedef { attributes, specifiers, declarators },
extra.span(),
)
});
let static_assert = static_assert_declaration();
let attribute = choice((
old_fashioned_attribute_specifier()
.repeated()
.at_least(1)
.collect::<Vec<AttributeSpecifier>>(),
attribute_specifier_sequence().then_ignore(punctuator(Punctuator::Semicolon)),
));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
static_assert.map_with(|s, e| Declaration::new(DeclarationKind::StaticAssert(s), e.span())),
normal,
typedef,
attribute.map_with(|a, e| Declaration::new(DeclarationKind::Attribute(a), e.span())),
))
.labelled("declaration")
.as_context()
}
pub fn declaration_specifiers<'a>() -> impl Parser<'a, Tokens<'a>, DeclarationSpecifiers, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
declaration_specifier()
.repeated()
.at_least(1)
.collect::<Vec<DeclarationSpecifier>>()
.then(attribute_specifier_sequence())
.map(|(specifiers, attributes)| DeclarationSpecifiers { specifiers, attributes }),
))
.labelled("declaration specifiers")
.as_context()
}
pub fn declaration_specifiers_with_typedef<'a>() -> impl Parser<'a, Tokens<'a>, DeclarationSpecifiers, Extra<'a>> + Clone
{
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
declaration_specifier()
.or(keyword("typedef").to(DeclarationSpecifier::StorageClass(StorageClassSpecifier::Typedef)))
.repeated()
.at_least(1)
.collect::<Vec<DeclarationSpecifier>>()
.then(attribute_specifier_sequence())
.map(|(specifiers, attributes)| DeclarationSpecifiers { specifiers, attributes }),
))
.labelled("declaration specifiers")
.as_context()
}
#[apply(cached)]
pub fn declaration_specifier<'a>() -> impl Parser<'a, Tokens<'a>, DeclarationSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
storage_class_specifier().map(DeclarationSpecifier::StorageClass),
type_specifier_qualifier().map(DeclarationSpecifier::TypeSpecifierQualifier),
function_specifier().map(DeclarationSpecifier::Function),
))
.labelled("declaration specifier")
.as_context()
}
pub fn init_declarator_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<InitDeclarator>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
init_declarator()
.separated_by(punctuator(Punctuator::Comma))
.at_least(1)
.collect::<Vec<InitDeclarator>>(),
))
.labelled("init declarator list")
.as_context()
}
pub fn init_declarator<'a>() -> impl Parser<'a, Tokens<'a>, InitDeclarator, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
declarator()
.then(punctuator(Punctuator::Assign).ignore_then(initializer()).or_not())
.map(|(declarator, initializer)| InitDeclarator { declarator, initializer }),
))
.labelled("init declarator")
.as_context()
}
pub fn typedef_declarator_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<Declarator>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
typedef_declarator()
.separated_by(punctuator(Punctuator::Comma))
.at_least(1)
.collect::<Vec<Declarator>>(),
))
.labelled("init declarator list")
.as_context()
}
pub fn typedef_declarator<'a>() -> impl Parser<'a, Tokens<'a>, Declarator, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
declarator().map_with(move |declarator, extra| {
if let Some(ident) = declarator.identifier() {
extra.state().ctx_mut().add_typedef_name(ident.clone());
}
declarator
}),
))
.labelled("init declarator")
.as_context()
}
#[apply(cached)]
pub fn storage_class_specifier<'a>() -> impl Parser<'a, Tokens<'a>, StorageClassSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("auto").to(StorageClassSpecifier::Auto),
keyword("constexpr").to(StorageClassSpecifier::Constexpr),
keyword("extern").to(StorageClassSpecifier::Extern),
keyword("register").to(StorageClassSpecifier::Register),
keyword("static").to(StorageClassSpecifier::Static),
keyword("thread_local").to(StorageClassSpecifier::ThreadLocal),
))
.labelled("storage class specifier")
.as_context()
}
pub fn type_specifier<'a>() -> impl Parser<'a, Tokens<'a>, TypeSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("void").to(TypeSpecifier::Void),
keyword("char").to(TypeSpecifier::Char),
keyword("short").to(TypeSpecifier::Short),
keyword("int").to(TypeSpecifier::Int),
keyword("long").to(TypeSpecifier::Long),
keyword("float").to(TypeSpecifier::Float),
keyword("double").to(TypeSpecifier::Double),
keyword("signed").to(TypeSpecifier::Signed),
keyword("unsigned").to(TypeSpecifier::Unsigned),
keyword("bool").or(keyword("_Bool")).to(TypeSpecifier::Bool),
keyword("_Complex").to(TypeSpecifier::Complex),
keyword("_Decimal32").to(TypeSpecifier::Decimal32),
keyword("_Decimal64").to(TypeSpecifier::Decimal64),
keyword("_Decimal128").to(TypeSpecifier::Decimal128),
keyword("_BitInt")
.ignore_then(
constant_expression()
.parenthesized()
.recover_with(recover_parenthesized(ConstantExpression::Error)),
)
.map(TypeSpecifier::BitInt),
atomic_type_specifier().map(TypeSpecifier::Atomic),
struct_or_union_specifier().map(TypeSpecifier::Struct),
enum_specifier().map(TypeSpecifier::Enum),
typeof_specifier().map(TypeSpecifier::Typeof),
typedef_name().map(TypeSpecifier::TypedefName), ))
.labelled("type specifier")
.as_context()
}
pub fn struct_or_union_specifier<'a>() -> impl Parser<'a, Tokens<'a>, StructOrUnionSpecifier, Extra<'a>> + Clone {
let struct_or_union = choice((
keyword("struct").to(StructOrUnion::Struct),
keyword("union").to(StructOrUnion::Union),
));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
struct_or_union
.then(attribute_specifier_sequence())
.then(identifier().or_not())
.then(member_declaration_list().braced().or_not())
.map(|(((kind, attributes), identifier), members)| StructOrUnionSpecifier {
kind,
attributes,
identifier,
members,
}),
))
.labelled("struct or union specifier")
.as_context()
}
pub fn member_declaration_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<MemberDeclaration>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
member_declaration().repeated().collect::<Vec<MemberDeclaration>>(),
))
.labelled("member declaration list")
.as_context()
}
pub fn member_declaration<'a>() -> impl Parser<'a, Tokens<'a>, MemberDeclaration, Extra<'a>> + Clone {
let static_assert = static_assert_declaration().map(MemberDeclaration::StaticAssert);
let normal = attribute_specifier_sequence()
.then(specifier_qualifier_list())
.then(member_declarator_list().or_not().map(Option::unwrap_or_default))
.then_ignore(punctuator(Punctuator::Semicolon))
.map(|((attributes, specifiers), declarators)| MemberDeclaration::Normal {
attributes,
specifiers,
declarators,
})
.recover_with(skip_until(any().ignored(), punctuator(Punctuator::Semicolon), || {
MemberDeclaration::Error
}));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
static_assert,
normal,
))
.labelled("member declaration")
.as_context()
}
#[apply(cached)]
pub fn specifier_qualifier_list<'a>() -> impl Parser<'a, Tokens<'a>, SpecifierQualifierList, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
type_specifier_qualifier()
.repeated()
.at_least(1)
.collect::<Vec<TypeSpecifierQualifier>>()
.then(attribute_specifier_sequence())
.map(|(items, attributes)| SpecifierQualifierList { items, attributes }),
))
.labelled("specifier qualifier list")
.as_context()
}
#[apply(cached)]
pub fn type_specifier_qualifier<'a>() -> impl Parser<'a, Tokens<'a>, TypeSpecifierQualifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
type_specifier().map(TypeSpecifierQualifier::TypeSpecifier),
type_qualifier().map(TypeSpecifierQualifier::TypeQualifier),
alignment_specifier().map(TypeSpecifierQualifier::AlignmentSpecifier),
))
.labelled("type specifier qualifier")
.as_context()
}
pub fn member_declarator_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<MemberDeclarator>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
member_declarator()
.separated_by(punctuator(Punctuator::Comma))
.at_least(1)
.collect::<Vec<MemberDeclarator>>(),
))
.labelled("member declarator list")
.as_context()
}
pub fn member_declarator<'a>() -> impl Parser<'a, Tokens<'a>, MemberDeclarator, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
declarator()
.or_not()
.then_ignore(punctuator(Punctuator::Colon))
.then(constant_expression())
.map(|(declarator, width)| MemberDeclarator::BitField { declarator, width }),
declarator().map(MemberDeclarator::Declarator),
))
.labelled("member declarator")
.as_context()
}
pub fn enum_specifier<'a>() -> impl Parser<'a, Tokens<'a>, EnumSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("enum")
.ignore_then(attribute_specifier_sequence())
.then(identifier().or_not())
.then(
punctuator(Punctuator::Colon)
.ignore_then(specifier_qualifier_list())
.or_not(),
)
.then(enumerator_list().braced().or_not())
.map(
|(((attributes, identifier), type_specifier), enumerators)| EnumSpecifier {
attributes,
identifier,
type_specifier,
enumerators,
},
),
))
.labelled("enum specifier")
.as_context()
}
pub fn enumerator_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<Enumerator>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
enumerator()
.map_with(|enumerator, extra| {
extra.state().ctx_mut().add_enum_constant(enumerator.name.clone());
enumerator
})
.separated_by(punctuator(Punctuator::Comma))
.allow_trailing()
.collect::<Vec<Enumerator>>(),
))
.labelled("enumerator list")
.as_context()
}
pub fn enumerator<'a>() -> impl Parser<'a, Tokens<'a>, Enumerator, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
identifier()
.then(attribute_specifier_sequence())
.then(
punctuator(Punctuator::Assign)
.ignore_then(constant_expression())
.or_not(),
)
.map(|((name, attributes), value)| Enumerator { name, attributes, value }),
))
.labelled("enumerator")
.as_context()
}
pub fn atomic_type_specifier<'a>() -> impl Parser<'a, Tokens<'a>, AtomicTypeSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("_Atomic")
.ignore_then(
type_name()
.parenthesized()
.recover_with(recover_parenthesized(TypeName::Error)),
)
.map(|type_name| AtomicTypeSpecifier { type_name }),
))
.labelled("atomic type specifier")
.as_context()
}
pub fn typeof_specifier<'a>() -> impl Parser<'a, Tokens<'a>, TypeofSpecifier, Extra<'a>> + Clone {
let typeof_arg = choice((
type_name().map(TypeofSpecifierArgument::TypeName),
expression().map(Box::new).map(TypeofSpecifierArgument::Expression),
))
.parenthesized()
.recover_with(recover_parenthesized(TypeofSpecifierArgument::Error));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("typeof")
.or(keyword("__typeof__"))
.ignore_then(typeof_arg.clone())
.map(TypeofSpecifier::Typeof),
keyword("typeof_unqual")
.ignore_then(typeof_arg)
.map(TypeofSpecifier::TypeofUnqual),
))
.labelled("typeof specifier")
.as_context()
}
#[apply(cached)]
pub fn type_qualifier<'a>() -> impl Parser<'a, Tokens<'a>, TypeQualifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("const").to(TypeQualifier::Const),
keyword("restrict").to(TypeQualifier::Restrict),
keyword("__restrict").to(TypeQualifier::Restrict),
keyword("volatile").to(TypeQualifier::Volatile),
keyword("_Atomic").to(TypeQualifier::Atomic),
keyword("_Nonnull").to(TypeQualifier::Nonnull),
keyword("_Nullable").to(TypeQualifier::Nullable),
keyword("_Thread_local").to(TypeQualifier::ThreadLocal),
))
.labelled("type qualifier")
.as_context()
}
pub fn function_specifier<'a>() -> impl Parser<'a, Tokens<'a>, FunctionSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("inline").to(FunctionSpecifier::Inline),
keyword("_Noreturn").to(FunctionSpecifier::Noreturn),
))
.labelled("function specifier")
.as_context()
}
pub fn alignment_specifier<'a>() -> impl Parser<'a, Tokens<'a>, AlignmentSpecifier, Extra<'a>> + Clone {
let expr = constant_expression()
.parenthesized()
.recover_with(recover_parenthesized(ConstantExpression::Error));
let typ = type_name()
.parenthesized()
.recover_with(recover_parenthesized(TypeName::Error));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("alignas").ignore_then(choice((
no_recover(typ.clone()).map(AlignmentSpecifier::Type),
expr.map(AlignmentSpecifier::Expression),
typ.map(AlignmentSpecifier::Type),
))),
))
.labelled("alignment specifier")
.as_context()
}
#[apply(cached)]
pub fn declarator<'a>() -> impl Parser<'a, Tokens<'a>, Declarator, Extra<'a>> + Clone {
let pointer = pointer()
.then(declarator().map(Box::new))
.map(|(pointer, declarator)| Declarator::Pointer { pointer, declarator });
let direct = direct_declarator().map(Declarator::Direct);
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
pointer,
direct,
))
.labelled("declarator")
.as_context()
}
#[apply(cached)]
pub fn direct_declarator<'a>() -> impl Parser<'a, Tokens<'a>, DirectDeclarator, Extra<'a>> + Clone {
let identifier_decl = identifier()
.then(attribute_specifier_sequence())
.map(|(identifier, attributes)| DirectDeclarator::Identifier { identifier, attributes });
let parenthesized = declarator()
.parenthesized()
.recover_with(recover_parenthesized(Declarator::Error))
.map(Box::new)
.map(DirectDeclarator::Parenthesized);
type DirectDeclaratorFn = Box<dyn FnOnce(DirectDeclarator) -> DirectDeclarator>;
let base = choice((identifier_decl, parenthesized));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
base.foldl(
choice((
array_declarator().then(attribute_specifier_sequence()).map(
|(array_declarator, attributes)| -> DirectDeclaratorFn {
Box::new(move |declarator| DirectDeclarator::Array {
declarator: Box::new(declarator),
attributes,
array_declarator,
})
},
),
parameter_type_list()
.parenthesized() .then(attribute_specifier_sequence())
.map(|(parameters, attributes)| -> DirectDeclaratorFn {
Box::new(move |declarator| DirectDeclarator::Function {
declarator: Box::new(declarator),
attributes,
parameters,
})
}),
))
.repeated(),
|acc, f| f(acc),
),
))
.labelled("direct declarator")
.as_context()
}
pub fn array_declarator<'a>() -> impl Parser<'a, Tokens<'a>, ArrayDeclarator, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
choice((
keyword("static")
.ignore_then(type_qualifier_list().or_not().map(Option::unwrap_or_default))
.then(assignment_expression().map(Brand::into_inner).map(Box::new))
.map(|(type_qualifiers, size)| ArrayDeclarator::Static { type_qualifiers, size }),
type_qualifier_list()
.then_ignore(keyword("static"))
.then(assignment_expression().map(Brand::into_inner).map(Box::new))
.map(|(type_qualifiers, size)| ArrayDeclarator::Static { type_qualifiers, size }),
type_qualifier_list()
.or_not()
.map(Option::unwrap_or_default)
.then_ignore(punctuator(Punctuator::Star))
.map(|type_qualifiers| ArrayDeclarator::VLA { type_qualifiers }),
type_qualifier_list()
.or_not()
.map(Option::unwrap_or_default)
.then(assignment_expression().map(Brand::into_inner).map(Box::new).or_not())
.map(|(type_qualifiers, size)| ArrayDeclarator::Normal { type_qualifiers, size }),
))
.bracketed()
.recover_with(recover_bracketed(ArrayDeclarator::Error)),
))
.labelled("array declarator")
.as_context()
}
#[apply(cached)]
pub fn pointer<'a>() -> impl Parser<'a, Tokens<'a>, Pointer, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
choice((
punctuator(Punctuator::Star).to(PointerOrBlock::Pointer),
punctuator(Punctuator::Caret).to(PointerOrBlock::Block),
))
.then(attribute_specifier_sequence())
.then(type_qualifier_list().or_not().map(Option::unwrap_or_default))
.map(|((pointer_or_block, attributes), type_qualifiers)| Pointer {
pointer_or_block,
attributes,
type_qualifiers,
}),
))
.labelled("pointer")
.as_context()
}
#[apply(cached)]
pub fn type_qualifier_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<TypeQualifier>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
type_qualifier().repeated().at_least(1).collect::<Vec<TypeQualifier>>(),
))
.labelled("type qualifier list")
.as_context()
}
#[apply(cached)]
pub fn parameter_type_list<'a>() -> impl Parser<'a, Tokens<'a>, ParameterTypeList, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
punctuator(Punctuator::Ellipsis).to(ParameterTypeList::OnlyVariadic),
parameter_declaration()
.separated_by(punctuator(Punctuator::Comma))
.collect::<Vec<ParameterDeclaration>>()
.then(
punctuator(Punctuator::Comma)
.ignore_then(punctuator(Punctuator::Ellipsis))
.or_not(),
)
.map(|(params, variadic)| {
if variadic.is_some() {
ParameterTypeList::Variadic(params)
} else {
ParameterTypeList::Parameters(params)
}
}),
))
.labelled("parameter type list")
.as_context()
}
pub fn parameter_declaration<'a>() -> impl Parser<'a, Tokens<'a>, ParameterDeclaration, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
attribute_specifier_sequence()
.then(declaration_specifiers())
.then(choice((
no_recover(declarator())
.map(ParameterDeclarationKind::Declarator)
.map(Some),
abstract_declarator().map(ParameterDeclarationKind::Abstract).or_not(),
)))
.map(|((attributes, specifiers), declarator)| ParameterDeclaration { attributes, specifiers, declarator }),
))
.labelled("parameter declaration")
.as_context()
}
#[apply(cached)]
pub fn type_name<'a>() -> impl Parser<'a, Tokens<'a>, TypeName, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
specifier_qualifier_list()
.then(abstract_declarator().or_not())
.map(|(specifiers, abstract_declarator)| TypeName::TypeName { specifiers, abstract_declarator }),
))
.labelled("type name")
.as_context()
}
#[apply(cached)]
pub fn abstract_declarator<'a>() -> impl Parser<'a, Tokens<'a>, AbstractDeclarator, Extra<'a>> + Clone {
let pointer = pointer()
.then(abstract_declarator().map(Box::new).or_not())
.map(|(pointer, abstract_declarator)| AbstractDeclarator::Pointer { pointer, abstract_declarator });
let direct = direct_abstract_declarator().map(AbstractDeclarator::Direct);
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
pointer,
direct,
))
.labelled("abstract declarator")
.as_context()
}
#[apply(cached)]
pub fn direct_abstract_declarator<'a>() -> impl Parser<'a, Tokens<'a>, DirectAbstractDeclarator, Extra<'a>> + Clone {
let parenthesized = abstract_declarator()
.recover_with(recover_parenthesized(AbstractDeclarator::Error))
.parenthesized()
.map(Box::new)
.map(DirectAbstractDeclarator::Parenthesized);
type DirectAbstractDeclaratorFn = Box<dyn FnOnce(Option<DirectAbstractDeclarator>) -> DirectAbstractDeclarator>;
let postfix = choice((
array_declarator().then(attribute_specifier_sequence()).map(
|(array_declarator, attributes)| -> DirectAbstractDeclaratorFn {
Box::new(move |declarator| {
let declarator = declarator.map(Box::new);
DirectAbstractDeclarator::Array { declarator, attributes, array_declarator }
})
},
),
parameter_type_list()
.parenthesized() .then(attribute_specifier_sequence())
.map(|(parameters, attributes)| -> DirectAbstractDeclaratorFn {
Box::new(move |declarator| {
let declarator = declarator.map(Box::new);
DirectAbstractDeclarator::Function { declarator, attributes, parameters }
})
}),
))
.repeated();
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
choice((
parenthesized.map(Some).foldl(postfix.clone(), |acc, f| Some(f(acc))),
empty().to(None).foldl(postfix.at_least(1), |acc, f| Some(f(acc))),
))
.unwrapped(),
))
.labelled("direct abstract declarator")
.as_context()
}
pub fn typedef_name<'a>() -> impl Parser<'a, Tokens<'a>, Identifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
identifier(),
))
.try_map_with(|name, extra| {
if extra.state().ctx().is_typedef_name(&name) {
Ok(name)
} else {
Err(expected_found(
["typedef name"],
Some(BalancedToken::Identifier(name)),
extra.span(),
))
}
})
.labelled("typedef name")
.as_context()
}
#[apply(cached)]
pub fn braced_initializer<'a>() -> impl Parser<'a, Tokens<'a>, BracedInitializer, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
designated_initializer()
.separated_by(punctuator(Punctuator::Comma))
.allow_trailing()
.collect::<Vec<DesignatedInitializer>>()
.braced()
.map(|initializers| BracedInitializer { initializers }),
))
.labelled("braced initializer")
.as_context()
}
#[apply(cached)]
pub fn initializer<'a>() -> impl Parser<'a, Tokens<'a>, Initializer, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
braced_initializer().map(Initializer::Braced),
assignment_expression()
.map(Brand::into_inner)
.map(Box::new)
.map(Initializer::Expression),
))
.labelled("initializer")
.as_context()
}
pub fn designated_initializer<'a>() -> impl Parser<'a, Tokens<'a>, DesignatedInitializer, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
designation()
.or_not()
.then(initializer())
.map(|(designation, initializer)| DesignatedInitializer { designation, initializer }),
))
.labelled("designated initializer")
.as_context()
}
pub fn designation<'a>() -> impl Parser<'a, Tokens<'a>, Designation, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
empty()
.to(None)
.foldl(designator().repeated().at_least(1), |designation, designator| {
Some(Designation {
designator,
designation: designation.map(Box::new),
})
})
.unwrapped()
.then_ignore(punctuator(Punctuator::Assign)),
))
.labelled("designation")
.as_context()
}
pub fn designator<'a>() -> impl Parser<'a, Tokens<'a>, Designator, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
constant_expression()
.bracketed()
.recover_with(recover_bracketed(ConstantExpression::Error))
.map(Designator::Array),
punctuator(Punctuator::Dot)
.ignore_then(identifier())
.map(Designator::Member),
))
.labelled("designator")
.as_context()
}
pub fn static_assert_declaration<'a>() -> impl Parser<'a, Tokens<'a>, StaticAssertDeclaration, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("static_assert")
.or(keyword("_Static_assert"))
.ignore_then(
constant_expression()
.then(punctuator(Punctuator::Comma).ignore_then(string_literal()).or_not())
.parenthesized(), )
.then_ignore(punctuator(Punctuator::Semicolon))
.map(|(condition, message)| StaticAssertDeclaration { condition, message }),
))
.labelled("static assert declaration")
.as_context()
}
#[apply(cached)]
pub fn statement<'a>() -> impl Parser<'a, Tokens<'a>, Statement, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
labelled_statement().map_with(|l, e| Statement::new(StatementKind::Labeled(l), e.span())),
unlabeled_statement().map_with(|u, e| Statement::new(StatementKind::Unlabeled(u), e.span())),
))
.labelled("statement")
.as_context()
}
#[apply(cached)]
pub fn unlabeled_statement<'a>() -> impl Parser<'a, Tokens<'a>, UnlabeledStatement, Extra<'a>> + Clone {
let primary_block = attribute_specifier_sequence()
.then(choice((
compound_statement().map(PrimaryBlock::Compound),
selection_statement().map(PrimaryBlock::Selection),
iteration_statement().map(PrimaryBlock::Iteration),
)))
.map(|(attributes, block)| UnlabeledStatement::Primary { attributes, block });
let jump = attribute_specifier_sequence()
.then(jump_statement())
.map(|(attributes, statement)| UnlabeledStatement::Jump { attributes, statement });
let expr = expression_statement().map(UnlabeledStatement::Expression);
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
primary_block,
jump,
expr,
))
.labelled("unlabeled statement")
.as_context()
}
#[apply(cached)]
pub fn label<'a>() -> impl Parser<'a, Tokens<'a>, Label, Extra<'a>> + Clone {
let case_label = attribute_specifier_sequence()
.then(keyword("case").ignore_then(constant_expression()))
.then_ignore(punctuator(Punctuator::Colon))
.map(|(attributes, expression)| Label::Case { attributes, expression });
let default_label = attribute_specifier_sequence()
.then(keyword("default"))
.then_ignore(punctuator(Punctuator::Colon))
.map(|(attributes, _)| Label::Default { attributes });
let ident_label = attribute_specifier_sequence()
.then(identifier())
.then_ignore(punctuator(Punctuator::Colon))
.map(|(attributes, identifier)| Label::Identifier { attributes, identifier });
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
case_label,
default_label,
ident_label,
))
.labelled("label")
.as_context()
}
pub fn labelled_statement<'a>() -> impl Parser<'a, Tokens<'a>, LabeledStatement, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
label()
.then(statement().map(Box::new))
.map(|(label, statement)| LabeledStatement { label, statement }),
))
.labelled("labeled statement")
.as_context()
}
#[apply(cached)]
pub fn compound_statement<'a>() -> impl Parser<'a, Tokens<'a>, CompoundStatement, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
block_item()
.repeated()
.collect::<Vec<BlockItem>>()
.braced()
.map(|items| CompoundStatement { items }),
))
.labelled("compound statement")
.as_context()
}
pub fn block_item<'a>() -> impl Parser<'a, Tokens<'a>, BlockItem, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
declaration().map(BlockItem::Declaration),
label().map(BlockItem::Label),
unlabeled_statement().map(BlockItem::Statement),
))
.labelled("block item")
.as_context()
}
pub fn expression_statement<'a>() -> impl Parser<'a, Tokens<'a>, ExpressionStatement, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
attribute_specifier_sequence()
.then(
expression()
.map(Box::new)
.or_not()
.then_ignore(punctuator(Punctuator::Semicolon))
.recover_with(skip_until(any().ignored(), punctuator(Punctuator::Semicolon), || {
Some(Box::new(Expression::dummy(ExpressionKind::Error)))
})),
)
.map(|(attributes, expression)| ExpressionStatement { attributes, expression }),
))
.labelled("expression statement")
.as_context()
}
pub fn selection_statement<'a>() -> impl Parser<'a, Tokens<'a>, SelectionStatement, Extra<'a>> + Clone {
let if_stmt = keyword("if")
.ignore_then(
expression()
.parenthesized()
.recover_with(recover_parenthesized_with(|span| {
Expression::new(ExpressionKind::Error, span)
}))
.map(Box::new),
)
.then(statement().map(Box::new))
.then(keyword("else").ignore_then(statement().map(Box::new)).or_not())
.map(|((condition, then_stmt), else_stmt)| SelectionStatement::If { condition, then_stmt, else_stmt });
let switch_stmt = keyword("switch")
.ignore_then(
expression()
.parenthesized()
.recover_with(recover_parenthesized_with(|span| {
Expression::new(ExpressionKind::Error, span)
}))
.map(Box::new),
)
.then(statement().map(Box::new))
.map(|(expression, statement)| SelectionStatement::Switch { expression, statement });
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
if_stmt,
switch_stmt,
))
.labelled("selection statement")
.as_context()
}
pub fn iteration_statement<'a>() -> impl Parser<'a, Tokens<'a>, IterationStatement, Extra<'a>> + Clone {
let while_stmt = keyword("while")
.ignore_then(
expression()
.parenthesized()
.recover_with(recover_parenthesized_with(|span| {
Expression::new(ExpressionKind::Error, span)
}))
.map(Box::new),
)
.then(statement().map(Box::new))
.map(|(condition, body)| IterationStatement::While { condition, body });
let do_while_stmt = keyword("do")
.ignore_then(statement().map(Box::new))
.then_ignore(keyword("while"))
.then(
expression()
.parenthesized()
.recover_with(recover_parenthesized_with(|span| {
Expression::new(ExpressionKind::Error, span)
}))
.map(Box::new),
)
.then_ignore(punctuator(Punctuator::Semicolon))
.map(|(body, condition)| IterationStatement::DoWhile { body, condition });
let for_stmt = keyword("for")
.ignore_then(
choice((
declaration().map(ForInit::Declaration).map(Some),
expression()
.map(Box::new)
.map(ForInit::Expression)
.or_not()
.then_ignore(punctuator(Punctuator::Semicolon)),
))
.then(expression().map(Box::new).or_not())
.then_ignore(punctuator(Punctuator::Semicolon))
.then(expression().map(Box::new).or_not())
.parenthesized(), )
.then(statement().map(Box::new))
.map(|(((init, condition), update), body)| IterationStatement::For { init, condition, update, body });
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
while_stmt,
do_while_stmt,
for_stmt,
))
.labelled("iteration statement")
.as_context()
}
pub fn jump_statement<'a>() -> impl Parser<'a, Tokens<'a>, JumpStatement, Extra<'a>> + Clone {
let goto_stmt = keyword("goto")
.ignore_then(identifier())
.then_ignore(punctuator(Punctuator::Semicolon))
.map(JumpStatement::Goto);
let continue_stmt = keyword("continue")
.then_ignore(punctuator(Punctuator::Semicolon))
.to(JumpStatement::Continue);
let break_stmt = keyword("break")
.then_ignore(punctuator(Punctuator::Semicolon))
.to(JumpStatement::Break);
let return_stmt = keyword("return")
.ignore_then(expression().or_not())
.then_ignore(punctuator(Punctuator::Semicolon))
.map(|expr| JumpStatement::Return(expr.map(Box::new)));
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
goto_stmt,
continue_stmt,
break_stmt,
return_stmt,
))
.labelled("jump statement")
.as_context()
}
#[apply(cached)]
pub fn attribute_specifier_sequence<'a>() -> impl Parser<'a, Tokens<'a>, Vec<AttributeSpecifier>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
attribute_specifier().repeated().collect::<Vec<AttributeSpecifier>>(),
))
.labelled("attribute specifier sequence")
.as_context()
}
pub fn attribute_specifier<'a>() -> impl Parser<'a, Tokens<'a>, AttributeSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
old_fashioned_attribute_specifier(),
asm_attribute_specifier(),
attribute_list()
.map(AttributeSpecifier::Attributes) .bracketed()
.bracketed(),
))
.labelled("attribute specifier")
.as_context()
}
#[apply(cached)]
pub fn old_fashioned_attribute_specifier<'a>() -> impl Parser<'a, Tokens<'a>, AttributeSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
keyword("__attribute__").ignore_then(
attribute_list()
.parenthesized()
.parenthesized()
.map(AttributeSpecifier::Attributes)
.recover_with(recover_parenthesized(AttributeSpecifier::Error)),
),
))
.labelled("old fashioned attribute specifier")
.as_context()
}
pub fn asm_attribute_specifier<'a>() -> impl Parser<'a, Tokens<'a>, AttributeSpecifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
choice((keyword("__asm"), keyword("__asm__"))).ignore_then(
string_literal()
.map(AttributeSpecifier::Asm)
.parenthesized()
.recover_with(recover_parenthesized(AttributeSpecifier::Error)),
),
))
.labelled("asm attribute specifier")
.as_context()
}
pub fn attribute_list<'a>() -> impl Parser<'a, Tokens<'a>, Vec<Attribute>, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
attribute()
.separated_by(punctuator(Punctuator::Comma))
.allow_trailing()
.collect::<Vec<Attribute>>(),
))
.labelled("attribute list")
.as_context()
}
#[apply(cached)]
pub fn attribute<'a>() -> impl Parser<'a, Tokens<'a>, Attribute, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
attribute_token()
.then(attribute_argument_clause().or_not())
.map(|(token, arguments)| Attribute { token, arguments }),
))
.labelled("attribute")
.as_context()
}
pub fn attribute_token<'a>() -> impl Parser<'a, Tokens<'a>, AttributeToken, Extra<'a>> + Clone {
let standard = identifier_or_keyword();
let prefixed = identifier_or_keyword()
.then_ignore(punctuator(Punctuator::Scope))
.then(identifier_or_keyword());
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
prefixed.map(|(prefix, identifier)| AttributeToken::Prefixed { prefix, identifier }),
standard.map(AttributeToken::Standard),
))
.labelled("attribute token")
.as_context()
}
pub fn attribute_argument_clause<'a>() -> impl Parser<'a, Tokens<'a>, TokenStream, Extra<'a>> + Clone {
select_ref! {
Token::Parenthesized(tokens) => tokens.clone(),
}
}
pub fn translation_unit<'a>() -> impl Parser<'a, Tokens<'a>, TranslationUnit, Extra<'a>> + Clone {
external_declaration()
.repeated()
.collect::<Vec<ExternalDeclaration>>()
.map(|external_declarations| TranslationUnit { external_declarations })
.labelled("translation unit")
.as_context()
}
pub fn external_declaration<'a>() -> impl Parser<'a, Tokens<'a>, ExternalDeclaration, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
function_definition().map(ExternalDeclaration::Function),
declaration().map(ExternalDeclaration::Declaration),
))
.labelled("external declaration")
.as_context()
}
pub fn function_definition<'a>() -> impl Parser<'a, Tokens<'a>, FunctionDefinition, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
attribute_specifier_sequence()
.then(declaration_specifiers())
.then(declarator())
.then(compound_statement())
.map(|(((attributes, specifiers), declarator), body)| FunctionDefinition {
attributes,
specifiers,
declarator,
body,
}),
))
.labelled("function definition")
.as_context()
}
pub fn identifier_or_keyword<'a>() -> impl Parser<'a, Tokens<'a>, Identifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
select_ref! {
Token::Identifier(value) => value.clone(),
},
))
}
pub fn identifier<'a>() -> impl Parser<'a, Tokens<'a>, Identifier, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
identifier_or_keyword().try_map(|id, span| match id.as_ref() {
"auto" | "break" | "case" | "char" | "const" | "continue" | "default" | "do" | "double" | "else"
| "enum" | "extern" | "float" | "for" | "goto" | "if" | "inline" | "int" | "long" | "register"
| "restrict" | "return" | "short" | "signed" | "sizeof" | "static" | "struct" | "switch" | "typedef"
| "union" | "unsigned" | "void" | "volatile" | "_Alignas" => {
Err(expected_found(["identifier"], Some(Token::Identifier(id)), span))
}
_ => Ok(id),
}),
))
}
pub fn constant<'a>() -> impl Parser<'a, Tokens<'a>, Constant, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
select_ref! {
Token::Constant(value) => value.clone(),
},
))
}
pub fn string_literal<'a>() -> impl Parser<'a, Tokens<'a>, StringLiterals, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
select_ref! {
Token::StringLiteral(value) => value.clone(),
},
))
}
pub fn quoted_string<'a>() -> impl Parser<'a, Tokens<'a>, String, Extra<'a>> + Clone {
choice((
#[cfg(feature = "quasi-quote")]
interpolation(),
select_ref! {
Token::QuotedString(value) => value.clone(),
},
))
}
#[cfg(feature = "quasi-quote")]
pub fn interpolation<'a, T: quasi_quote::Interpolate>() -> impl Parser<'a, Tokens<'a>, T, Extra<'a>> + Clone {
use std::any::Any;
select_ref! {
Token::Interpolation(value) => value.clone(),
}
.try_map(|value, span| {
(value as Box<dyn Any>)
.downcast::<T>()
.map(|value| *value)
.map_err(|_| Rich::custom(span, "unexpected interpolation value"))
})
}
pub fn keyword<'a>(kwd: &str) -> impl Parser<'a, Tokens<'a>, (), Extra<'a>> + Clone {
select_ref! {
Token::Identifier(name) if name.as_ref() == kwd => ()
}
}
pub fn punctuator<'a>(punc: Punctuator) -> impl Parser<'a, Tokens<'a>, (), Extra<'a>> + Clone {
select_ref! {
Token::Punctuator(p) if *p == punc => ()
}
}
pub fn no_recover<'a, A, O>(parser: A) -> impl Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone
where
A: Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone,
O: Clone,
{
map_ctx(|_| Context { no_recover: true }, parser)
}
pub fn allow_recover<'a, A, O>(parser: A) -> impl Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone
where
A: Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone,
O: Clone,
{
map_ctx(|_| Context { no_recover: false }, parser)
}
pub fn recover_via_parser<'a, A, O>(parser: A) -> impl chumsky::recovery::Strategy<'a, Tokens<'a>, O, Extra<'a>> + Clone
where
A: Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone,
O: Clone,
{
via_parser(parser.try_map_with(
|error, extra: &mut chumsky::input::MapExtra<'_, '_, Tokens<'a>, Extra<'a>>| {
if extra.ctx().no_recover {
Err(Rich::custom(extra.span(), "cannot recover in this context"))
} else {
Ok(error)
}
},
))
}
pub fn recover_parenthesized<'a, O: Clone>(
error: O,
) -> impl chumsky::recovery::Strategy<'a, Tokens<'a>, O, Extra<'a>> + Clone {
recover_via_parser(select_ref! {
Token::Parenthesized(_) => error.clone()
})
}
pub fn recover_parenthesized_with<'a, O: Clone>(
f: impl Fn(Span) -> O + Clone,
) -> impl chumsky::recovery::Strategy<'a, Tokens<'a>, O, Extra<'a>> + Clone {
recover_via_parser(any().map_with(move |_, extra| f(extra.span())).parenthesized())
}
pub fn recover_bracketed<'a, O: Clone>(
error: O,
) -> impl chumsky::recovery::Strategy<'a, Tokens<'a>, O, Extra<'a>> + Clone {
recover_via_parser(select_ref! {
Token::Bracketed(_) => error.clone()
})
}
pub fn recover_bracketed_with<'a, O: Clone>(
f: impl Fn(Span) -> O + Clone,
) -> impl chumsky::recovery::Strategy<'a, Tokens<'a>, O, Extra<'a>> + Clone {
recover_via_parser(any().map_with(move |_, extra| f(extra.span())).bracketed())
}
pub fn expected_found<'a, L>(
expected: impl IntoIterator<Item = L>,
found: Option<BalancedToken>,
span: Span,
) -> Rich<'a, Token, Span>
where
L: Into<chumsky::error::RichPattern<'a, Token>>,
{
use chumsky::{label::LabelError, util::MaybeRef};
LabelError::<Tokens<'a>, L>::expected_found(expected, found.map(MaybeRef::Val), span)
}
pub trait ParserExt<O> {
fn parenthesized<'a>(self) -> impl Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone
where
Self: Sized,
Self: Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone,
{
self.nested_in(select_ref! {
Token::Parenthesized(tokens) => tokens.as_input()
})
}
fn bracketed<'a>(self) -> impl Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone
where
Self: Sized,
Self: Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone,
{
self.nested_in(select_ref! {
Token::Bracketed(tokens) => tokens.as_input()
})
}
fn braced<'a>(self) -> impl Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone
where
Self: Sized,
Self: Parser<'a, Tokens<'a>, O, Extra<'a>> + Clone,
{
self.nested_in(select_ref! {
Token::Braced(tokens) => tokens.as_input()
})
}
}
impl<'a, T, O> ParserExt<O> for T where T: Parser<'a, Tokens<'a>, O, Extra<'a>> {}