Skip to content

Commit

Permalink
Improve type aliases for enum/struct/union decls
Browse files Browse the repository at this point in the history
  • Loading branch information
dkorpel committed Aug 15, 2023
1 parent d60f609 commit 736ceb5
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 112 deletions.
64 changes: 43 additions & 21 deletions source/ctod/cdeclaration.d
Original file line number Diff line number Diff line change
Expand Up @@ -75,33 +75,55 @@ Decl[] ctodTryDeclaration(ref CtodCtx ctx, ref Node node) {
return translateDecl(";", true);
case Sym.declaration: // global / local variable
return translateDecl(";", true);
case Sym.type_definition:
Decl[] decls = parseDecls(ctx, node, inlinetypes);
string result = "";
foreach(s; inlinetypes) {
result ~= s.toString();
}
bool first = true;
foreach(d; decls) {
if (d.type == CType.named(d.identifier)) {
// result ~= "/*alias " ~ d.toString() ~ ";*/";
} else {
if (first) {
first = false;
} else {
result ~= "\n";
}
result ~= "alias " ~ d.identifier ~ " = " ~ d.type.toString() ~ ";";
}
}
node.replace(result);
return decls;
default:
break;
}
return null;
}

bool ctodTryTypedef(ref CtodCtx ctx, ref Node node) {
InlineType[] inlinetypes;
if (node.typeEnum != Sym.type_definition) {
return false;
}
Decl[] decls = parseDecls(ctx, node, inlinetypes);
string result = "";

// It's very common to typedef an anonymous type with a single name:
// `typedef struct {...} X`
// No need to give temporary name `_X` and create `alias X = _X`,
// just pretend the type was named `X` all along
if (inlinetypes.length == 1 && decls.length == 1) {
if (decls[0].type == CType.named(inlinetypes[0].name)) {
inlinetypes[0].name = decls[0].identifier;
decls = null;
}
}

foreach(s; inlinetypes) {
result ~= s.toString();
if (s.node) {
// Put enum members into the global scope with aliases
node.append(enumMemberAliases(s.name, *s.node));
}
}
bool first = true;
foreach(d; decls) {
if (d.type == CType.named(d.identifier)) {
// result ~= "/*alias " ~ d.toString() ~ ";*/";
} else {
if (first) {
first = false;
} else {
result ~= "\n";
}
result ~= "alias " ~ d.identifier ~ " = " ~ d.type.toString() ~ ";";
}
}
node.replace(result);
return true;
}

/// Try translating variable initializers
/// Returns: true if translation is done, no need to translate children
bool ctodTryInitializer(ref CtodCtx ctx, ref Node node) {
Expand Down
6 changes: 3 additions & 3 deletions source/ctod/cexpr.d
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ bool ctodExpression(ref CtodCtx ctx, ref Node node) {
break;
case Sym.conditional_expression:
depthFirst();
// condition:
// consequence:
// alternative:
// condition:
// consequence:
// alternative:
break;
case Sym.pointer_expression:
depthFirst();
Expand Down
127 changes: 68 additions & 59 deletions source/ctod/ctype.d
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ struct InlineType {
string keyword;
string name = null;
string body_;
string enumAliases = null;
Node* node; // body node

pure nothrow:

Expand All @@ -93,7 +93,7 @@ pure nothrow:

/// Generate alias declarations to put enum members into the global namespace
/// C enums don't have a scope for their members
private string enumMemberAliases(string enumName, ref Node c) {
string enumMemberAliases(string enumName, ref Node c) {
if (c.typeEnum != Sym.enumerator_list) {
return null;
}
Expand All @@ -107,32 +107,34 @@ private string enumMemberAliases(string enumName, ref Node c) {
return res;
}

private string typeSymToKeyword(Sym sym) {
switch(sym) {
case Sym.struct_specifier: return "struct";
case Sym.union_specifier: return "union";
case Sym.enum_specifier: return "enum";
default: return null;
}
}

/// C declarations are declared this way:
/// First, a 'base type' which is a primitive type or identifier
/// Then, one or more expressions that should evaluate to a value of 'base type'
///
/// Note: the base-type can be an (anonymous) struct / union / enum, which is not allowed in D.
/// Therefor, the second return value is a type declaration that the primitive type depends on.
/// These are stored in `inlineTypes`, and the caller should emit these
///
/// Returns: [primitive type, dependent type]
/// Returns: primitive type
string parseTypeNode(ref CtodCtx ctx, ref Node node, ref InlineType[] inlineTypes, bool keepOpaque) {

// keyword = struct, union or enum
string namedType(string keyword, Sym sym) {
string namedType(Sym sym) {
auto nameNode = node.childField(Field.name);
if (auto c = node.childField(Field.body_)) {
ctx.pushTypeScope(sym);
translateNode(ctx, *c);
ctx.popTypeScope();
string name = nameNode ? nameNode.source : null;

// Put enum members into the global scope with aliases
string enumAliases = enumMemberAliases(name, *c);

if (name && enumAliases)
c.append(enumAliases);

inlineTypes ~= InlineType(keyword, name, c.output(), enumAliases);
inlineTypes ~= InlineType(typeSymToKeyword(sym), name, c.output(), c);
return name;
} else if (nameNode) {
if (keepOpaque) {
Expand Down Expand Up @@ -173,57 +175,64 @@ string parseTypeNode(ref CtodCtx ctx, ref Node node, ref InlineType[] inlineType
}
}
case Sym.sized_type_specifier:
bool signed = true;
int longCount = 0;
string primitive = "";
foreach(ref c; node.children) {
switch(c.typeEnum) {
case Sym.comment: continue;
case Sym.anon_unsigned:
signed = false;
break;
case Sym.anon_long:
longCount++;
break;
case Sym.primitive_type:
primitive = ctodPrimitiveType(c.source);
break;
case Sym.anon_short: // not a primitive_type apparently, but similar to `unsigned`
primitive = "short";
break;
default: break;
}
}

if (longCount > 0 && primitive == "double") {
primitive = "real";
} else if (longCount == 1 && primitive == "") {
primitive = "c_long";
ctx.needsClong = true;
} else if (longCount == 2 && primitive == "") {
primitive = "long";
} else if (!signed && primitive == "") {
primitive = "int";
}

if (!signed && primitive.length && primitive[0] != 'u') {
if (primitive == "char") {
primitive = "ubyte";
} else if (primitive == "c_long") {
primitive = "c_ulong";
} else {
primitive = "u" ~ primitive;
}
}
return primitive;
case Sym.struct_specifier: return namedType("struct", node.typeEnum);
case Sym.union_specifier: return namedType("union", node.typeEnum);
case Sym.enum_specifier: return namedType("enum", node.typeEnum);
return ctodSizedTypeSpecifier(ctx, node);
case Sym.struct_specifier:
case Sym.union_specifier:
case Sym.enum_specifier:
return namedType(node.typeEnum);
default: break;
}
return null;
}

/// Translate built-in integral types (int, long, short, char, etc.)
string ctodSizedTypeSpecifier(ref CtodCtx ctx, ref Node node) {
bool signed = true;
int longCount = 0;
string primitive = "";
foreach(ref c; node.children) {
switch(c.typeEnum) {
case Sym.comment:
continue;
case Sym.anon_unsigned:
signed = false;
break;
case Sym.anon_long:
longCount++;
break;
case Sym.primitive_type:
primitive = ctodPrimitiveType(c.source);
break;
case Sym.anon_short: // not a primitive_type apparently, but similar to `unsigned`
primitive = "short";
break;
default: break;
}
}

if (longCount > 0 && primitive == "double") {
primitive = "real";
} else if (longCount == 1 && primitive == "") {
primitive = "c_long";
ctx.needsClong = true;
} else if (longCount == 2 && primitive == "") {
primitive = "long";
} else if (!signed && primitive == "") {
primitive = "int";
}

if (!signed && primitive.length > 0 && primitive[0] != 'u') {
if (primitive == "char") {
primitive = "ubyte";
} else if (primitive == "c_long") {
primitive = "c_ulong";
} else {
primitive = "u" ~ primitive;
}
}
return primitive;
}

/// Qualifiers for a C declaration
struct CQuals {
bool const_;
Expand Down
17 changes: 11 additions & 6 deletions source/ctod/test.d
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ struct S {
test("
typedef union
{
uint u;
float f;
uint u;
float f;
} FP32;
", "
union _FP32 {
uint_ u;
float f;
}alias FP32 = _FP32;
union FP32 {
uint_ u;
float f;
}
");

test("union U { float f, x; };", "union U { float f = 0, x; }");
Expand Down Expand Up @@ -234,6 +234,7 @@ void main() {

test("struct OpaqueS;", "struct OpaqueS;");
test("union OpaqueU;", "union OpaqueU;");
test("enum OpaqueE;", "enum OpaqueE;");

test("
struct S {
Expand Down Expand Up @@ -343,6 +344,10 @@ alias two = AnEnum.two;
");

test("typedef enum { a, b } Foo;", "enum Foo { a, b }\nalias a = Foo.a;\nalias b = Foo.b;\n");

test("typedef enum { a } *Foo;", "enum _Foo { a }alias Foo = _Foo*;\nalias a = _Foo.a;\n");

test("
typedef enum AnEnum
{
Expand Down
Loading

0 comments on commit 736ceb5

Please sign in to comment.