use rucc_base::{Interner, Symbol};
use crate::kind::{ArrayLen, FunctionId, Qualifiers, Type, TypeKind};
use crate::types::{TypeId, Types};
#[must_use]
pub fn spell(types: &Types, names: &Interner, id: TypeId) -> String {
Speller { types, names }.declaration(id, Declarator::nothing())
}
#[must_use]
pub fn declare(types: &Types, names: &Interner, id: TypeId, name: Symbol) -> String {
Speller { types, names }.declaration(id, Declarator::of(names.resolve(name).to_owned()))
}
#[derive(Debug)]
struct Declarator {
text: String,
glued: bool,
}
impl Declarator {
fn nothing() -> Declarator {
Declarator { text: String::new(), glued: false }
}
fn of(text: String) -> Declarator {
Declarator { text, glued: false }
}
fn suffixed(self, suffix: &str) -> Declarator {
let glued = self.glued || self.text.is_empty();
Declarator { text: self.text + suffix, glued }
}
}
#[derive(Debug)]
struct Speller<'a> {
types: &'a Types,
names: &'a Interner,
}
impl Speller<'_> {
fn declaration(&self, id: TypeId, inner: Declarator) -> String {
let ty = self.types.get(id);
let base = match ty.kind {
TypeKind::Pointer(pointee) => return self.pointer(ty, pointee, inner),
TypeKind::Array { elem, len } => return self.array(ty, elem, len, inner),
TypeKind::Function(function) => return self.function(function, inner),
TypeKind::Void => String::from("void"),
TypeKind::Bool => String::from("_Bool"),
TypeKind::Int(kind) => String::from(kind.as_str()),
TypeKind::Float(kind) => String::from(kind.as_str()),
TypeKind::Complex(kind) => format!("_Complex {}", kind.as_str()),
TypeKind::BitInt { signed, width } => {
let sign = if signed { "" } else { "unsigned " };
format!("{sign}_BitInt({width})")
}
TypeKind::Atomic(inner) => format!("_Atomic({})", self.spell(inner)),
TypeKind::Vector { elem, len } => format!("__vector({len}) {}", self.spell(elem)),
TypeKind::Record(record) => {
let info = self.types.record_info(record);
format!("{} {}", info.kind.as_str(), self.tag(info.tag))
}
TypeKind::Enum(enumeration) => {
let info = self.types.enum_info(enumeration);
format!("enum {}", self.tag(info.tag))
}
TypeKind::Typedef { name, .. } => self.names.resolve(name).to_owned(),
};
let mut out = String::new();
if let Some(quals) = quals_text(ty.quals) {
out.push_str(quals);
out.push(' ');
}
out.push_str(&base);
if !inner.text.is_empty() {
if !inner.glued {
out.push(' ');
}
out.push_str(&inner.text);
}
out
}
fn pointer(&self, ty: Type, pointee: TypeId, inner: Declarator) -> String {
let mut declarator = String::from("*");
if let Some(quals) = quals_text(ty.quals) {
declarator.push_str(quals);
if !inner.text.is_empty() {
declarator.push(' ');
}
}
declarator.push_str(&inner.text);
if matches!(self.types.kind(pointee), TypeKind::Array { .. } | TypeKind::Function(_)) {
declarator = format!("({declarator})");
}
self.declaration(pointee, Declarator::of(declarator))
}
fn array(&self, ty: Type, elem: TypeId, len: ArrayLen, inner: Declarator) -> String {
let mut suffix = String::from("[");
if let Some(quals) = quals_text(ty.quals) {
suffix.push_str(quals);
if !matches!(len, ArrayLen::Unknown) {
suffix.push(' ');
}
}
match len {
ArrayLen::Fixed(count) => suffix.push_str(&count.to_string()),
ArrayLen::Unknown => {}
ArrayLen::Star | ArrayLen::Variable(_) => suffix.push('*'),
}
suffix.push(']');
self.declaration(elem, inner.suffixed(&suffix))
}
fn function(&self, function: FunctionId, inner: Declarator) -> String {
let signature = self.types.signature(function);
let mut suffix = String::from("(");
for (index, ¶m) in signature.params.iter().enumerate() {
if index > 0 {
suffix.push_str(", ");
}
suffix.push_str(&self.spell(param));
}
if signature.variadic {
if !signature.params.is_empty() {
suffix.push_str(", ");
}
suffix.push_str("...");
} else if signature.params.is_empty() && signature.prototyped {
suffix.push_str("void");
}
suffix.push(')');
self.declaration(signature.ret, inner.suffixed(&suffix))
}
fn spell(&self, id: TypeId) -> String {
self.declaration(id, Declarator::nothing())
}
fn tag(&self, tag: Option<Symbol>) -> String {
match tag {
Some(name) => self.names.resolve(name).to_owned(),
None => String::from("<anonymous>"),
}
}
}
fn quals_text(quals: Qualifiers) -> Option<&'static str> {
match (
quals.has(Qualifiers::CONST),
quals.has(Qualifiers::VOLATILE),
quals.has(Qualifiers::RESTRICT),
) {
(false, false, false) => None,
(true, false, false) => Some("const"),
(false, true, false) => Some("volatile"),
(false, false, true) => Some("restrict"),
(true, true, false) => Some("const volatile"),
(true, false, true) => Some("const restrict"),
(false, true, true) => Some("volatile restrict"),
(true, true, true) => Some("const volatile restrict"),
}
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use super::*;
use crate::kind::{ArrayLen, FloatKind, FunctionType, IntKind, RecordKind};
fn fixture() -> (Types, Interner) {
(Types::new(), Interner::new())
}
#[test]
fn a_basic_type_is_its_keywords() {
let (types, names) = fixture();
let int = types.int(IntKind::Int);
assert_eq!(spell(&types, &names, int), "int");
assert_eq!(spell(&types, &names, types.void()), "void");
assert_eq!(spell(&types, &names, types.boolean()), "_Bool");
let long_double = types.float(FloatKind::LongDouble);
assert_eq!(spell(&types, &names, long_double), "long double");
}
#[test]
fn a_qualifier_goes_in_front_of_what_it_qualifies() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let qualified = types.qualified(int, Qualifiers::CONST.with(Qualifiers::VOLATILE));
assert_eq!(spell(&types, &names, qualified), "const volatile int");
}
#[test]
fn a_pointers_own_qualifier_goes_after_the_star() {
let (mut types, names) = fixture();
let char_type = types.int(IntKind::Char);
let constant = types.qualified(char_type, Qualifiers::CONST);
let pointer = types.pointer(constant);
let constant_pointer = types.qualified(pointer, Qualifiers::CONST);
assert_eq!(spell(&types, &names, constant_pointer), "const char *const");
}
#[test]
fn a_qualified_pointer_with_a_name_keeps_them_apart() {
let (mut types, mut names) = fixture();
let int = types.int(IntKind::Int);
let pointer = types.pointer(int);
let restricted = types.qualified(pointer, Qualifiers::RESTRICT);
let p = names.intern("p");
assert_eq!(declare(&types, &names, restricted, p), "int *restrict p");
}
#[test]
fn a_declarator_is_written_around_the_name() {
let (mut types, mut names) = fixture();
let int = types.int(IntKind::Int);
let char_type = types.int(IntKind::Char);
let signature =
FunctionType { ret: int, params: vec![char_type], variadic: false, prototyped: true };
let function = types.function(signature);
let pointer = types.pointer(function);
let array = types.array(pointer, ArrayLen::Fixed(3));
let f = names.intern("f");
assert_eq!(declare(&types, &names, array, f), "int (*f[3])(char)");
}
#[test]
fn an_abstract_declarator_keeps_the_parentheses_the_name_would_have_needed() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let array = types.array(int, ArrayLen::Fixed(3));
let pointer = types.pointer(array);
assert_eq!(spell(&types, &names, pointer), "int (*)[3]");
}
#[test]
fn a_suffix_with_nothing_in_front_of_it_goes_against_the_type() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let array = types.array(int, ArrayLen::Fixed(4));
let nested = types.array(array, ArrayLen::Fixed(2));
let takes_an_int =
FunctionType { ret: int, params: vec![int], variadic: false, prototyped: true };
let function = types.function(takes_an_int.clone());
let to_int = types.pointer(int);
let gives_a_pointer = types.function(FunctionType { ret: to_int, ..takes_an_int });
let to_array = types.pointer(array);
assert_eq!(spell(&types, &names, array), "int[4]");
assert_eq!(spell(&types, &names, nested), "int[2][4]");
assert_eq!(spell(&types, &names, function), "int(int)");
assert_eq!(spell(&types, &names, gives_a_pointer), "int *(int)");
assert_eq!(spell(&types, &names, to_array), "int (*)[4]");
}
#[test]
fn an_array_of_arrays_reads_left_to_right() {
let (mut types, mut names) = fixture();
let int = types.int(IntKind::Int);
let inner = types.array(int, ArrayLen::Fixed(3));
let outer = types.array(inner, ArrayLen::Fixed(2));
let a = names.intern("a");
assert_eq!(declare(&types, &names, outer, a), "int a[2][3]");
}
#[test]
fn an_array_without_a_size_says_so_and_a_variable_one_says_only_that_it_has_one() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let unknown = types.array(int, ArrayLen::Unknown);
let variable = types.array(int, ArrayLen::Variable(crate::kind::VlaId(0)));
assert_eq!(spell(&types, &names, unknown), "int[]");
assert_eq!(spell(&types, &names, variable), "int[*]");
}
#[test]
fn a_prototype_with_no_parameters_is_not_a_function_without_one() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let prototyped =
FunctionType { ret: int, params: Vec::new(), variadic: false, prototyped: true };
let old = FunctionType { ret: int, params: Vec::new(), variadic: false, prototyped: false };
let prototyped = types.function(prototyped);
let old = types.function(old);
let prototyped = types.pointer(prototyped);
let old = types.pointer(old);
assert_eq!(spell(&types, &names, prototyped), "int (*)(void)");
assert_eq!(spell(&types, &names, old), "int (*)()");
}
#[test]
fn a_variadic_function_ends_in_the_ellipsis() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let char_type = types.int(IntKind::Char);
let signature =
FunctionType { ret: int, params: vec![char_type], variadic: true, prototyped: true };
let function = types.function(signature);
let pointer = types.pointer(function);
assert_eq!(spell(&types, &names, pointer), "int (*)(char, ...)");
}
#[test]
fn a_typedef_is_spelled_as_itself_and_its_canonical_form_as_what_it_stands_for() {
let (mut types, mut names) = fixture();
let ulong = types.int(IntKind::ULong);
let name = names.intern("size_t");
let size_t = types.typedef(name, ulong);
let pointer = types.pointer(size_t);
assert_eq!(spell(&types, &names, pointer), "size_t *");
let canonical = types.canonical(pointer);
assert_eq!(spell(&types, &names, canonical), "unsigned long *");
}
#[test]
fn a_tag_that_was_never_written_is_named_the_way_gcc_names_it() {
let (mut types, mut names) = fixture();
let tag = names.intern("S");
let named = types.declare_record(RecordKind::Struct, Some(tag));
let unnamed = types.declare_record(RecordKind::Union, None);
let named = types.record(named);
let unnamed = types.record(unnamed);
assert_eq!(spell(&types, &names, named), "struct S");
assert_eq!(spell(&types, &names, unnamed), "union <anonymous>");
}
#[test]
fn an_atomic_type_is_written_as_the_type_it_is() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let atomic = types.atomic(int);
let pointer = types.pointer(atomic);
assert_eq!(spell(&types, &names, atomic), "_Atomic(int)");
assert_eq!(spell(&types, &names, pointer), "_Atomic(int) *");
}
#[test]
fn a_vector_is_written_the_way_gcc_writes_one() {
let (mut types, names) = fixture();
let int = types.int(IntKind::Int);
let vector = types.vector(int, 4);
assert_eq!(spell(&types, &names, vector), "__vector(4) int");
}
}