use elegance::Render;
use crate::{ast::*, visitor::Visitor};
mod precedence {
pub const COMMA: usize = 1;
pub const ASSIGNMENT: usize = 2;
pub const CONDITIONAL: usize = 3;
pub const LOGICAL_OR: usize = 4;
pub const LOGICAL_AND: usize = 5;
pub const BITWISE_OR: usize = 6;
pub const BITWISE_XOR: usize = 7;
pub const BITWISE_AND: usize = 8;
pub const EQUALITY: usize = 9;
pub const RELATIONAL: usize = 10;
pub const SHIFT: usize = 11;
pub const ADDITIVE: usize = 12;
pub const MULTIPLICATIVE: usize = 13;
pub const CAST: usize = 14;
pub const UNARY: usize = 15;
pub const POSTFIX: usize = 16;
}
mod decl_precedence {
pub const POINTER: usize = 1;
pub const POSTFIX: usize = 2;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Context {
precedence: usize,
assoc: bool,
decl_precedence: usize,
}
impl Context {
fn needs_parens(&self, expr_prec: usize) -> bool {
expr_prec < self.precedence || (expr_prec == self.precedence && self.assoc)
}
fn decl_needs_parens(&self, decl_prec: usize) -> bool {
self.decl_precedence > 0 && decl_prec < self.decl_precedence
}
}
fn binary_op_precedence(op: &BinaryOperator) -> usize {
match op {
BinaryOperator::Multiply | BinaryOperator::Divide | BinaryOperator::Modulo => precedence::MULTIPLICATIVE,
BinaryOperator::Add | BinaryOperator::Subtract => precedence::ADDITIVE,
BinaryOperator::LeftShift | BinaryOperator::RightShift => precedence::SHIFT,
BinaryOperator::Less | BinaryOperator::Greater | BinaryOperator::LessEqual | BinaryOperator::GreaterEqual => {
precedence::RELATIONAL
}
BinaryOperator::Equal | BinaryOperator::NotEqual => precedence::EQUALITY,
BinaryOperator::BitwiseAnd => precedence::BITWISE_AND,
BinaryOperator::BitwiseXor => precedence::BITWISE_XOR,
BinaryOperator::BitwiseOr => precedence::BITWISE_OR,
BinaryOperator::LogicalAnd => precedence::LOGICAL_AND,
BinaryOperator::LogicalOr => precedence::LOGICAL_OR,
}
}
fn expr_precedence(e: &Expression) -> usize {
match &e.kind {
ExpressionKind::Postfix(_) => precedence::POSTFIX,
ExpressionKind::Unary(_) => precedence::UNARY,
ExpressionKind::Cast(_) => precedence::CAST,
ExpressionKind::Binary(b) => binary_op_precedence(&b.operator),
ExpressionKind::Conditional(_) => precedence::CONDITIONAL,
ExpressionKind::Assignment(_) => precedence::ASSIGNMENT,
ExpressionKind::Comma(_) => precedence::COMMA,
ExpressionKind::Error => precedence::POSTFIX,
}
}
pub type Printer<'a, R> = elegance::Printer<'a, R, String, Context>;
impl<'a, R: Render> Visitor<'a> for Printer<'a, R> {
type Result = Result<(), R::Error>;
fn visit_variable_name(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_type_name_identifier(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_enum_constant(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_label_name(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_member_name(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_struct_name(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_enum_name(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_enumerator_name(&mut self, id: &'a Identifier) -> Self::Result {
self.text(&id.0)
}
fn visit_translation_unit(&mut self, tu: &'a TranslationUnit) -> Self::Result {
for external in &tu.external_declarations {
self.visit_external_declaration(external)?;
self.hard_break()?;
}
Ok(())
}
fn visit_function_definition(&mut self, f: &'a FunctionDefinition) -> Self::Result {
self.igroup(0, |pp| {
for a in &f.attributes {
pp.visit_attribute_specifier(a)?;
pp.space()?;
}
pp.visit_declaration_specifiers(&f.specifiers)?;
pp.space()?;
pp.visit_declarator(&f.declarator)?;
pp.space()?;
pp.visit_compound_statement(&f.body)?;
Ok(())
})
}
fn visit_attribute_specifier(&mut self, a: &'a AttributeSpecifier) -> Self::Result {
match a {
AttributeSpecifier::Attributes(attributes) => self.igroup(2, |pp| {
pp.text("[[")?;
for (i, attr) in attributes.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.space()?;
}
pp.visit_attribute(attr)?;
}
pp.scan_break(0, -2)?;
pp.text("]]")?;
Ok(())
}),
AttributeSpecifier::Asm(string_literals) => self.visit_asm_attribute_specifier(string_literals),
AttributeSpecifier::Error => Ok(()),
}
}
fn visit_attribute(&mut self, a: &'a Attribute) -> Self::Result {
self.igroup(2, |pp| {
match &a.token {
AttributeToken::Standard(identifier) => pp.text(&identifier.0)?,
AttributeToken::Prefixed { prefix, identifier } => {
pp.text(&prefix.0)?;
pp.text("::")?;
pp.text(&identifier.0)?;
}
}
if let Some(args) = &a.arguments {
pp.igroup(2, |pp| {
pp.text("(")?;
print_balanced_token_sequence(pp, args)?;
pp.scan_break(0, -2)?;
pp.text(")")
})?;
}
Ok(())
})
}
fn visit_label(&mut self, l: &'a Label) -> Self::Result {
match l {
Label::Identifier { attributes, identifier } => {
for a in attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.visit_label_name(identifier)?;
self.text(":")?;
}
Label::Case { attributes, expression } => {
for a in attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.text("case")?;
self.space()?;
self.visit_constant_expression(expression)?;
self.text(":")?;
}
Label::Default { attributes } => {
for a in attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.text("default:")?;
}
}
self.space()
}
fn visit_unlabeled_statement(&mut self, s: &'a UnlabeledStatement) -> Self::Result {
match s {
UnlabeledStatement::Expression(es) => self.visit_expression_statement(es),
UnlabeledStatement::Primary { attributes, block } => {
for a in attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.visit_primary_block(block)
}
UnlabeledStatement::Jump { attributes, statement } => {
for a in attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.visit_jump_statement(statement)
}
}
}
fn visit_expression_statement(&mut self, e: &'a ExpressionStatement) -> Self::Result {
for a in &e.attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
if let Some(expr) = &e.expression {
self.visit_expression(expr)?;
}
self.text(";")
}
fn visit_jump_statement(&mut self, j: &'a JumpStatement) -> Self::Result {
match j {
JumpStatement::Goto(id) => {
self.text("goto")?;
self.space()?;
self.visit_label_name(id)?;
self.text(";")
}
JumpStatement::Continue => self.text("continue;"),
JumpStatement::Break => self.text("break;"),
JumpStatement::Return(expr) => {
self.text("return")?;
if let Some(e) = expr {
self.space()?;
self.visit_expression(e)?;
}
self.text(";")
}
}
}
fn visit_selection_statement(&mut self, sel: &'a SelectionStatement) -> Self::Result {
match sel {
SelectionStatement::If { condition, then_stmt, else_stmt } => {
self.text("if")?;
self.space()?;
self.text("(")?;
self.visit_expression(condition)?;
self.text(")")?;
self.space()?;
self.visit_statement(then_stmt)?;
if let Some(e) = else_stmt {
self.space()?;
self.text("else")?;
self.space()?;
self.visit_statement(e)?;
}
Ok(())
}
SelectionStatement::Switch { expression, statement } => {
self.text("switch")?;
self.space()?;
self.text("(")?;
self.visit_expression(expression)?;
self.text(")")?;
self.space()?;
self.visit_statement(statement)
}
}
}
fn visit_iteration_statement(&mut self, iter: &'a IterationStatement) -> Self::Result {
match iter {
IterationStatement::While { condition, body } => {
self.text("while")?;
self.space()?;
self.text("(")?;
self.visit_expression(condition)?;
self.text(")")?;
self.space()?;
self.visit_statement(body)
}
IterationStatement::DoWhile { body, condition } => {
self.text("do")?;
self.space()?;
self.visit_statement(body)?;
self.space()?;
self.text("while")?;
self.space()?;
self.text("(")?;
self.visit_expression(condition)?;
self.text(");")
}
IterationStatement::For { init, condition, update, body } => {
self.text("for")?;
self.space()?;
self.text("(")?;
if let Some(i) = init {
self.visit_for_init(i)?;
} else {
self.text(";")?;
}
if let Some(c) = condition {
self.space()?;
self.visit_expression(c)?;
}
self.text(";")?;
if let Some(u) = update {
self.space()?;
self.visit_expression(u)?;
}
self.text(")")?;
self.space()?;
self.visit_statement(body)
}
IterationStatement::Error => Ok(()),
}
}
fn visit_for_init(&mut self, fi: &'a ForInit) -> Self::Result {
match fi {
ForInit::Expression(e) => {
self.visit_expression(e)?;
self.text(";")
}
ForInit::Declaration(d) => {
self.visit_declaration(d) }
}
}
fn visit_expression(&mut self, e: &'a Expression) -> Self::Result {
let ctx = self.extra;
let expr_prec = expr_precedence(e);
let needs_parens = ctx.needs_parens(expr_prec);
self.scan_begin(0, true);
if needs_parens {
self.text("(")?;
self.scan_break(0, 2)?;
}
self.extra = Context::default();
match &e.kind {
ExpressionKind::Postfix(p) => self.visit_postfix_expression(p)?,
ExpressionKind::Unary(u) => self.visit_unary_expression(u)?,
ExpressionKind::Cast(c) => self.visit_cast_expression(c)?,
ExpressionKind::Binary(b) => self.visit_binary_expression(b)?,
ExpressionKind::Conditional(c) => self.visit_conditional_expression(c)?,
ExpressionKind::Assignment(a) => self.visit_assignment_expression(a)?,
ExpressionKind::Comma(c) => self.visit_comma_expression(c)?,
ExpressionKind::Error => {}
}
self.extra = ctx;
if needs_parens {
self.scan_break(0, 0)?;
self.text(")")?;
}
self.scan_end()?;
Ok(())
}
fn visit_binary_expression(&mut self, b: &'a BinaryExpression) -> Self::Result {
self.igroup(0, |pp| {
let op_prec = binary_op_precedence(&b.operator);
pp.extra = Context {
precedence: op_prec,
assoc: false,
decl_precedence: 0,
};
pp.visit_expression(&b.left)?;
pp.space()?;
pp.visit_binary_operator(&b.operator)?;
pp.scan_break(1, 2)?;
pp.extra = Context {
precedence: op_prec,
assoc: true,
decl_precedence: 0,
};
pp.visit_expression(&b.right)
})
}
fn visit_binary_operator(&mut self, op: &'a BinaryOperator) -> Self::Result {
let text = match op {
BinaryOperator::Multiply => "*",
BinaryOperator::Divide => "/",
BinaryOperator::Modulo => "%",
BinaryOperator::Add => "+",
BinaryOperator::Subtract => "-",
BinaryOperator::LeftShift => "<<",
BinaryOperator::RightShift => ">>",
BinaryOperator::BitwiseAnd => "&",
BinaryOperator::BitwiseXor => "^",
BinaryOperator::BitwiseOr => "|",
BinaryOperator::Less => "<",
BinaryOperator::Greater => ">",
BinaryOperator::LessEqual => "<=",
BinaryOperator::GreaterEqual => ">=",
BinaryOperator::Equal => "==",
BinaryOperator::NotEqual => "!=",
BinaryOperator::LogicalAnd => "&&",
BinaryOperator::LogicalOr => "||",
};
self.text(text)
}
fn visit_conditional_expression(&mut self, cond: &'a ConditionalExpression) -> Self::Result {
self.igroup(2, |pp| {
pp.extra = Context {
precedence: precedence::CONDITIONAL,
assoc: true,
decl_precedence: 0,
};
pp.visit_expression(&cond.condition)?;
pp.space()?;
pp.text("?")?;
pp.scan_break(1, 2)?;
pp.extra = Context::default();
pp.visit_expression(&cond.then_expr)?;
pp.space()?;
pp.text(":")?;
pp.scan_break(1, 2)?;
pp.extra = Context {
precedence: precedence::CONDITIONAL,
assoc: false,
decl_precedence: 0,
};
pp.visit_expression(&cond.else_expr)
})
}
fn visit_assignment_expression(&mut self, a: &'a AssignmentExpression) -> Self::Result {
self.igroup(0, |pp| {
pp.extra = Context {
precedence: precedence::ASSIGNMENT,
assoc: true,
decl_precedence: 0,
};
pp.visit_expression(&a.left)?;
pp.space()?;
pp.visit_assignment_operator(&a.operator)?;
pp.scan_break(1, 2)?;
pp.extra = Context {
precedence: precedence::ASSIGNMENT,
assoc: false,
decl_precedence: 0,
};
pp.visit_expression(&a.right)
})
}
fn visit_assignment_operator(&mut self, op: &'a AssignmentOperator) -> Self::Result {
let text = match op {
AssignmentOperator::Assign => "=",
AssignmentOperator::MulAssign => "*=",
AssignmentOperator::DivAssign => "/=",
AssignmentOperator::ModAssign => "%=",
AssignmentOperator::AddAssign => "+=",
AssignmentOperator::SubAssign => "-=",
AssignmentOperator::LeftShiftAssign => "<<=",
AssignmentOperator::RightShiftAssign => ">>=",
AssignmentOperator::AndAssign => "&=",
AssignmentOperator::XorAssign => "^=",
AssignmentOperator::OrAssign => "|=",
};
self.text(text)
}
fn visit_comma_expression(&mut self, c: &'a CommaExpression) -> Self::Result {
self.igroup(0, |pp| {
for (i, expr) in c.expressions.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.scan_break(1, 2)?;
}
pp.extra = Context {
precedence: precedence::COMMA,
assoc: i > 0, decl_precedence: 0,
};
pp.visit_expression(expr)?;
}
Ok(())
})
}
fn visit_declaration(&mut self, d: &'a Declaration) -> Self::Result {
self.igroup(0, |pp| {
match &d.kind {
DeclarationKind::Normal { attributes, specifiers, declarators } => {
for a in attributes {
pp.visit_attribute_specifier(a)?;
pp.space()?;
}
pp.visit_declaration_specifiers(specifiers)?;
if !declarators.is_empty() {
pp.space()?;
for (i, init_decl) in declarators.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.space()?;
}
pp.visit_declarator(&init_decl.declarator)?;
if let Some(initializer) = &init_decl.initializer {
pp.space()?;
pp.text("=")?;
pp.space()?;
pp.visit_initializer(initializer)?;
}
}
}
pp.text(";")
}
DeclarationKind::Typedef { attributes, specifiers, declarators } => {
for a in attributes {
pp.visit_attribute_specifier(a)?;
pp.space()?;
}
pp.text("typedef")?;
pp.space()?;
let mut first = true;
for spec in specifiers.specifiers.iter() {
match spec {
DeclarationSpecifier::StorageClass(StorageClassSpecifier::Typedef) => {
continue;
}
_ => {
if !first {
pp.space()?;
}
first = false;
pp.visit_declaration_specifier(spec)?;
}
}
}
if !declarators.is_empty() {
pp.space()?;
for (i, decl) in declarators.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.space()?;
}
pp.visit_declarator(decl)?;
}
}
pp.text(";")
}
DeclarationKind::StaticAssert(sa) => {
pp.text("_Static_assert")?;
pp.text("(")?;
pp.visit_constant_expression(&sa.condition)?;
if let Some(msg) = &sa.message {
pp.text(",")?;
pp.space()?;
for (i, lit) in msg.0.iter().enumerate() {
if i > 0 {
pp.space()?;
}
print_string_literal(pp, lit)?;
}
}
pp.text(");")?;
Ok(())
}
DeclarationKind::Attribute(attrs) => {
for (i, a) in attrs.iter().enumerate() {
if i > 0 {
pp.space()?;
}
pp.visit_attribute_specifier(a)?;
}
pp.text(";")
}
DeclarationKind::Error => Ok(()),
}
})
}
fn visit_declarator(&mut self, d: &'a Declarator) -> Self::Result {
let ctx = self.extra;
let decl_prec = match d {
Declarator::Direct(_) => decl_precedence::POSTFIX,
Declarator::Pointer { .. } => decl_precedence::POINTER,
Declarator::Error => decl_precedence::POSTFIX,
};
let needs_parens = ctx.decl_needs_parens(decl_prec);
if needs_parens {
self.text("(")?;
}
self.extra.decl_precedence = 0;
match d {
Declarator::Direct(dd) => self.visit_direct_declarator(dd)?,
Declarator::Pointer { pointer, declarator } => {
self.visit_pointer(pointer)?;
self.space()?;
self.visit_declarator(declarator)?;
}
Declarator::Error => {}
}
self.extra = ctx;
if needs_parens {
self.text(")")?;
}
Ok(())
}
fn visit_direct_declarator(&mut self, d: &'a DirectDeclarator) -> Self::Result {
match d {
DirectDeclarator::Identifier { identifier, attributes } => {
self.visit_variable_name(identifier)?;
for a in attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
Ok(())
}
DirectDeclarator::Parenthesized(inner) => {
let inner_prec = match inner.as_ref() {
Declarator::Direct(_) => decl_precedence::POSTFIX,
Declarator::Pointer { .. } => decl_precedence::POINTER,
Declarator::Error => decl_precedence::POSTFIX,
};
let ctx = self.extra;
let needs_parens = ctx.decl_needs_parens(inner_prec);
if needs_parens {
self.text("(")?;
}
self.extra.decl_precedence = 0;
self.visit_declarator(inner)?;
self.extra = ctx;
if needs_parens {
self.text(")")?;
}
Ok(())
}
DirectDeclarator::Array { declarator, attributes, array_declarator } => {
let old_ctx = self.extra;
self.extra.decl_precedence = decl_precedence::POSTFIX;
self.visit_direct_declarator(declarator)?;
self.extra = old_ctx;
for a in attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
self.text("[")?;
self.visit_array_declarator(array_declarator)?;
self.text("]")
}
DirectDeclarator::Function { declarator, attributes, parameters } => {
let old_ctx = self.extra;
self.extra.decl_precedence = decl_precedence::POSTFIX;
self.visit_direct_declarator(declarator)?;
self.extra = old_ctx;
self.igroup(2, |pp| {
pp.text("(")?;
pp.visit_parameter_type_list(parameters)?;
pp.scan_break(0, -2)?;
pp.text(")")
})?;
for a in attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
Ok(())
}
}
}
fn visit_postfix_expression(&mut self, p: &'a PostfixExpression) -> Self::Result {
match p {
PostfixExpression::Primary(pr) => self.visit_primary_expression(pr),
PostfixExpression::ArrayAccess { array, index } => {
self.visit_postfix_expression(array)?;
self.text("[")?;
let old_ctx = self.extra;
self.extra = Context::default();
self.visit_expression(index)?;
self.extra = old_ctx;
self.text("]")
}
PostfixExpression::FunctionCall { function, arguments } => {
self.visit_postfix_expression(function)?;
self.igroup(2, |pp| {
pp.text("(")?;
for (i, arg) in arguments.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.space()?;
}
let old_ctx = pp.extra;
pp.extra = Context::default();
pp.visit_expression(arg)?;
pp.extra = old_ctx;
}
pp.scan_break(0, -2)?;
pp.text(")")
})
}
PostfixExpression::MemberAccess { object, member } => {
self.visit_postfix_expression(object)?;
self.text(".")?;
self.visit_member_name(member)
}
PostfixExpression::MemberAccessPtr { object, member } => {
self.visit_postfix_expression(object)?;
self.text("->")?;
self.visit_member_name(member)
}
PostfixExpression::PostIncrement(inner) => {
self.visit_postfix_expression(inner)?;
self.text("++")
}
PostfixExpression::PostDecrement(inner) => {
self.visit_postfix_expression(inner)?;
self.text("--")
}
PostfixExpression::CompoundLiteral(cl) => {
self.text("(")?;
for scs in &cl.storage_class_specifiers {
self.visit_storage_class_specifier(scs)?;
self.space()?;
}
self.visit_type_name(&cl.type_name)?;
self.text(")")?;
self.visit_braced_initializer(&cl.initializer)
}
}
}
fn visit_primary_expression(&mut self, pr: &'a PrimaryExpression) -> Self::Result {
match pr {
PrimaryExpression::Identifier(id) => self.visit_variable_name(id),
PrimaryExpression::Constant(c) => print_constant(self, c),
PrimaryExpression::EnumerationConstant(id) => self.visit_enum_constant(id),
PrimaryExpression::StringLiteral(lits) => {
for (i, lit) in lits.0.iter().enumerate() {
if i > 0 {
self.space()?;
}
print_string_literal(self, lit)?;
}
Ok(())
}
PrimaryExpression::QuotedString(s) => {
self.text("`")?;
self.text(s)?;
self.text("`")
}
PrimaryExpression::Parenthesized(e) => {
self.text("(")?;
let old_ctx = self.extra;
self.extra = Context::default();
self.visit_expression(e)?;
self.extra = old_ctx;
self.text(")")
}
PrimaryExpression::Generic(g) => {
self.text("_Generic")?;
self.igroup(2, |pp| {
pp.text("(")?;
let old_ctx = pp.extra;
pp.extra = Context::default();
pp.visit_expression(&g.controlling_expression)?;
pp.text(",")?;
pp.space()?;
for (i, assoc) in g.associations.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.space()?;
}
match assoc {
GenericAssociation::Type { type_name, expression } => {
pp.visit_type_name(type_name)?;
pp.text(":")?;
pp.space()?;
pp.visit_expression(expression)?;
}
GenericAssociation::Default { expression } => {
pp.text("default:")?;
pp.space()?;
pp.visit_expression(expression)?;
}
}
}
pp.extra = old_ctx;
pp.scan_break(0, -2)?;
pp.text(")")
})
}
PrimaryExpression::Error => Ok(()),
}
}
fn visit_unary_expression(&mut self, u: &'a UnaryExpression) -> Self::Result {
match u {
UnaryExpression::Postfix(p) => self.visit_postfix_expression(p),
UnaryExpression::PreIncrement(inner) => {
self.text("++")?;
let old_ctx = self.extra;
self.extra = Context {
precedence: precedence::UNARY,
assoc: false,
decl_precedence: 0,
};
self.visit_unary_expression(inner)?;
self.extra = old_ctx;
Ok(())
}
UnaryExpression::PreDecrement(inner) => {
self.text("--")?;
let old_ctx = self.extra;
self.extra = Context {
precedence: precedence::UNARY,
assoc: false,
decl_precedence: 0,
};
self.visit_unary_expression(inner)?;
self.extra = old_ctx;
Ok(())
}
UnaryExpression::Unary { operator, operand } => {
self.visit_unary_operator(operator)?;
let old_ctx = self.extra;
self.extra = Context {
precedence: precedence::CAST,
assoc: false,
decl_precedence: 0,
};
self.visit_cast_expression(operand)?;
self.extra = old_ctx;
Ok(())
}
UnaryExpression::Sizeof(inner) => {
self.text("sizeof")?;
self.space()?;
let old_ctx = self.extra;
self.extra = Context {
precedence: precedence::UNARY,
assoc: false,
decl_precedence: 0,
};
self.visit_unary_expression(inner)?;
self.extra = old_ctx;
Ok(())
}
UnaryExpression::SizeofType(tn) => {
self.text("sizeof")?;
self.text("(")?;
self.visit_type_name(tn)?;
self.text(")")
}
UnaryExpression::Alignof(tn) => {
self.text("_Alignof")?;
self.text("(")?;
self.visit_type_name(tn)?;
self.text(")")
}
}
}
fn visit_unary_operator(&mut self, op: &'a UnaryOperator) -> Self::Result {
let text = match op {
UnaryOperator::Address => "&",
UnaryOperator::Dereference => "*",
UnaryOperator::Plus => "+",
UnaryOperator::Minus => "-",
UnaryOperator::BitwiseNot => "~",
UnaryOperator::LogicalNot => "!",
};
self.text(text)
}
fn visit_cast_expression(&mut self, c: &'a CastExpression) -> Self::Result {
match c {
CastExpression::Unary(u) => self.visit_unary_expression(u),
CastExpression::Cast { type_name, expression } => {
self.text("(")?;
self.visit_type_name(type_name)?;
self.text(")")?;
let old_ctx = self.extra;
self.extra = Context {
precedence: precedence::CAST,
assoc: false,
decl_precedence: 0,
};
self.visit_cast_expression(expression)?;
self.extra = old_ctx;
Ok(())
}
}
}
fn visit_compound_statement(&mut self, c: &'a CompoundStatement) -> Self::Result {
self.cgroup(2, |pp| {
pp.text("{")?;
for item in &c.items {
pp.space()?;
pp.igroup(2, |pp| pp.visit_block_item(item))?;
}
pp.scan_break(1, -2)?;
pp.text("}")
})
}
fn visit_declaration_specifiers(&mut self, s: &'a DeclarationSpecifiers) -> Self::Result {
for (i, spec) in s.specifiers.iter().enumerate() {
if i > 0 {
self.space()?;
}
self.visit_declaration_specifier(spec)?;
}
Ok(())
}
fn visit_storage_class_specifier(&mut self, scs: &'a StorageClassSpecifier) -> Self::Result {
let text = match scs {
StorageClassSpecifier::Auto => "auto",
StorageClassSpecifier::Constexpr => "constexpr",
StorageClassSpecifier::Extern => "extern",
StorageClassSpecifier::Register => "register",
StorageClassSpecifier::Static => "static",
StorageClassSpecifier::ThreadLocal => "_Thread_local",
StorageClassSpecifier::Typedef => "typedef",
};
self.text(text)
}
fn visit_function_specifier(&mut self, fs: &'a FunctionSpecifier) -> Self::Result {
let text = match fs {
FunctionSpecifier::Inline => "inline",
FunctionSpecifier::Noreturn => "_Noreturn",
};
self.text(text)
}
fn visit_type_qualifier(&mut self, tq: &'a TypeQualifier) -> Self::Result {
let text = match tq {
TypeQualifier::Const => "const",
TypeQualifier::Restrict => "restrict",
TypeQualifier::Volatile => "volatile",
TypeQualifier::Atomic => "_Atomic",
TypeQualifier::Nonnull => "_Nonnull",
TypeQualifier::Nullable => "_Nullable",
TypeQualifier::ThreadLocal => "_Thread_local",
};
self.text(text)
}
fn visit_alignment_specifier(&mut self, a: &'a AlignmentSpecifier) -> Self::Result {
self.text("_Alignas")?;
self.text("(")?;
match a {
AlignmentSpecifier::Type(tn) => self.visit_type_name(tn)?,
AlignmentSpecifier::Expression(e) => self.visit_constant_expression(e)?,
}
self.text(")")
}
fn visit_type_specifier(&mut self, ts: &'a TypeSpecifier) -> Self::Result {
match ts {
TypeSpecifier::Void => self.text("void"),
TypeSpecifier::Char => self.text("char"),
TypeSpecifier::Short => self.text("short"),
TypeSpecifier::Int => self.text("int"),
TypeSpecifier::Long => self.text("long"),
TypeSpecifier::Float => self.text("float"),
TypeSpecifier::Double => self.text("double"),
TypeSpecifier::Signed => self.text("signed"),
TypeSpecifier::Unsigned => self.text("unsigned"),
TypeSpecifier::BitInt(e) => {
self.text("_BitInt")?;
self.text("(")?;
self.visit_constant_expression(e)?;
self.text(")")
}
TypeSpecifier::Bool => self.text("_Bool"),
TypeSpecifier::Complex => self.text("_Complex"),
TypeSpecifier::Decimal32 => self.text("_Decimal32"),
TypeSpecifier::Decimal64 => self.text("_Decimal64"),
TypeSpecifier::Decimal128 => self.text("_Decimal128"),
TypeSpecifier::Atomic(a) => self.visit_atomic_type_specifier(a),
TypeSpecifier::Struct(s) => self.visit_struct_union_specifier(s),
TypeSpecifier::Enum(e) => self.visit_enum_specifier(e),
TypeSpecifier::TypedefName(id) => self.visit_type_name_identifier(id),
TypeSpecifier::Typeof(t) => self.visit_typeof(t),
}
}
fn visit_struct_union_specifier(&mut self, s: &'a StructOrUnionSpecifier) -> Self::Result {
let keyword = match s.kind {
StructOrUnion::Struct => "struct",
StructOrUnion::Union => "union",
};
self.text(keyword)?;
for a in &s.attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
if let Some(id) = &s.identifier {
self.space()?;
self.visit_struct_name(id)?;
}
if let Some(members) = &s.members {
self.space()?;
self.igroup(2, |pp| {
pp.text("{")?;
pp.space()?;
for member in members {
pp.visit_member_declaration(member)?;
pp.space()?;
}
pp.scan_break(0, -2)?;
pp.text("}")
})?;
}
Ok(())
}
fn visit_member_declaration(&mut self, m: &'a MemberDeclaration) -> Self::Result {
match m {
MemberDeclaration::Normal { attributes, specifiers, declarators } => {
for a in attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.visit_specifier_qualifier_list(specifiers)?;
if !declarators.is_empty() {
self.space()?;
for (i, decl) in declarators.iter().enumerate() {
if i > 0 {
self.text(",")?;
self.space()?;
}
self.visit_member_declarator(decl)?;
}
}
self.text(";")
}
MemberDeclaration::StaticAssert(sa) => self.visit_static_assert_declaration(sa),
MemberDeclaration::Error => Ok(()),
}
}
fn visit_member_declarator(&mut self, md: &'a MemberDeclarator) -> Self::Result {
match md {
MemberDeclarator::Declarator(d) => self.visit_declarator(d),
MemberDeclarator::BitField { declarator, width } => {
if let Some(d) = declarator {
self.visit_declarator(d)?;
}
self.text(":")?;
self.space()?;
self.visit_constant_expression(width)
}
}
}
fn visit_static_assert_declaration(&mut self, sa: &'a StaticAssertDeclaration) -> Self::Result {
self.text("_Static_assert")?;
self.text("(")?;
self.visit_constant_expression(&sa.condition)?;
if let Some(msg) = &sa.message {
self.text(",")?;
self.space()?;
self.visit_static_assert_message(msg)?;
}
self.text(");")
}
fn visit_static_assert_message(&mut self, msg: &'a StringLiterals) -> Self::Result {
for (i, lit) in msg.0.iter().enumerate() {
if i > 0 {
self.space()?;
}
print_string_literal(self, lit)?;
}
Ok(())
}
fn visit_enum_specifier(&mut self, e: &'a EnumSpecifier) -> Self::Result {
self.text("enum")?;
for a in &e.attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
if let Some(id) = &e.identifier {
self.space()?;
self.visit_enum_name(id)?;
}
if let Some(type_spec) = &e.type_specifier {
self.space()?;
self.text(":")?;
self.space()?;
self.visit_specifier_qualifier_list(type_spec)?;
}
if let Some(enumerators) = &e.enumerators {
self.space()?;
self.cgroup(2, |pp| {
pp.text("{")?;
pp.space()?;
for (i, e) in enumerators.iter().enumerate() {
if i > 0 {
pp.text(",")?;
pp.space()?;
}
pp.visit_enumerator(e)?;
}
pp.scan_break(0, -2)?;
pp.text("}")
})?;
}
Ok(())
}
fn visit_enumerator(&mut self, e: &'a Enumerator) -> Self::Result {
self.igroup(0, |pp| {
for a in &e.attributes {
pp.visit_attribute_specifier(a)?;
pp.space()?;
}
pp.visit_enumerator_name(&e.name)?;
if let Some(value) = &e.value {
pp.space()?;
pp.text("=")?;
pp.scan_break(1, 2)?;
pp.visit_constant_expression(value)?;
}
Ok(())
})
}
fn visit_atomic_type_specifier(&mut self, a: &'a AtomicTypeSpecifier) -> Self::Result {
self.text("_Atomic")?;
self.text("(")?;
self.visit_type_name(&a.type_name)?;
self.text(")")
}
fn visit_typeof(&mut self, t: &'a TypeofSpecifier) -> Self::Result {
match t {
TypeofSpecifier::Typeof(_) => self.text("typeof")?,
TypeofSpecifier::TypeofUnqual(_) => self.text("typeof_unqual")?,
}
match t {
TypeofSpecifier::Typeof(arg) | TypeofSpecifier::TypeofUnqual(arg) => {
self.text("(")?;
match arg {
TypeofSpecifierArgument::Expression(e) => self.visit_expression(e)?,
TypeofSpecifierArgument::TypeName(tn) => self.visit_type_name(tn)?,
TypeofSpecifierArgument::Error => {}
}
self.text(")")
}
}
}
fn visit_specifier_qualifier_list(&mut self, s: &'a SpecifierQualifierList) -> Self::Result {
for (i, item) in s.items.iter().enumerate() {
if i > 0 {
self.space()?;
}
self.visit_type_specifier_qualifier(item)?;
}
for a in &s.attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
Ok(())
}
fn visit_type_name(&mut self, tn: &'a TypeName) -> Self::Result {
match tn {
TypeName::TypeName { specifiers, abstract_declarator } => {
self.visit_specifier_qualifier_list(specifiers)?;
if let Some(ad) = abstract_declarator {
self.space()?;
self.visit_abstract_declarator(ad)?;
}
Ok(())
}
TypeName::Error => Ok(()),
}
}
fn visit_abstract_declarator(&mut self, a: &'a AbstractDeclarator) -> Self::Result {
let ctx = self.extra;
let decl_prec = match a {
AbstractDeclarator::Direct(_) => decl_precedence::POSTFIX,
AbstractDeclarator::Pointer { .. } => decl_precedence::POINTER,
AbstractDeclarator::Error => decl_precedence::POSTFIX,
};
let needs_parens = ctx.decl_needs_parens(decl_prec);
if needs_parens {
self.text("(")?;
}
self.extra.decl_precedence = 0;
match a {
AbstractDeclarator::Direct(d) => self.visit_direct_abstract_declarator(d)?,
AbstractDeclarator::Pointer { pointer, abstract_declarator } => {
self.visit_pointer(pointer)?;
if let Some(ad) = abstract_declarator {
self.space()?;
self.visit_abstract_declarator(ad)?;
}
}
AbstractDeclarator::Error => {}
}
self.extra = ctx;
if needs_parens {
self.text(")")?;
}
Ok(())
}
fn visit_direct_abstract_declarator(&mut self, d: &'a DirectAbstractDeclarator) -> Self::Result {
match d {
DirectAbstractDeclarator::Parenthesized(inner) => {
let inner_prec = match inner.as_ref() {
AbstractDeclarator::Direct(_) => decl_precedence::POSTFIX,
AbstractDeclarator::Pointer { .. } => decl_precedence::POINTER,
AbstractDeclarator::Error => decl_precedence::POSTFIX,
};
let ctx = self.extra;
let needs_parens = ctx.decl_needs_parens(inner_prec);
if needs_parens {
self.text("(")?;
}
self.extra.decl_precedence = 0;
self.visit_abstract_declarator(inner)?;
self.extra = ctx;
if needs_parens {
self.text(")")?;
}
Ok(())
}
DirectAbstractDeclarator::Array { declarator, attributes, array_declarator } => {
let old_ctx = self.extra;
self.extra.decl_precedence = decl_precedence::POSTFIX;
if let Some(dd) = declarator {
self.visit_direct_abstract_declarator(dd)?;
}
self.extra = old_ctx;
for a in attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
self.text("[")?;
self.visit_array_declarator(array_declarator)?;
self.text("]")
}
DirectAbstractDeclarator::Function { declarator, attributes, parameters } => {
let old_ctx = self.extra;
self.extra.decl_precedence = decl_precedence::POSTFIX;
if let Some(dd) = declarator {
self.visit_direct_abstract_declarator(dd)?;
}
self.extra = old_ctx;
self.igroup(2, |pp| {
pp.text("(")?;
pp.visit_parameter_type_list(parameters)?;
pp.scan_break(0, -2)?;
pp.text(")")
})?;
for a in attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
Ok(())
}
}
}
fn visit_braced_initializer(&mut self, b: &'a BracedInitializer) -> Self::Result {
self.cgroup(2, |pp| {
pp.text("{")?;
for (i, init) in b.initializers.iter().enumerate() {
if i > 0 {
pp.text(",")?;
}
pp.space()?;
pp.visit_designated_initializer(init)?;
}
pp.scan_break(0, -2)?;
pp.text("}")
})
}
fn visit_designated_initializer(&mut self, init: &'a DesignatedInitializer) -> Self::Result {
self.igroup(2, |pp| {
if let Some(designation) = &init.designation {
pp.visit_designation(designation)?;
pp.space()?;
pp.text("=")?;
pp.space()?;
}
pp.visit_initializer(&init.initializer)
})
}
fn visit_designation(&mut self, d: &'a Designation) -> Self::Result {
if let Some(next) = &d.designation {
self.visit_designation(next)?;
}
match &d.designator {
Designator::Array(e) => {
self.text("[")?;
self.visit_constant_expression(e)?;
self.text("]")
}
Designator::Member(id) => {
self.text(".")?;
self.visit_member_name(id)
}
}
}
fn visit_pointer(&mut self, p: &'a Pointer) -> Self::Result {
self.visit_pointer_or_block(&p.pointer_or_block)?;
for a in &p.attributes {
self.space()?;
self.visit_attribute_specifier(a)?;
}
for tq in &p.type_qualifiers {
self.space()?;
self.visit_type_qualifier(tq)?;
}
Ok(())
}
fn visit_pointer_or_block(&mut self, pb: &'a PointerOrBlock) -> Self::Result {
match pb {
PointerOrBlock::Pointer => self.text("*"),
PointerOrBlock::Block => self.text("^"),
}
}
fn visit_array_declarator(&mut self, a: &'a ArrayDeclarator) -> Self::Result {
match a {
ArrayDeclarator::Normal { type_qualifiers, size } => {
for (i, tq) in type_qualifiers.iter().enumerate() {
if i > 0 {
self.space()?;
}
self.visit_type_qualifier(tq)?;
}
if let Some(s) = size {
if !type_qualifiers.is_empty() {
self.space()?;
}
self.visit_expression(s)?;
}
Ok(())
}
ArrayDeclarator::Static { type_qualifiers, size } => {
self.text("static")?;
for tq in type_qualifiers {
self.space()?;
self.visit_type_qualifier(tq)?;
}
self.space()?;
self.visit_expression(size)
}
ArrayDeclarator::VLA { type_qualifiers } => {
for (i, tq) in type_qualifiers.iter().enumerate() {
if i > 0 {
self.space()?;
}
self.visit_type_qualifier(tq)?;
}
if !type_qualifiers.is_empty() {
self.space()?;
}
self.text("*")
}
ArrayDeclarator::Error => Ok(()),
}
}
fn visit_parameter_type_list(&mut self, p: &'a ParameterTypeList) -> Self::Result {
match p {
ParameterTypeList::Parameters(params) => {
for (i, param) in params.iter().enumerate() {
if i > 0 {
self.text(",")?;
self.space()?;
}
self.visit_parameter_declaration(param)?;
}
Ok(())
}
ParameterTypeList::Variadic(params) => {
for (i, param) in params.iter().enumerate() {
if i > 0 {
self.text(",")?;
self.space()?;
}
self.visit_parameter_declaration(param)?;
}
self.text(",")?;
self.space()?;
self.text("...")
}
ParameterTypeList::OnlyVariadic => self.text("..."),
}
}
fn visit_parameter_declaration(&mut self, p: &'a ParameterDeclaration) -> Self::Result {
for a in &p.attributes {
self.visit_attribute_specifier(a)?;
self.space()?;
}
self.visit_declaration_specifiers(&p.specifiers)?;
if let Some(kind) = &p.declarator {
self.space()?;
self.visit_parameter_declaration_kind(kind)?;
}
Ok(())
}
}
fn print_balanced_token_sequence<'a, R: Render>(
pp: &mut Printer<'a, R>,
seq: &'a BalancedTokenSequence,
) -> Result<(), R::Error> {
for (i, token) in seq.tokens.iter().enumerate() {
if i > 0 {
pp.space()?;
}
match &token.value {
BalancedToken::Parenthesized(seq) => pp.igroup(2, |pp| {
pp.text("(")?;
print_balanced_token_sequence(pp, seq)?;
pp.scan_break(0, -2)?;
pp.text(")")
})?,
BalancedToken::Bracketed(seq) => pp.igroup(2, |pp| {
pp.text("[")?;
print_balanced_token_sequence(pp, seq)?;
pp.scan_break(0, -2)?;
pp.text("]")
})?,
BalancedToken::Braced(seq) => pp.igroup(2, |pp| {
pp.text("{")?;
print_balanced_token_sequence(pp, seq)?;
pp.scan_break(0, -2)?;
pp.text("}")
})?,
BalancedToken::Identifier(identifier) => pp.text(&identifier.0)?,
BalancedToken::StringLiteral(string_literals) => {
for (i, lit) in string_literals.0.iter().enumerate() {
if i > 0 {
pp.space()?;
}
print_string_literal(pp, lit)?;
}
}
BalancedToken::QuotedString(lit) => {
pp.text("`")?;
pp.text(lit)?;
pp.text("`")?;
}
BalancedToken::Constant(constant) => print_constant(pp, constant)?,
BalancedToken::Punctuator(punctuator) => print_punctuator(pp, punctuator)?,
#[cfg(feature = "quasi-quote")]
BalancedToken::Template(template) => {
pp.text("@")?;
pp.text(template.name.as_ref())?;
}
#[cfg(feature = "quasi-quote")]
BalancedToken::Interpolation(_) => {}
BalancedToken::Unknown => {}
}
}
Ok(())
}
fn print_string_literal<'a, R: Render>(pp: &mut Printer<'a, R>, lit: &'a StringLiteral) -> Result<(), R::Error> {
match &lit.encoding_prefix {
Some(EncodingPrefix::U8) => pp.text("u8")?,
Some(EncodingPrefix::U) => pp.text("u")?,
Some(EncodingPrefix::CapitalU) => pp.text("U")?,
Some(EncodingPrefix::L) => pp.text("L")?,
None => {}
}
pp.text("\"")?;
for ch in lit.value.chars() {
match ch {
'\\' => pp.text("\\\\")?,
'\'' => pp.text("\\'")?,
'"' => pp.text("\\\"")?,
'\n' => pp.text("\\n")?,
'\r' => pp.text("\\r")?,
'\t' => pp.text("\\t")?,
'\0' => pp.text("\\0")?,
'\x07' => pp.text("\\a")?,
'\x08' => pp.text("\\b")?,
'\x0c' => pp.text("\\f")?,
'\x0b' => pp.text("\\v")?,
'\x1b' => pp.text("\\e")?,
_ => pp.text_owned(ch.to_string())?,
}
}
pp.text("\"")
}
fn print_constant<'a, R: Render>(pp: &mut Printer<'a, R>, c: &'a Constant) -> Result<(), R::Error> {
match c {
Constant::Integer(integer_constant) => {
pp.text_owned(integer_constant.value.to_string())?;
if let Some(suffix) = integer_constant.suffix {
let suffix_str = match suffix {
crate::ast::IntegerSuffix::Unsigned => "U",
crate::ast::IntegerSuffix::Long => "L",
crate::ast::IntegerSuffix::LongLong => "LL",
crate::ast::IntegerSuffix::UnsignedLong => "UL",
crate::ast::IntegerSuffix::UnsignedLongLong => "ULL",
crate::ast::IntegerSuffix::BitPrecise => "_BitInt()",
crate::ast::IntegerSuffix::UnsignedBitPrecise => "unsigned _BitInt()",
};
pp.text(suffix_str)?;
}
Ok(())
}
Constant::Floating(floating_constant) => {
let value = floating_constant.value;
if value.is_infinite() || value.is_nan() {
panic!("Cannot print infinite or NaN floating point literals");
} else {
let value_str = value.to_string();
if !value_str.contains('.') && !value_str.contains('e') && !value_str.contains('E') {
pp.text_owned(format!("{:.1}", value))?;
} else {
pp.text_owned(value_str)?;
}
if let Some(suffix) = floating_constant.suffix {
let suffix_str = match suffix {
crate::ast::FloatingSuffix::F => "f",
crate::ast::FloatingSuffix::L => "l",
crate::ast::FloatingSuffix::DF => "df",
crate::ast::FloatingSuffix::DD => "dd",
crate::ast::FloatingSuffix::DL => "dl",
};
pp.text(suffix_str)?;
}
}
Ok(())
}
Constant::Character(character_constant) => {
match &character_constant.encoding_prefix {
Some(EncodingPrefix::U8) => pp.text("u8")?,
Some(EncodingPrefix::U) => pp.text("u")?,
Some(EncodingPrefix::CapitalU) => pp.text("U")?,
Some(EncodingPrefix::L) => pp.text("L")?,
None => {}
}
pp.text("'")?;
for ch in character_constant.value.chars() {
match ch {
'\\' => pp.text("\\\\")?,
'\'' => pp.text("\\'")?,
'"' => pp.text("\\\"")?,
'\n' => pp.text("\\n")?,
'\r' => pp.text("\\r")?,
'\t' => pp.text("\\t")?,
'\0' => pp.text("\\0")?,
'\x07' => pp.text("\\a")?,
'\x08' => pp.text("\\b")?,
'\x0c' => pp.text("\\f")?,
'\x0b' => pp.text("\\v")?,
'\x1b' => pp.text("\\e")?,
_ => pp.text_owned(ch.to_string())?,
}
}
pp.text("'")
}
Constant::Predefined(predefined_constant) => {
let const_str = match predefined_constant {
crate::ast::PredefinedConstant::False => "false",
crate::ast::PredefinedConstant::True => "true",
crate::ast::PredefinedConstant::Nullptr => "nullptr",
};
pp.text(const_str)
}
}
}
fn print_punctuator<'a, R: Render>(pp: &mut Printer<'a, R>, p: &'a Punctuator) -> Result<(), R::Error> {
let text = match p {
Punctuator::LeftBracket => "[",
Punctuator::RightBracket => "]",
Punctuator::LeftParen => "(",
Punctuator::RightParen => ")",
Punctuator::LeftBrace => "{",
Punctuator::RightBrace => "}",
Punctuator::Dot => ".",
Punctuator::Arrow => "->",
Punctuator::Increment => "++",
Punctuator::Decrement => "--",
Punctuator::Ampersand => "&",
Punctuator::Star => "*",
Punctuator::Plus => "+",
Punctuator::Minus => "-",
Punctuator::Tilde => "~",
Punctuator::Bang => "!",
Punctuator::Slash => "/",
Punctuator::Percent => "%",
Punctuator::LeftShift => "<<",
Punctuator::RightShift => ">>",
Punctuator::Less => "<",
Punctuator::Greater => ">",
Punctuator::LessEqual => "<=",
Punctuator::GreaterEqual => ">=",
Punctuator::Equal => "==",
Punctuator::NotEqual => "!=",
Punctuator::Caret => "^",
Punctuator::Pipe => "|",
Punctuator::LogicalAnd => "&&",
Punctuator::LogicalOr => "||",
Punctuator::Question => "?",
Punctuator::Colon => ":",
Punctuator::Scope => "::",
Punctuator::Semicolon => ";",
Punctuator::Ellipsis => "...",
Punctuator::Assign => "=",
Punctuator::MulAssign => "*=",
Punctuator::DivAssign => "/=",
Punctuator::ModAssign => "%=",
Punctuator::AddAssign => "+=",
Punctuator::SubAssign => "-=",
Punctuator::LeftShiftAssign => "<<=",
Punctuator::RightShiftAssign => ">>=",
Punctuator::AndAssign => "&=",
Punctuator::XorAssign => "^=",
Punctuator::OrAssign => "|=",
Punctuator::Comma => ",",
Punctuator::Hash => "#",
Punctuator::HashHash => "##",
};
pp.text(text)
}