Skip to content
This repository has been archived by the owner on Jun 3, 2021. It is now read-only.

Pretty Printing 2: Electric Boogaloo #407

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/analyze/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1374,12 +1374,12 @@ pub(crate) mod test {
analyze(s, Parser::expr, Analyzer::parse_expr)
}

fn assert_decl_display(left: &str, right: &str) {
pub(crate) fn assert_decl_display(left: &str, right: &str) {
assert_eq!(decl(left).unwrap().to_string(), right);
}
fn assert_extern_decl_display(s: &str) {
// TODO: this `auto` is such a hack
assert_decl_display(s, &format!("auto {}", s));
assert_decl_display(s, &format!("{}", s));
}

pub(super) fn assert_same(left: &str, right: &str) {
Expand Down
4 changes: 3 additions & 1 deletion src/data/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,9 @@ impl Display for Metadata {
if self.qualifiers != Qualifiers::default() {
write!(f, "{} ", self.qualifiers)?;
}
write!(f, "{} ", self.storage_class)?;
if self.storage_class != StorageClass::default() {
write!(f, "{} ", self.storage_class)?;
}
pythongirl325 marked this conversation as resolved.
Show resolved Hide resolved
super::types::print_type(&self.ctype, Some(self.id), f)
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ mod tests {
let types = [
"int",
"int *",
"int[1][2][3]",
"char * (*)(float)",
"short * (*)[1][2][3]",
"int [1][2][3]",
"char *(*)(float)",
"short *(*)[1][2][3]",
pythongirl325 marked this conversation as resolved.
Show resolved Hide resolved
"_Bool",
"struct s",
];
Expand Down
249 changes: 156 additions & 93 deletions src/data/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,118 +276,163 @@ impl std::fmt::Display for Type {
}
}

fn write_struct_type(struct_type: &StructType, f: &mut Formatter) -> fmt::Result {
match struct_type {
StructType::Named(name, _) => {
write!(f, "{}", name)?;
}
StructType::Anonymous(members) => {
writeln!(f, "{{")?;
for member in members.iter() {
writeln!(f, " {};", member)?;
}
write!(f, "}}")?;
}
}
Ok(())
}

pub(super) fn print_type(
ctype: &Type,
name: Option<InternedStr>,
f: &mut Formatter,
) -> fmt::Result {
print_pre(ctype, f)?;
print_mid(ctype, name, f)?;
print_post(ctype, f)
}

fn print_pre(ctype: &Type, f: &mut Formatter) -> fmt::Result {
use Type::*;
match ctype {
Char(signed) | Short(signed) | Int(signed) | Long(signed) => {
let lower = &format!("{:?}", ctype).to_lowercase();
let substr = match lower.find('(') {
Some(n) => &lower[..n],
None => lower.as_str(),
fn unroll_type(ctype: &Type) -> Vec<&Type> {
let mut types = Vec::new();
let mut next_type = ctype;
loop {
types.push(next_type);
next_type = match next_type {
Type::Array(of, _) => of.as_ref(),
Type::Pointer(to, _) => to.as_ref(),
Type::Function(FunctionType { return_type, .. }) => return_type.as_ref(),
_ => break,
};
write!(f, "{}{}", if *signed { "" } else { "unsigned " }, substr)
}
Bool => write!(f, "_Bool"),
Float | Double | Void => write!(f, "{}", format!("{:?}", ctype).to_lowercase()),
Pointer(inner, _) | Array(inner, _) => print_pre(inner, f),
Function(ftype) => print_type(&ftype.return_type, None, f),
Enum(Some(ident), _) => write!(f, "enum {}", ident),
Enum(None, _) => write!(f, "<anonymous enum>"),
Union(StructType::Named(ident, _)) => write!(f, "union {}", ident),
Union(_) => write!(f, "<anonymous union>"),
Struct(StructType::Named(ident, _)) => write!(f, "struct {}", ident),
Struct(_) => write!(f, "<anonymous struct>"),
VaList => write!(f, "va_list"),
Error => write!(f, "<type error>"),
types
}
}

fn print_mid(ctype: &Type, name: Option<InternedStr>, f: &mut Formatter) -> fmt::Result {
match ctype {
Type::Pointer(to, qs) => {
let name = name.unwrap_or_default();
// what do we do for (**p)()?
// we have to look arbitrarily deep into the type,
// but also we have to only print the ( once,
// so how do we know we know if it's already been printed?
let depth = match &**to {
Type::Array(_, _) | Type::Function(_) => true,
_ => false,
};
print_mid(to, None, f)?;
use std::fmt::Write;
use Type::*;

let unrolled_type = unroll_type(ctype);

let mut prefixes = Vec::new();
let mut postfixes = Vec::new();

write!(f, " ")?;
if depth {
write!(f, "(")?;
// Need to skip the last item because that's the final type that needs to be
// put in as the specifier
for (index, declarator_type) in unrolled_type[..unrolled_type.len() - 1].iter().enumerate() {
pythongirl325 marked this conversation as resolved.
Show resolved Hide resolved
match declarator_type {
Array(_, array_type) => {
prefixes.push(String::new());
postfixes.push(match array_type {
ArrayType::Fixed(length) => format!("[{}]", length),
ArrayType::Unbounded => "[]".to_string(),
});
}
Type::Function(function_type) => {
prefixes.push(String::new());

let pointer = if qs != &Default::default() && name != InternedStr::default() {
format!("*{} {}", qs, name)
} else {
format!("*{}{}", qs, name)
};
write!(f, "{}", pointer)?;
if depth {
write!(f, ")")?;
let params = &function_type.params;
let mut buff = String::new();
write!(buff, "(")?;
for (index, symbol) in params.iter().enumerate() {
let symbol = symbol.get();
write!(buff, "{}", symbol)?;
if index != params.len() - 1 || function_type.varargs {
write!(buff, ", ")?;
}
}

if function_type.varargs {
write!(buff, "...")?;
}

write!(buff, ")")?;
postfixes.push(buff);
}
Ok(())
}
Type::Array(to, _) => print_mid(to, name, f),
_ => {
if let Some(name) = name {
write!(f, " {}", name)?;
Pointer(_, qs) => {
let needs_parens = match unrolled_type[index + 1] {
Array(_, _) | Function(_) => true,
_ => false,
};

prefixes.push(format!(
"{}*{}",
if needs_parens { "(" } else { "" },
if *qs != Default::default() {
format!("{} ", qs)
} else {
String::new()
}
));

if needs_parens {
postfixes.push(")".to_string());
} else {
postfixes.push(String::new());
}
}
Ok(())
_ => unreachable!(),
}
}
}
fn print_post(ctype: &Type, f: &mut Formatter) -> fmt::Result {
match ctype {
Type::Pointer(to, _) => print_post(to, f),
Type::Array(to, size) => {
write!(f, "[")?;
if let ArrayType::Fixed(size) = size {
write!(f, "{}", size)?;
}
write!(f, "]")?;
print_post(to, f)

let final_type = unrolled_type[unrolled_type.len() - 1];
pythongirl325 marked this conversation as resolved.
Show resolved Hide resolved
match final_type {
Char(signed) | Short(signed) | Int(signed) | Long(signed) => {
write!(
f,
"{}{}",
if *signed { "" } else { "unsigned " },
match final_type {
Char(_) => "char",
Short(_) => "short",
Int(_) => "int",
Long(_) => "long",
_ => unreachable!(),
}
)?;
}
Type::Function(func_type) => {
write!(f, "(")?;
let mut params = func_type.params.iter();
let print = |f: &mut _, symbol: MetadataRef| {
let symbol = symbol.get();
let id = if symbol.id == InternedStr::default() {
None
} else {
Some(symbol.id)
};
print_type(&symbol.ctype, id, f)
};
if let Some(&first) = params.next() {
print(f, first)?;
}
for &symbol in params {
write!(f, ", ")?;
print(f, symbol)?;
}
if func_type.varargs {
write!(f, ", ...")?;
}
write!(f, ")")
Bool => write!(f, "_Bool")?,
Float => write!(f, "float")?,
Double => write!(f, "double")?,
Void => write!(f, "void")?,
Enum(Some(ident), _) => write!(f, "enum {}", ident)?,
Enum(None, _) => write!(f, "<anonymous enum>")?,
pythongirl325 marked this conversation as resolved.
Show resolved Hide resolved
Union(struct_type) => {
write!(f, "union ")?;
write_struct_type(struct_type, f)?;
}
Struct(struct_type) => {
write!(f, "struct ")?;
write_struct_type(struct_type, f)?;
}
_ => Ok(()),
VaList => write!(f, "va_list")?,
Error => write!(f, "<type error>")?,
// These are unreachable because if they were part of the type, the
// would have been unrolled. Only specifier types are valid final types
// in the unrolling algorithm.
Array(_, _) | Pointer(_, _) | Function(_) => unreachable!(),
}

if unrolled_type.len() > 1 || name.unwrap_or_default() != InternedStr::default() {
write!(f, " ")?;
}

for prefix in prefixes.iter().rev() {
write!(f, "{}", prefix)?;
}

if let Some(name) = name {
write!(f, "{}", name)?;
}

for postfix in postfixes.iter() {
write!(f, "{}", postfix)?;
}

Ok(())
}

impl FunctionType {
Expand Down Expand Up @@ -431,4 +476,22 @@ pub(crate) mod tests {
]
})
}

use crate::analyze::test::assert_decl_display;

#[test]
fn test_big_one() {
assert_decl_display("struct { int i; } S;", "struct {\n int i;\n} S;");
assert_decl_display("int f();", "int f();");
assert_decl_display("int bar;", "int bar;");
assert_decl_display("int *foo;", "int *foo;");
assert_decl_display("**const*volatile a;", "int **const *volatile a;");
assert_decl_display("int (*a[])();", "int (*a[])();");
assert_decl_display("int (*(*f))(int);", "int (**f)(int);");
assert_decl_display(
"int *(*jynelson)(int (*)(int));",
"int *(*jynelson)(int (*)(int));",
);
assert_decl_display("int f(...);", "int f(...);");
pythongirl325 marked this conversation as resolved.
Show resolved Hide resolved
}
}