Skip to content

Commit

Permalink
Initial Dlang support
Browse files Browse the repository at this point in the history
closes: #866

* enable c++ compat
* fix struct/enum/union (semicolon)
* fix casts
* C++ namespace  support
* typedefs to alias
* attributes added (`@nogc`, `nothrow`, `@safe`) scope
* D file on tests enabled
* D generic instaciation (`Foo!(T, U)`, similar to `Foo<T, U>`)
* disable enum typedef (like C++)
* add struct-literals
* build all tests/expectations
  • Loading branch information
kassane committed Dec 15, 2024
1 parent b9b8f88 commit 8e1ac09
Show file tree
Hide file tree
Showing 159 changed files with 4,238 additions and 53 deletions.
2 changes: 1 addition & 1 deletion src/bindgen/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl Bindings {

pub fn write<F: Write>(&self, file: F) {
match self.config.language {
Language::Cxx | Language::C => {
Language::Cxx | Language::C | Language::D => {
self.write_with_backend(file, &mut CLikeLanguageBackend::new(&self.config))
}
Language::Cython => {
Expand Down
63 changes: 54 additions & 9 deletions src/bindgen/cdecl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,11 @@ impl CDecl {
"error generating cdecl for {:?}",
t
);
self.type_name = p.to_repr_c(config).to_string();
if config.language == Language::D {
self.type_name = p.to_repr_d(config).to_string();
} else {
self.type_name = p.to_repr_c(config).to_string();
}
}
Type::Ptr {
ref ty,
Expand Down Expand Up @@ -213,7 +217,11 @@ impl CDecl {
write!(out, "{}", self.type_name);

if !self.type_generic_args.is_empty() {
out.write("<");
if config.language == Language::D {
out.write("!(");
} else {
out.write("<");
}
out.write_horizontal_source_list(
language_backend,
&self.type_generic_args,
Expand All @@ -223,7 +231,11 @@ impl CDecl {
GenericArgument::Const(ref expr) => write!(out, "{}", expr.as_str()),
},
);
out.write(">");
if config.language == Language::D {
out.write(")");
} else {
out.write(">");
}
}

// When we have an identifier, put a space between the type and the declarators
Expand All @@ -233,6 +245,7 @@ impl CDecl {

// Write the left part of declarators before the identifier
let mut iter_rev = self.declarators.iter().rev().peekable();
let mut is_functors = false;

#[allow(clippy::while_let_on_iterator)]
while let Some(declarator) = iter_rev.next() {
Expand All @@ -244,7 +257,17 @@ impl CDecl {
is_nullable,
is_ref,
} => {
out.write(if is_ref { "&" } else { "*" });
if config.language == Language::D {
// out.write(if is_ref { "ref " } else { "*" });
if is_ref {
out.write("ref ");
} else if is_functors {
} else {
out.write("*");
}
} else {
out.write(if is_ref { "&" } else { "*" });
}
if is_const {
out.write("const ");
}
Expand All @@ -254,22 +277,34 @@ impl CDecl {
}
}
}
CDeclarator::Array(..) => {
CDeclarator::Array(ref constant) => {
if next_is_pointer {
out.write("(");
}
if config.language == Language::D {
write!(out, "[{}] ", constant);
}
}
CDeclarator::Func { .. } => {
if next_is_pointer {
out.write("(");
if config.language == Language::D {
out.write(" function");
is_functors = true;
} else {
out.write("(");
}
}
}
}
}

// Write the identifier
if let Some(ident) = ident {
write!(out, "{}", ident);
if is_functors {
// out.write(" ");
} else {
write!(out, "{}", ident);
}
}

// Write the right part of declarators after the identifier
Expand All @@ -286,7 +321,9 @@ impl CDecl {
if last_was_pointer {
out.write(")");
}
write!(out, "[{}]", constant);
if config.language != Language::D {
write!(out, "[{}]", constant);
}

last_was_pointer = false;
}
Expand All @@ -296,7 +333,9 @@ impl CDecl {
never_return,
} => {
if last_was_pointer {
out.write(")");
if config.language != Language::D {
out.write(")");
}
}

out.write("(");
Expand Down Expand Up @@ -363,6 +402,12 @@ impl CDecl {
out.write_fmt(format_args!(" {}", no_return_attr));
}
}
if config.language == Language::D && is_functors {
if let Some(ident) = ident {
write!(out, " {}", ident);
}
}
is_functors = false;

last_was_pointer = true;
}
Expand Down
7 changes: 7 additions & 0 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum Language {
Cxx,
C,
Cython,
D,
}

impl FromStr for Language {
Expand All @@ -42,6 +43,9 @@ impl FromStr for Language {
"C" => Ok(Language::C),
"cython" => Ok(Language::Cython),
"Cython" => Ok(Language::Cython),
"d" => Ok(Language::D),
"dlang" => Ok(Language::D),
"D" => Ok(Language::D),
_ => Err(format!("Unrecognized Language: '{}'.", s)),
}
}
Expand All @@ -54,6 +58,7 @@ impl Language {
match self {
Language::Cxx | Language::C => "typedef",
Language::Cython => "ctypedef",
Language::D => "alias",
}
}
}
Expand Down Expand Up @@ -166,6 +171,7 @@ pub enum DocumentationStyle {
C99,
Doxy,
Cxx,
D,
Auto,
}

Expand All @@ -179,6 +185,7 @@ impl FromStr for DocumentationStyle {
"cxx" => Ok(DocumentationStyle::Cxx),
"c++" => Ok(DocumentationStyle::Cxx),
"doxy" => Ok(DocumentationStyle::Doxy),
"dlang" => Ok(DocumentationStyle::D),
"auto" => Ok(DocumentationStyle::Auto),
_ => Err(format!("Unrecognized documentation style: '{}'.", s)),
}
Expand Down
6 changes: 6 additions & 0 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ impl Constant {
) {
debug_assert!(self.associated_to.is_some());
debug_assert!(config.language == Language::Cxx);
debug_assert!(config.language == Language::D);
debug_assert!(!associated_to_struct.is_transparent);
debug_assert!(config.structure.associated_constants_in_body);
debug_assert!(config.constant.allow_static_const);
Expand Down Expand Up @@ -721,6 +722,11 @@ impl Constant {
write!(out, " {} # = ", name);
language_backend.write_literal(out, value);
}
Language::D => {
write!(out, "enum {} = ", name);
language_backend.write_literal(out, value);
out.write(";");
}
}

condition.write_after(config, out);
Expand Down
31 changes: 28 additions & 3 deletions src/bindgen/ir/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,27 @@ impl Enum {
write!(out, "{}enum {}", config.style.cython_def(), tag_name);
}
}
Language::D => {
out.write("enum");

if self.annotations.must_use(config) {
if let Some(ref anno) = config.enumeration.must_use {
write!(out, " {}", anno)
}
}

if let Some(note) = self
.annotations
.deprecated_note(config, DeprecatedNoteKind::Enum)
{
write!(out, " {}", note);
}

write!(out, " {}", tag_name);
if let Some(prim) = size {
write!(out, " : {}", prim);
}
}
}
out.open_brace();

Expand All @@ -748,7 +769,11 @@ impl Enum {
out.close_brace(false);
write!(out, " {};", tag_name);
} else {
out.close_brace(true);
if config.language != Language::D {
out.close_brace(true);
} else {
out.close_brace(false);
}
}

// Emit typedef specifying the tag enum's size if necessary.
Expand All @@ -760,7 +785,7 @@ impl Enum {
out.write("#ifndef __cplusplus");
}

if config.language != Language::Cxx {
if config.language != Language::Cxx && config.language != Language::D {
out.new_line();
write!(out, "{} {} {};", config.language.typedef(), prim, tag_name);
}
Expand All @@ -784,7 +809,7 @@ impl Enum {
) {
match config.language {
Language::C if config.style.generate_typedef() => out.write("typedef "),
Language::C | Language::Cxx => {}
Language::C | Language::Cxx | Language::D => {}
Language::Cython => out.write(config.style.cython_def()),
}

Expand Down
24 changes: 19 additions & 5 deletions src/bindgen/ir/generic_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,23 @@ impl GenericParams {
out: &mut SourceWriter<F>,
with_default: bool,
) {
if !self.0.is_empty() && config.language == Language::Cxx {
out.write("template<");
if (!self.0.is_empty() && config.language == Language::Cxx) || (!self.0.is_empty() && config.language == Language::D) {
out.write(if config.language == Language::D {
"("
} else {
"template<"
});
for (i, item) in self.0.iter().enumerate() {
if i != 0 {
out.write(", ");
}
match item.ty {
GenericParamType::Type => {
write!(out, "typename {}", item.name);
if config.language == Language::D {
write!(out, "{}", item.name);
} else {
write!(out, "typename {}", item.name);
}
if let Some(GenericArgument::Type(ref ty)) = item.default {
write!(out, " = ");
cdecl::write_type(language_backend, out, ty, config);
Expand All @@ -171,8 +179,14 @@ impl GenericParams {
}
}
}
out.write(">");
out.new_line();
out.write(if config.language == Language::D {
")"
} else {
">"
});
if config.language != Language::D {
out.new_line();
}
}
}

Expand Down
51 changes: 51 additions & 0 deletions src/bindgen/ir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,57 @@ impl PrimitiveType {
}
}

pub fn to_repr_d(&self, config: &Config) -> &'static str {
match *self {
PrimitiveType::Void => "void",
PrimitiveType::Bool => "bool",
PrimitiveType::Char => "char",
PrimitiveType::SChar => "byte",
PrimitiveType::UChar => "ubyte",
// NOTE: It'd be nice to use a char32_t, but:
//
// * uchar.h is not present on mac (see #423).
//
// * char32_t isn't required to be compatible with Rust's char, as
// the C++ spec only requires it to be the same size as
// uint_least32_t, which is _not_ guaranteed to be 4-bytes.
//
PrimitiveType::Char32 => "uint",
PrimitiveType::Integer {
kind,
signed,
zeroable: _,
} => match (kind, signed) {
(IntKind::Short, true) => "short",
(IntKind::Short, false) => "ushort",
(IntKind::Int, true) => "int",
(IntKind::Int, false) => "uint",
(IntKind::Long, true) => "long",
(IntKind::Long, false) => "ulong",
(IntKind::LongLong, true) => "long long",
(IntKind::LongLong, false) => "ulong long",
(IntKind::SizeT, true) => "long",
(IntKind::SizeT, false) => "ulong",
(IntKind::Size, true) if config.usize_is_size_t => "long",
(IntKind::Size, false) if config.usize_is_size_t => "ulong",
(IntKind::Size, true) => "long",
(IntKind::Size, false) => "ulong",
(IntKind::B8, true) => "byte",
(IntKind::B8, false) => "ubyte",
(IntKind::B16, true) => "short",
(IntKind::B16, false) => "ushort",
(IntKind::B32, true) => "int",
(IntKind::B32, false) => "uint",
(IntKind::B64, true) => "long",
(IntKind::B64, false) => "ulong",
},
PrimitiveType::Float => "float",
PrimitiveType::Double => "double",
PrimitiveType::PtrDiffT => "long",
PrimitiveType::VaList => "...",
}
}

fn can_cmp_order(&self) -> bool {
!matches!(*self, PrimitiveType::Bool)
}
Expand Down
Loading

0 comments on commit 8e1ac09

Please sign in to comment.