Files
Cpp2Beef/src/Generator.bf
2026-03-06 18:41:15 +01:00

1577 lines
46 KiB
Beef

using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using LibClang;
namespace Cpp2Beef;
abstract class Cpp2BeefGenerator
{
protected abstract Span<char8*> Args { get; }
protected abstract StreamWriter GetWriterForHeader(StringView header);
protected abstract Flags Flags { get; }
public enum Flags
{
None = 0,
PreseveColumns = 1,
}
protected virtual void GetNameInBindings(CXCursor cursor, String outString)
{
let spelling = GetCursorSpelling!(cursor);
getName: switch (cursor.kind)
{
case .TypedefDecl:
switch (spelling)
{
case "size_t": outString.Append("c_size");
case "wchar_t": outString.Append("c_wchar");
case "va_list": outString.Append("VarArgs");
case "int8_t": outString.Append("int8");
case "int16_t": outString.Append("int16");
case "int32_t": outString.Append("int32");
case "int64_t": outString.Append("int64");
case "uint8_t": outString.Append("uint8");
case "uint16_t": outString.Append("uint16");
case "uint32_t": outString.Append("uint32");
case "uint64_t": outString.Append("uint64");
case "intptr_t": outString.Append("c_intptr");
case "uintptr_t": outString.Append("c_uintptr");
default: fallthrough getName;
}
default:
Compiler.Identifier.GetSourceName(spelling, outString);
}
}
enum AttrFlags { None = 0, Packed = 1, NoDiscard = 2 }
protected virtual void WriteCustomAttributes(CXCursor cursor)
{
AttrFlags attrs = 0;
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
AttrFlags* attrs = (.)client_data;
switch (cursor.kind)
{
case .PackedAttr: *attrs |= .Packed;
case .WarnUnusedResultAttr: *attrs |= .NoDiscard;
default:
}
return .Continue;
}, &attrs);
if (Clang.GetCursorAvailability(cursor) == .Deprecated) str.Append("[Obsolete] ");
if (cursor.kind == .EnumDecl) str.Append("[AllowDuplicates] ");
else if (attrs.HasFlag(.Packed)) str.Append("[Packed] ");
if (attrs.HasFlag(.NoDiscard)) str.Append("[NoDiscard] ");
}
protected enum Block { case NoBlock, StaticBlock, CustomBlock(StringView); }
protected virtual Block GetCursorBlock(CXCursor cursor)
{
switch (cursor.kind)
{
case .StructDecl,
.ClassDecl,
.UnionDecl,
.EnumDecl,
.TypeAliasDecl,
.TypedefDecl,
.Namespace: return .NoBlock;
case .FunctionDecl,
.CXXMethod,
.Constructor,
.Destructor,
.ConversionFunction,
.FieldDecl,
.VarDecl,
.MacroDefinition: return .StaticBlock;
default: Internal.FatalError(scope $"Missing GetCursorBlock implementation {FatalErrorMsg(..scope .(256))}");
}
}
protected void WriteBlock(Block block)
{
StringView target;
switch (block)
{
case .NoBlock: target = "";
case .StaticBlock: target = "static";
case .CustomBlock(let p0): target = p0;
}
if (fileInfo.block == target) return;
if (!fileInfo.block.IsEmpty)
{
str.TrimEnd();
str.Append("\n", cursorIndent, "}\n\n");
}
fileInfo.block.Set(target);
switch (block)
{
case .NoBlock:
case .StaticBlock:
str.Append("static\n{\n");
case .CustomBlock(let p0):
str.Append("extension ", p0, "\n{\n");
}
}
protected void BeginCursor(CXCursor cursor)
{
WriteComments(cursor, canChangeBlock ? (Block?)GetCursorBlock(cursor) : null);
fileInfo.prevEnd = Clang.GetRangeEnd(Clang.GetCursorExtent(cursor));
}
protected virtual void HandleCursor(CXCursor cursor)
{
switch (cursor.kind)
{
case .StructDecl, .ClassDecl, .UnionDecl, .EnumDecl:
if (Clang.Cursor_IsAnonymous(cursor) != 0)
return;
if (templateParams.IsEmpty && Clang.EqualCursors(cursor, Clang.GetTypeDeclaration(Clang.GetCursorType(cursor))) == 0)
return;
BeginCursor(cursor);
if (_ case .EnumDecl) Enum(cursor);
else Record(cursor);
case .TypeAliasDecl, .TypedefDecl:
if (Clang.Cursor_IsNull(lastRecordOrEnum) == 0)
{
let lastName = GetNameInBindings(lastRecordOrEnum, ..scope .(64));
let curName = GetNameInBindings(cursor, ..scope .(64));
if (lastName == curName) return;
}
BeginCursor(cursor);
TypeAlias(cursor);
case .Namespace: BeginCursor(cursor); Namespace(cursor);
case .FunctionDecl: BeginCursor(cursor); FunctionDecl(cursor);
case .FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl:
Runtime.FatalError("Templates are currently unsupported but Coming Soon tm");
case .CXXMethod,
.Constructor,
.Destructor,
.ConversionFunction:
if (templateParams.IsEmpty && Clang.EqualCursors(Clang.GetCursorLexicalParent(cursor), Clang.GetCursorSemanticParent(cursor)) == 0)
return;
BeginCursor(cursor);
CXXMethod(cursor);
case .FieldDecl: BeginCursor(cursor); FieldDecl(cursor);
case .VarDecl: BeginCursor(cursor); VarDecl(cursor);
case .MacroDefinition:
if (Clang.Cursor_IsMacroFunctionLike(cursor) != 0) return;
let tokens = ScopeTokenize!(cursor, unit);
if (tokens.Length <= 1) return;
BeginCursor(cursor);
MacroDefinition(cursor);
case .InclusionDirective, .MacroExpansion, .CXXBaseSpecifier, .CXXAccessSpecifier,
.LinkageSpec, .TemplateTypeParameter, .NonTypeTemplateParameter, .StaticAssert: // ignored
default: Debug.WriteLine(scope $"Unhandled cursor: {_}");
}
}
protected virtual bool IsOutParam(CXCursor arg, CXCursor method)
{
let comment = Clang.Cursor_GetParsedComment(method);
let spelling = GetCursorSpelling!(arg);
for (let i < Clang.Comment_GetNumChildren(comment))
{
let param = Clang.Comment_GetChild(comment, i);
if (Clang.Comment_GetKind(param) != .ParamCommand) continue;
if (ScopeCXString!(Clang.ParamCommandComment_GetParamName(param)) != spelling) continue;
return Clang.ParamCommandComment_GetDirection(param) == .Out;
}
return false;
}
protected virtual void ModifyWrapperPrintingPolicy(CXPrintingPolicy policy) {}
protected CXIndex index = Clang.CreateIndex(excludeDeclarationsFromPCH: 0, displayDiagnostics: 1) ~ Clang.DisposeIndex(_);
protected CXTranslationUnit unit;
protected append String str = .(1024);
protected append String wrapperBuf = .(1024);
protected CXCursor currentCursor = Clang.GetNullCursor();
protected append String cursorIndent = .(16);
private bool canChangeBlock = true;
private CXCursor lastRecordOrEnum = Clang.GetNullCursor();
protected append String fullCursorName = .(256);
protected CXPrintingPolicy printingPolicy;
private StreamWriter currentWritter;
private StreamWriter wrapperWritter;
protected StringView WrapperFilePath;
private append String wrapperTemplateChain = .(16);
private append String templateParams = .(16);
private append String templateParamsWhere = .(64);
private String defferedWrapperWrite = null;
private struct UnitMacroIndex : this(uint32 line, CXFile file), IHashable
{
public int GetHashCode() => line;
public static bool operator==(Self lhs, Self rhs) => lhs.line == rhs.line && Clang.File_IsEqual(lhs.file, rhs.file) != 0;
}
protected class FileInfo : this(CXSourceLocation prevEnd, CXFile file)
{
public append String block = .(64);
public enum { None = 0, LSquirly = 1, RSquirly = 0b10 } queuedTokens = .None;
public (CXType type, int32 curWidth) bitfield = default;
}
protected FileInfo fileInfo = null;
private Dictionary<String, FileInfo> fileInfos = new .(32) ~ DeleteDictionaryAndKeysAndValues!(_);
private append Dictionary<UnitMacroIndex, CXCursor> unitMacros = .(128);
public static mixin ScopeCXString(CXString str)
{
defer:mixin Clang.DisposeString(str);
StringView(Clang.GetCString(str))
}
public static mixin GetCursorSpelling(CXCursor cursor)
{
ScopeCXString!:mixin(Clang.GetCursorSpelling(cursor))
}
public static mixin GetTypeSpelling(CXType type)
{
ScopeCXString!:mixin(Clang.GetTypeSpelling(type))
}
public static mixin GetFilePath(CXFile file)
{
ScopeCXString!:mixin(Clang.GetFileName(file))
}
protected mixin GetTokenSpelling(CXToken token)
{
ScopeCXString!:mixin(Clang.GetTokenSpelling(unit, token))
}
public static void UpperSnakeCase2PascalCase(StringView snakeCase, String outString)
{
bool upper = true;
for (let c in snakeCase)
if (c == '_') upper = true;
else
{
outString.Append(upper ? c.ToUpper : c.ToLower);
upper = false;
}
}
protected virtual void PreGeneration() {}
protected virtual void PostGeneration() {}
public enum GenerationError
{
ParsingFailed
}
public Result<void, GenerationError> Generate(char8* headerPath, StringView wrapperPath = null)
{
unitMacros.Clear();
fileInfos.Clear();
lastRecordOrEnum = Clang.GetNullCursor();
if (!wrapperPath.IsNull)
{
wrapperWritter = scope:: .();
wrapperWritter.Create(wrapperPath);
WrapperFilePath = wrapperPath;
wrapperBuf.Set("""
#define private public
#define protected public
#include "
""");
Path.GetFileName(.(headerPath), wrapperBuf);
wrapperBuf.Append("""
"
#include <stdint.h>
template <typename T>
using __type = T;
extern "C"
{
""");
FlushWrapper();
}
else
wrapperWritter = null;
let args = Args;
#if DEBUG
findLang: do
{
for (let arg in args)
{
StringView view = .(arg);
if (view == "-x" || view.StartsWith("--language"))
break findLang;
}
Runtime.FatalError("You must set a language via Args (e.g. --language=c++)");
}
#endif
CXTranslationUnit_Flags unitFlags = .SkipFunctionBodies | .DetailedPreprocessingRecord;
unit = Clang.ParseTranslationUnit(index, headerPath, args.Ptr, (.)args.Length, null, 0, (.)unitFlags);
if (unit == default) return .Err(.ParsingFailed);
printingPolicy = Clang.GetCursorPrintingPolicy(Clang.GetTranslationUnitCursor(unit));
ModifyWrapperPrintingPolicy(printingPolicy);
defer Clang.PrintingPolicy_Dispose(printingPolicy);
PreGeneration();
Clang.VisitChildren(Clang.GetTranslationUnitCursor(unit), (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
{
let location = Clang.GetCursorLocation(cursor);
Clang.GetFileLocation(location, let file, let line, ?, ?);
let header = ScopeCXString!(Clang.GetFileName(file));
self.currentWritter = self.GetWriterForHeader(header);
if (self.currentWritter == null) return .Continue;
if (cursor.kind == .MacroDefinition)
{
self.unitMacros[.(line, file)] = cursor;
return .Continue;
}
if (!self.fileInfos.TryGetAlt(header, ?, out self.fileInfo))
{
self.fileInfo = new .(Clang.GetLocation(self.unit, file, 1, 1), file);
self.fileInfos.Add(new .(header), self.fileInfo);
}
}
self.WriteCursor(cursor);
switch (cursor.kind)
{
case .ClassDecl, .StructDecl, .EnumDecl, .UnionDecl:
self.lastRecordOrEnum = cursor;
default:
}
return .Continue;
}, Internal.UnsafeCastToPtr(this));
PostGeneration();
if (wrapperBuf.IsEmpty)
wrapperWritter.Write("}\n\n//begin-comptime\n");
for (let kv in fileInfos)
{
fileInfo = kv.value;
currentWritter = GetWriterForHeader(kv.key);
Clang.GetFileContents(unit, kv.value.file, let size);
WriteComments(Clang.GetLocationForOffset(unit, kv.value.file, (.)size-1), .NoBlock);
Flush();
}
return .Ok;
}
protected void WriteCursor(CXCursor cursor, bool macro = false)
{
if ((macro) != (cursor.kind == .MacroDefinition)) return;
CXCursor prevCursor = currentCursor;
currentCursor = cursor;
defer { currentCursor = prevCursor; }
int removeLenFullCursorName;
{
int curLen = fullCursorName.Length;
if (!fullCursorName.IsEmpty)
fullCursorName.Append("::");
fullCursorName.Append(GetCursorSpelling!(cursor));
removeLenFullCursorName = fullCursorName.Length - curLen;
}
defer { fullCursorName.Length -= removeLenFullCursorName; }
var cursor;
int removeLenWrapperTemplateChain = 0;
defer { wrapperTemplateChain.Length -= removeLenWrapperTemplateChain; }
if (!macro)
{
templateParams.Clear();
templateParamsWhere.Clear();
switch (cursor.kind)
{
case .FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl:
templateParams.Append('<');
let len = wrapperTemplateChain.Length;
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
switch (cursor.kind)
{
case .TemplateTypeParameter:
case .NonTypeTemplateParameter:
self.templateParamsWhere.Append(" where ", GetCursorSpelling!(cursor), " : const ");
self.Flush();
self.Type(Clang.GetCursorType(cursor));
self.templateParamsWhere.Append(self.str);
self.str.Clear();
case .TemplateTemplateParameter:
Internal.FatalError(scope $"C++ template template parameters are not supported {self.FatalErrorMsg(..scope .(256))}");
default: return .Continue;
}
let spelling = GetCursorSpelling!(cursor);
self.templateParams.Append(spelling, ", ");
self.wrapperTemplateChain.Append("__{typeof(", spelling, ")}");
return .Continue;
}, Internal.UnsafeCastToPtr(this));
templateParams.Length -= 2;
templateParams.Append('>');
removeLenWrapperTemplateChain = wrapperTemplateChain.Length - len;
cursor.kind = Clang.GetTemplateCursorKind(cursor);
default:
}
}
HandleCursor(cursor);
Flush();
}
protected void FatalErrorMsg(String outString)
{
Clang.GetFileLocation(Clang.GetCursorLocation(currentCursor), let file, let line, let col, ?);
outString.Append("in cursor '", GetCursorSpelling!(currentCursor), "' at line ");
outString.AppendF($"{line}:{col}");
outString.Append(" in file ", GetFilePath!(file));
}
protected void Flush()
{
currentWritter.Write(str);
str.Clear();
}
protected void FlushWrapper()
{
wrapperWritter.Write(wrapperBuf);
wrapperBuf.Clear();
}
protected virtual void GetIndentation(CXSourceLocation location, String outString)
{
StringView file;
uint32 offset;
{
Clang.GetFileLocation(location, let cxfile, ?, ?, out offset);
char8* ptr = Clang.GetFileContents(unit, cxfile, let size);
file = .(ptr, (.)size);
}
int i = offset;
for (; i >= 0 && file[i] != '\n'; i--) {}
i++;
for (; i < file.Length && file[i].IsWhiteSpace; i++) outString.Append(file[i]);
}
protected virtual void AllWhiteSpaceUntil(CXSourceLocation from, enum { NoNewLines, YesNewLines } newlineMode)
{
StringView file;
uint32 offset;
{
Clang.GetFileLocation(from, let cxfile, ?, ?, out offset);
char8* ptr = Clang.GetFileContents(unit, cxfile, let size);
file = .(ptr, (.)size);
}
for (int i = offset-1; i >= 0; i--)
{
char8 c = file[i];
if (c == '/' || c == '*') continue;
if ((!c.IsWhiteSpace) || (newlineMode == .NoNewLines && c == '\n')) break;
str.Append(c);
}
}
protected virtual void AllWhiteSpaceBetween(CXSourceLocation start, CXSourceLocation end)
{
if (Clang.EqualLocations(start, Clang.GetNullLocation()) != 0 ||
Clang.EqualLocations(end, Clang.GetNullLocation()) != 0) return;
StringView file;
Clang.GetFileLocation(start, let startFile, ?, ?, let startOffset);
Clang.GetFileLocation(end, let endfile, ?, ?, let endOffset);
if (Clang.File_IsEqual(startFile, endfile) == 0) return;
char8* ptr = Clang.GetFileContents(unit, startFile, let size);
file = .(ptr, (.)size);
for (int i = startOffset; i < endOffset; i++)
{
char8 c = file[i];
//if (c.IsWhiteSpace) str.Append(c);
str.Append(c.IsWhiteSpace ? c : ' ');
}
}
protected virtual CXSourceLocation GetCursorAnchor(CXCursor cursor)
{
let cursorLocation = Clang.GetCursorLocation(cursor);
//let rangeLocation = Clang.GetRangeStart(Clang.GetCursorExtent(cursor));
//if (Clang.File_IsEqual(Clang.GetSpellingLocation(cursorLocation, file: ..?, ?, ?, ?), Clang.GetSpellingLocation(rangeLocation, file: ..?, ?, ?, ?)) == 0)
return cursorLocation;
//return rangeLocation;
}
protected virtual void WriteComments(CXCursor cursor, Block? block = null)
{
let location = GetCursorAnchor(cursor);
if (cursor.kind == .MacroDefinition)
{
if (block.HasValue) WriteBlock(block.Value);
GetIndentation(location, str);
return;
}
cursorIndent.Clear();
GetIndentation(location, cursorIndent);
WriteComments(location, block);
}
protected virtual void WriteComments(CXSourceLocation writeUntil, Block? block = null)
{
var block;
if (fileInfo.queuedTokens != .None) block = null;
Clang.GetFileLocation(writeUntil, let curFile, let curLine, let curColumn, let curOffset);
Clang.GetFileLocation(fileInfo.prevEnd, let prevFile, let prevLine, let prevColumn, let prevOffset);
defer { fileInfo.prevEnd = writeUntil; }
if (Clang.File_IsEqual(curFile, prevFile) == 0) Runtime.FatalError(scope $"{GetFilePath!(curFile)} != {GetFilePath!(prevFile)}");
var between = Clang.GetRange(
Clang.GetLocationForOffset(unit, prevFile, prevOffset),
Clang.GetLocationForOffset(unit, curFile, curOffset)
);
CXToken* tokenPtr = null; uint32 tokenCount = 0;
Clang.Tokenize(unit, between, &tokenPtr, &tokenCount);
defer Clang.DisposeTokens(unit, tokenPtr, tokenCount);
Span<CXToken> tokens = .(tokenPtr, tokenCount);
uint32 line = prevLine;
mixin WriteLinesUntil(uint32 targetLine, CXSourceLocation from)
{
int numNewLines = targetLine - line;
for (let i < numNewLines)
{
str.Append('\n');
line++;
if (unitMacros.TryGetValue(.(line, curFile), let macro))
{
WriteCursor(macro, macro: true);
if (block.HasValue) remainingMacros: do
{
let line = line;
for (uint32 ln = line+1; ln < curLine; ln++)
if (unitMacros.ContainsKey(.(ln, curFile)))
break remainingMacros;
WriteBlock(block.Value);
}
}
}
if (numNewLines == 0)
AllWhiteSpaceUntil(from, .NoNewLines);
else
GetIndentation(from, str);
}
void Block()
{
if (fileInfo.queuedTokens != .None) return;
findMacros: do
{
for (uint32 ln = line; ln < curLine; ln++)
{
if (!unitMacros.TryGetValue(.(ln, curFile), let macro)) continue;
WriteBlock(GetCursorBlock(macro));
break findMacros;
}
if (block.HasValue) WriteBlock(block.Value);
}
}
bool first = true;
for (let token in tokens)
{
bool isWritingQueuedToken = false;
switch (Clang.GetTokenKind(token))
{
case .Comment:
case .Punctuation:
mixin QueuedToken(var queuedToken, StringView spelling)
{
if (fileInfo.queuedTokens.HasFlag(queuedToken) && GetTokenSpelling!(token) == spelling)
{
fileInfo.queuedTokens ^= queuedToken;
isWritingQueuedToken = true;
}
}
QueuedToken!(decltype(fileInfo.queuedTokens).LSquirly, "{");
QueuedToken!(decltype(fileInfo.queuedTokens).RSquirly, "}");
if (!isWritingQueuedToken) continue;
if (fileInfo.queuedTokens == .None) block = @block;
default: continue;
}
let location = Clang.GetTokenLocation(unit, token);
Clang.GetFileLocation(location, ?, let startLine, ?, ?);
WriteLinesUntil!(startLine, location);
if (!isWritingQueuedToken)
{
if (first) Block();
first = false;
}
WriteToken(token);
for (let c in GetTokenSpelling!(token))
if (c == '\n')
line++;
Flush();
}
WriteLinesUntil!(curLine, writeUntil);
if (@block.HasValue) WriteBlock(@block.Value);
}
protected enum TypeParamMode { None, InParam, OutParam }
protected virtual void Type(CXType type, TypeParamMode paramMode = .None)
{
switch (type.kind)
{
case .Void: str.Append("void");
case .Bool: str.Append("bool");
case .Char_U, .UChar: str.Append("c_uchar");
case .Char16: str.Append("char16");
case .Char32: str.Append("char32");
case .UShort: str.Append("c_ushort");
case .UInt: str.Append("c_uint");
case .ULong: str.Append("c_ulong");
case .ULongLong: str.Append("c_ulonglong");
case .Char_S, .SChar: str.Append("c_char");
case .WChar: str.Append("c_wchar");
case .Short: str.Append("c_short");
case .Int: str.Append("c_int");
case .Long: str.Append("c_long");
case .LongLong: str.Append("c_longlong");
case .Float: str.Append("float");
case .Double: str.Append("double");
case .NullPtr: str.Append("decltype(null)");
case .Pointer:
let pointee = Clang.GetPointeeType(type);
if (paramMode == .OutParam) str.Append("out ");
Type(pointee);
if (paramMode != .OutParam && pointee.kind != .FunctionProto && pointee.kind != .FunctionNoProto)
str.Append('*');
case .LValueReference, .RValueReference:
let nonRefType = Clang.GetNonReferenceType(type);
switch (paramMode)
{
case .None: str.Append("ref ");
case .InParam:
switch (type.kind)
{
case .LValueReference:
if (Clang.IsConstQualifiedType(nonRefType) == 0)
str.Append("ref ");
else
fallthrough;
case .RValueReference:
str.Append("in ");
default: Runtime.FatalError();
}
case .OutParam: str.Append("out ");
}
Type(nonRefType);
case .FunctionNoProto: str.Append("function "); Type(Clang.GetResultType(type)); str.Append("()");
case .FunctionProto: str.Append("function "); Type(Clang.GetResultType(type)); WriteFunctionProtoParams(type, ScopeTokenize!(currentCursor, unit));
case .ConstantArray: Type(Clang.GetArrayElementType(type)); str.Append('['); Clang.GetArraySize(type).ToString(str); str.Append(']');
case .IncompleteArray: Type(Clang.GetArrayElementType(type)); str.Append('*');
case .Auto: str.Append("var");
case .Elaborated, .Record, .Enum, .Typedef:
CXCursor decl = Clang.GetTypeDeclaration(type);
if (Clang.Cursor_IsAnonymous(decl) != 0)
{
switch (decl.kind)
{
case .EnumDecl: Enum(decl);
case .StructDecl, .ClassDecl, .UnionDecl: Record(decl);
default: Runtime.FatalError(scope $"Unhandled anon type: {_}");
}
if (fileInfo.queuedTokens.HasFlag(.RSquirly))
{
str.Append('\n');
GetIndentation(GetCursorAnchor(decl), str);
str.Append('}');
fileInfo.queuedTokens ^= .RSquirly;
}
break;
}
{
String qualified = scope .(256);
String buffer = scope .(256);
GetNameInBindings(decl, qualified);
CXCursor parent = decl;
while (true)
{
parent = Clang.GetCursorSemanticParent(parent);
if (Clang.IsDeclaration(parent.kind) == 0 || parent.kind == .LinkageSpec) break;
buffer.Clear();
switch (parent.kind)
{
case .FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl:
GetNameInBindings(parent, buffer);
buffer.Append('<');
Clang.VisitChildren(parent, (cursor, parent, client_data) =>
{
String buffer = (.)Internal.UnsafeCastToObject(client_data);
switch (cursor.kind)
{
case .TemplateTypeParameter, .NonTypeTemplateParameter:
case .TemplateTemplateParameter:
Runtime.FatalError(scope $"C++ template template parameters are not supported");
default: return .Continue;
}
buffer.Append(GetCursorSpelling!(cursor), ", ");
return .Continue;
}, Internal.UnsafeCastToPtr(buffer));
buffer.Length -= 2;
buffer.Append('>');
default:
Flush();
Type(Clang.GetCursorType(parent));
buffer.Append(str);
str.Clear();
}
buffer.Append('.');
qualified.Insert(0, buffer);
}
str.Append(qualified);
}
let numTemplateArgs = Clang.Type_GetNumTemplateArguments(type);
if (numTemplateArgs > 0)
{
str.Append('<');
for (let i < numTemplateArgs)
{
if (i > 0) str.Append(", ");
Type(Clang.Type_GetTemplateArgumentAsType(type, (.)i));
}
str.Append('>');
}
case .Unexposed, .DependentSizedArray: str.Append(ScopeCXString!(Clang.GetTypeSpelling(Clang.GetUnqualifiedType(type)))); // template param
default: Runtime.FatalError(scope $"Unhandled type: {_} \"{GetTypeSpelling!(type)}\"");
}
}
protected virtual void WriteFunctionProtoParams(CXType type, Span<CXToken> tokens = null)
{
var iter = tokens.GetEnumerator();
for (let i < 2)
for (let token in iter)
if (Clang.GetTokenKind(token) == .Punctuation && GetTokenSpelling!(token) == "(")
break;
str.Append('(');
let numArgs = Clang.GetNumArgTypes(type);
for (let i < numArgs)
{
if (i > 0) str.Append(", ");
Type(Clang.GetArgType(type, (.)i));
CXToken last = default;
for (let token in iter)
{
if (Clang.GetTokenKind(token) == .Punctuation && { let spelling = GetTokenSpelling!(token); spelling == "," || spelling == ")" })
{
if (Clang.GetTokenKind(last) != .Identifier) break;
str.Append(' ');
Compiler.Identifier.GetSourceName(GetTokenSpelling!(last), str);
break;
}
last = token;
}
}
if (Clang.IsFunctionTypeVariadic(type) != 0)
{
if (numArgs > 0) str.Append(", ");
str.Append("...");
}
str.Append(')');
}
protected void AccessSpecifier(CXCursor cursor)
{
switch (Clang.GetCXXAccessSpecifier(cursor))
{
case .InvalidAccessSpecifier,
.Public: str.Append("public ");
case .Protected: str.Append("protected ");
case .Private: str.Append("private ");
}
}
protected mixin BeginBody(CXCursor cursor)
{
fileInfo.prevEnd = GetCursorAnchor(cursor);
fileInfo.queuedTokens |= .LSquirly;
// [Friend] is needed in case you use BeginBody! in your code
if (canChangeBlock)
{
defer:mixin { this.[Friend]canChangeBlock = true; }
this.[Friend]canChangeBlock = false;
}
defer:mixin
{
if (this.[Friend]defferedWrapperWrite != null)
{
str.Append("\n\n", cursorIndent, "const String _wrapperText = \"\\n\"\n",
this.[Friend]defferedWrapperWrite, ";");
delete this.[Friend]defferedWrapperWrite;
this.[Friend]defferedWrapperWrite = null;
}
if (fileInfo.queuedTokens.HasFlag(.LSquirly))
{
switch (cursor.kind)
{
case .StructDecl, .UnionDecl, .ClassDecl:
str.Append(';');
default:
str.Append(" {}");
}
fileInfo.queuedTokens = .None;
}
else
fileInfo.queuedTokens |= .RSquirly;
}
}
const int int_maxDigits = scope $"{int.MaxValue:X}".Length + 1;
protected virtual void CppWrapperName(CXCursor cursor, String outString)
{
String hashCode = ScopeCXString!(Clang.GetCursorUSR(cursor))
.GetHashCode().ToString(..scope .(int_maxDigits), "X", null);
outString.Append("cpp2beef_");
if (hashCode.StartsWith('-')) hashCode[0] = 'm';
outString.Append('0', int_maxDigits - hashCode.Length);
outString.Append(hashCode);
}
protected virtual enum { C, Cpp } Linkable_Attributes(CXCursor cursor)
{
let mangledName = ScopeCXString!(Clang.Cursor_GetMangling(cursor));
let name = GetNameInBindings(cursor, ..scope .(mangledName.Length));
WriteCustomAttributes(cursor);
if (mangledName == name)
{
str.Append("[CLink] ");
return .C;
}
else if (!mangledName.IsEmpty && mangledName[0].IsLetterOrDigit)
{
str.Append("[LinkName(");
mangledName.QuoteString(str);
str.Append(")] ");
return .C;
}
else
{
str.Append("[LinkName(\"");
CppWrapperName(cursor, str);
str.Append('"');
if (!wrapperTemplateChain.IsEmpty)
if (templateParams.IsEmpty)
str.Append(" + __template_chain");
else
str.Append(" + CppWrapperF($\"", wrapperTemplateChain, "\")");
str.Append(")] ");
return .Cpp;
}
}
protected virtual void Method_Parameters(CXCursor cursor)
{
let numArgs = Clang.Cursor_GetNumArguments(cursor);
for (let i < numArgs)
{
if (i > 0) str.Append(", ");
let arg = Clang.Cursor_GetArgument(cursor, (.)i);
//AllWhiteSpaceUntil(Clang.GetCursorLocation(arg));
currentCursor = arg;
bool isOutParam = IsOutParam(arg, cursor);
Type(Clang.GetCursorType(arg), isOutParam ? .OutParam : .InParam);
str.Append(' ');
GetNameInBindings(arg, str);
str.TrimEnd();
WriteTokensAfterEquals(arg);
}
if (Clang.Cursor_IsVariadic(cursor) != 0)
{
if (numArgs > 0) str.Append(", ");
str.Append("...");
}
currentCursor = cursor;
}
protected virtual void WriteTypeAndName(CXCursor cursor, CXType type, enum { Standard, TypeAlias, ConversionFunction, Ctor, Dtor } format = .Standard)
{
bool preserveColumns = Flags.HasFlag(.PreseveColumns);
Flush();
if (preserveColumns)
AllWhiteSpaceBetween(
Clang.GetRangeStart(Clang.GetCursorExtent(cursor)),
Clang.GetRangeStart(Clang.Cursor_GetSpellingNameRange(cursor, 0, 0))
);
if (format == .Ctor)
{
str.Append("this");
return;
}
String whitespace;
if (preserveColumns)
{
whitespace = scope:: .(str);
str.Clear();
Type(type);
whitespace.Length -= Math.Min(str.Length, whitespace.Length);
if (whitespace.IsEmpty) whitespace.Append(' ');
}
else
{
whitespace = " ";
Type(type);
}
switch (format)
{
case .Standard:
str.Append(whitespace);
GetNameInBindings(cursor, str);
str.Append(templateParams);
case .TypeAlias:
String typeStr = scope .(str);
str.Clear();
str.Append(' ');
GetNameInBindings(cursor, str);
str.Append(templateParams, " = ", typeStr, ";");
if (preserveColumns) str.Append(whitespace);
case .ConversionFunction:
String typeStr = scope .(str);
str.Clear();
str.Append("operator", whitespace, typeStr, templateParams);
case .Ctor: Runtime.FatalError();
case .Dtor: str.Append(whitespace, "Dispose");
}
}
protected static mixin ScopeTokenize(CXCursor cursor, CXTranslationUnit unit)
{
let range = Clang.GetCursorExtent(cursor);
CXToken* tokenPtr = null; uint32 tokenCount = 0;
Clang.Tokenize(unit, range, &tokenPtr, &tokenCount);
defer:mixin Clang.DisposeTokens(unit, tokenPtr, tokenCount);
Span<CXToken>(tokenPtr, (.)tokenCount)
}
protected virtual void WriteTokensAfterEquals(CXCursor cursor)
{
var tokens = ScopeTokenize!(cursor, unit);
CXSourceLocation prevEnd = Clang.GetNullLocation();
findEquals: do
{
for (let token in tokens)
{
let spelling = GetTokenSpelling!(token);
if (spelling != "=")
{
prevEnd = Clang.GetRangeEnd(Clang.GetTokenExtent(unit, token));
continue;
}
tokens.RemoveFromStart(@token.Index);
break findEquals;
}
return;
}
WriteTokens(tokens, prevEnd, .Identifier);
}
protected virtual void WriteToken(CXToken token)
{
let spelling = ScopeCXString!(Clang.GetTokenSpelling(unit, token));
let kind = Clang.GetTokenKind(token);
token: switch (kind)
{
case .Literal:
str.Append(spelling);
if (str.EndsWith("LL"))
str.Length--;
case .Identifier:
var cursor = Clang.GetCursor(unit, Clang.GetTokenLocation(unit, token));
if (Clang.Cursor_IsNull(cursor) != 0) fallthrough;
cursor = Clang.GetCursorDefinition(cursor);
if (Clang.Cursor_IsNull(cursor) != 0) fallthrough;
GetNameInBindings(cursor, str);
case .Punctuation, .Comment, .Keyword:
str.Append(spelling);
}
}
protected virtual void WriteTokens(Span<CXToken> tokens, CXSourceLocation prevTokenEnd, CXTokenKind prevKind)
{
var prevTokenEnd;
for (let token in tokens)
{
let extent = Clang.GetTokenExtent(unit, token);
AllWhiteSpaceBetween(prevTokenEnd, Clang.GetRangeStart(extent));
WriteToken(token);
prevTokenEnd = Clang.GetRangeEnd(extent);
}
//TODO: move to custom utils
/* // This formats the tokens instead of copying the format 1:1
const CXTokenKind Unary = (.)-1;
var prevKind;
for (let token in tokens)
{
let spelling = ScopeCXString!(Clang.GetTokenSpelling(unit, token));
let kind = Clang.GetTokenKind(token);
token: switch (kind)
{
case .Keyword, .Identifier, .Literal:
if (prevKind == .Keyword || prevKind == .Identifier || prevKind == .Punctuation)
str.Append(' ');
if (kind == .Identifier) do
{
var cursor = Clang.GetCursor(unit, Clang.GetTokenLocation(unit, token));
if (Clang.Cursor_IsNull(cursor) != 0) break;
cursor = Clang.GetCursorDefinition(cursor);
if (Clang.Cursor_IsNull(cursor) != 0) break;
GetNameInBindings(cursor, str);
break token;
}
str.Append(spelling);
if (kind == .Literal && str.EndsWith("LL"))
str.Length--;
case .Punctuation:
defer str.Append(spelling);
if (prevKind != Unary && !(spelling == "(" && prevKind != .Punctuation) && spelling != ")")
{
if (spelling != ",")
str.Append(' ');
if (prevKind != .Punctuation)
break;
}
prevKind = Unary;
continue;
case .Comment:
}
prevKind = kind;
}*/
}
protected virtual void Namespace(CXCursor cursor)
{
WriteCustomAttributes(cursor);
str.Append("extension ");
GetNameInBindings(cursor, str);
{
BeginBody!(cursor);
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
self.WriteCursor(cursor);
return .Continue;
}, Internal.UnsafeCastToPtr(this));
}
if (Clang.Cursor_IsInlineNamespace(cursor) != 0)
{
GetIndentation(GetCursorAnchor(cursor), str);
str.Append("public static using ");
GetNameInBindings(cursor, str);
str.Append(";\n");
}
}
protected virtual void WriteMethodWrapper(CXCursor cursor)
{
let parent = Clang.GetCursorType(Clang.GetCursorSemanticParent(cursor));
var resultType = Clang.GetCursorResultType(cursor);
bool nonStatic = (cursor.kind == .CXXMethod && Clang.CXXMethod_IsStatic(cursor) == 0) || cursor.kind == .Destructor || cursor.kind == .ConversionFunction;
if (!wrapperTemplateChain.IsEmpty)
FlushWrapper();
StringView parentSpelling = "\" + __cpp_type + \"";
if (wrapperTemplateChain.IsEmpty)
parentSpelling = ScopeCXString!::(Clang.GetFullyQualifiedName(parent, printingPolicy, 0));
if (cursor.kind == .Constructor)
wrapperBuf.Append("__type<", parentSpelling, "> ");
else
{
wrapperBuf.Append("__type<");
wrapperBuf.Append(ScopeCXString!(Clang.GetFullyQualifiedName(resultType, printingPolicy, 0)));
wrapperBuf.Append("> ");
}
CppWrapperName(cursor, wrapperBuf);
if (!wrapperTemplateChain.IsEmpty)
{
if (templateParams.IsEmpty)
wrapperBuf.Append("\" + __template_chain + \"");
else
wrapperBuf.Append("\" + CppWrapperF($\"", wrapperTemplateChain, "\") + \"");
}
wrapperBuf.Append('(');
if (nonStatic)
wrapperBuf.Append(parentSpelling, " *self");
for (int i < Clang.Cursor_GetNumArguments(cursor))
{
let arg = Clang.Cursor_GetArgument(cursor, (.)i);
if (nonStatic || i > 0) wrapperBuf.Append(", ");
wrapperBuf.Append("__type<");
wrapperBuf.Append(ScopeCXString!(Clang.GetFullyQualifiedName(Clang.GetCursorType(arg), printingPolicy, 0)));
wrapperBuf.Append("> p");
i.ToString(wrapperBuf);
}
wrapperBuf.Append(") { ");
if (resultType.kind != .Void) wrapperBuf.Append("return ");
if (nonStatic)
{
if (cursor.kind != .Destructor)
wrapperBuf.Append("self->", GetCursorSpelling!(cursor));
else
wrapperBuf.Append("self->~", parentSpelling);
}
else if (cursor.kind == .Constructor)
wrapperBuf.Append(parentSpelling);
else
wrapperBuf.Append(fullCursorName);
wrapperBuf.Append('(');
for (int i < Clang.Cursor_GetNumArguments(cursor))
{
if (i > 0) wrapperBuf.Append(", ");
wrapperBuf.Append('p');
i.ToString(wrapperBuf);
}
wrapperBuf.Append("); }");
if (wrapperTemplateChain.IsEmpty)
{
wrapperBuf.Append('\n');
FlushWrapper();
}
else
{
if (defferedWrapperWrite == null)
defferedWrapperWrite = new .(1024);
defferedWrapperWrite.Append(cursorIndent, "\t + \"extern \\\"C\\\" ");
wrapperBuf.ToString(defferedWrapperWrite);
defferedWrapperWrite.Append("\\n\"\n");
wrapperBuf.Clear();
}
}
protected virtual void FunctionDecl(CXCursor cursor)
{
if (Linkable_Attributes(cursor) == .Cpp)
WriteMethodWrapper(cursor);
AccessSpecifier(cursor);
str.Append("static extern ");
WriteTypeAndName(cursor, Clang.GetCursorResultType(cursor));
str.Append('(');
Method_Parameters(cursor);
str.Append(')');
str.Append(templateParamsWhere);
str.Append(';');
}
protected virtual void CXXMethod(CXCursor cursor) //TODO: conversion function
{
void Attributes()
{
if (Linkable_Attributes(cursor) == .Cpp)
WriteMethodWrapper(cursor);
}
let spelling = GetCursorSpelling!(cursor);
if (spelling.StartsWith("operator"))
{
if (spelling == "operator[]")
{
Runtime.Assert(templateParams.IsEmpty, "Properties can't have generics");
AccessSpecifier(cursor);
if (Clang.CXXMethod_IsStatic(cursor) != 0) str.Append("static ");
str.Append("extern ");
Type(Clang.GetCursorResultType(cursor));
str.Append(" this[");
Method_Parameters(cursor);
str.Append("] { ");
Attributes();
str.Append("get; }");
return;
}
Attributes();
AccessSpecifier(cursor);
str.Append("static extern ");
WriteTypeAndName(cursor, Clang.GetCursorResultType(cursor),
cursor.kind == .ConversionFunction ? .ConversionFunction : .Standard);
str.Append("(in Self, ");
Method_Parameters(cursor);
if (str.EndsWith(", ")) str.Length -= 2;
str.Append(')');
str.Append(templateParamsWhere);
str.Append(';');
return;
}
Attributes();
AccessSpecifier(cursor);
if (Clang.CXXMethod_IsStatic(cursor) != 0) str.Append("static ");
str.Append("extern ");
switch (cursor.kind)
{
case .Constructor: WriteTypeAndName(cursor, default, .Ctor);
case .Destructor: WriteTypeAndName(cursor, Clang.GetCursorResultType(cursor), .Dtor);
case .CXXMethod:
WriteTypeAndName(cursor, Clang.GetCursorResultType(cursor));
default:
Runtime.FatalError("Unhandled c++ method kind");
}
str.Append('(');
Method_Parameters(cursor);
str.Append(')');
if (Clang.CXXMethod_IsStatic(cursor) == 0 && Clang.CXXMethod_IsConst(cursor) == 0 && cursor.kind == .CXXMethod)
str.Append(" mut");
str.Append(templateParamsWhere);
str.Append(';');
}
int bitfieldUniquenessCounter = 0;
protected virtual void DumpBitfieldStorage()
{
str.Append("\n", cursorIndent, "private ");
Type(fileInfo.bitfield.type);
str.Append(" __bitfield_");
bitfieldUniquenessCounter++.ToString(str);
str.Append(';');
fileInfo.bitfield = default;
}
protected virtual void FieldDecl(CXCursor cursor)
{
let type = Clang.GetCursorType(cursor);
if (Clang.Cursor_IsBitField(cursor) != 0)
{
if (fileInfo.bitfield.type != default && Clang.EqualTypes(fileInfo.bitfield.type, type) == 0)
DumpBitfieldStorage();
if (fileInfo.bitfield == default)
fileInfo.bitfield.type = type;
int32 width = Clang.GetFieldDeclBitWidth(cursor);
Debug.Assert(width <= Clang.Type_GetSizeOf(type) * 8);
fileInfo.bitfield.curWidth += width;
if (fileInfo.bitfield.curWidth > Clang.Type_GetSizeOf(type) * 8)
{
DumpBitfieldStorage();
fileInfo.bitfield.curWidth = width;
}
str.Append("[Bitfield(.");
if (GetCursorSpelling!(cursor).IsEmpty)
{
str.Append("Private, .Bits(");
width.ToString(str);
str.Append("), \"__anon_bitfield_");
bitfieldUniquenessCounter++.ToString(str);
str.Append("\")]");
}
else
{
Flush();
AccessSpecifier(cursor);
str[0] = str[0].ToUpper;
str.Append(", .Bits(");
width.ToString(str);
str.Append("), \"");
GetNameInBindings(cursor, str);
str.Append("\")]");
}
return;
}
WriteCustomAttributes(cursor);
AccessSpecifier(cursor);
WriteTypeAndName(cursor, type);
str.Append(';');
}
protected virtual void VarDecl(CXCursor cursor)
{
WriteCustomAttributes(cursor);
let type = Clang.GetCursorType(cursor);
switch (Clang.GetCursorLinkage(cursor))
{
case .Internal when Clang.IsConstQualifiedType(type) != 0:
AccessSpecifier(cursor);
str.Append("const ");
WriteTypeAndName(cursor, type);
WriteTokensAfterEquals(cursor);
str.Append(';');
case .External, .UniqueExternal:
Flush();
let linkLang = Linkable_Attributes(cursor);
if (linkLang == .Cpp) str.Clear();
AccessSpecifier(cursor);
str.Append("static extern ");
if (linkLang == .Cpp && type.kind != .LValueReference && type.kind != .RValueReference)
str.Append("ref ");
WriteTypeAndName(cursor, type);
if (linkLang == .Cpp)
{
let wrapperName = CppWrapperName(cursor, ..scope .());
str.Append(" { [LinkName(\"", wrapperName, "\")] get; }");
wrapperBuf.Append("__type<");
wrapperBuf.Append(ScopeCXString!(Clang.GetFullyQualifiedName(type, printingPolicy, 0)));
wrapperBuf.Append("> ");
if (type.kind != .LValueReference && type.kind != .RValueReference)
wrapperBuf.Append('&');
wrapperBuf.Append(wrapperName, "() { return ", fullCursorName, "; }\n");
FlushWrapper();
}
str.Append(';');
default:
Runtime.FatalError(scope $"Unhandled var linkage: {_}");
}
}
public static bool HasVTable(CXCursor cursor)
{
return Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
switch (cursor.kind)
{
case .CXXBaseSpecifier:
if (HasVTable(Clang.GetCursorDefinition(cursor)))
return .Break;
case .CXXMethod, .Constructor, .Destructor, .ConversionFunction:
if (Clang.CXXMethod_IsVirtual(cursor) != 0)
return .Break;
default:
}
return .Continue;
}, null) != 0;
}
protected virtual void Record(CXCursor cursor)
{
WriteCustomAttributes(cursor);
switch (cursor.kind)
{
case .StructDecl, .ClassDecl: str.Append("[CRepr] ");
case .UnionDecl: str.Append("[CRepr, Union] ");
default: Runtime.FatalError("Unhandled record type");
}
AccessSpecifier(cursor);
str.Append("struct ");
if (Clang.Cursor_IsAnonymous(cursor) == 0)
GetNameInBindings(cursor, str);
str.Append(templateParams);
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
if (cursor.kind != .Destructor) return .Continue;
String str = (.)Internal.UnsafeCastToObject(client_data);
str.Append(" : IDisposable");
return .Break;
}, Internal.UnsafeCastToPtr(str));
str.Append(templateParamsWhere);
BeginBody!(cursor);
//TODO
/*Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
switch (cursor.kind)
{
case .FieldDecl, .CXXMethod, .Constructor, .Destructor, .ConversionFunction, .FunctionDecl, .VarDecl,
.FunctionTemplate, .ClassTemplate, .TypeAliasTemplateDecl, .StructDecl, .ClassDecl,
.UnionDecl, .EnumDecl, .TypeAliasDecl, .TypedefDecl:
self.WriteComments(cursor);
return .Break;
default:
return .Continue;
}
}, Internal.UnsafeCastToPtr(this));*/
if (!wrapperTemplateChain.IsEmpty)
{
//WriteQueuedOpenSquirly();
str.Append("private const String __cpp_type = \"", GetCursorSpelling!(cursor), "<\"");
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
switch (cursor.kind)
{
case .TemplateTypeParameter:
self.str.Append(" + CppTypeToC(typeof(", GetCursorSpelling!(cursor), "))");
case .NonTypeTemplateParameter:
self.str.Append(" + CppConstValue<");
self.Type(Clang.GetCursorType(cursor));
self.str.Append(">(", GetCursorSpelling!(cursor), ")");
default: return .Continue;
}
self.str.Append(" + \", \"");
return .Continue;
}, Internal.UnsafeCastToPtr(this));
str.Length -= 3;
str.Append(">\";\n", cursorIndent);
str.Append("private const String __template_chain = CppWrapperF($\"", wrapperTemplateChain, "\");\n\n", cursorIndent);
/*str.Append("[CppWriteToWrapper<\"", WrapperFilePath, "\">(\"\\ntemplate ");
switch (cursor.kind)
{
case .StructDecl: str.Append("struct ");
case . ClassDecl: str.Append("class " );
case . UnionDecl: str.Append("union " );
default: Runtime.FatalError();
}
str.Append("\" + __cpp_type + \";\\n\")]\n\n", indent);*/
}
bool firstBase = true;
bool hasVTable = HasVTable(cursor);
Clang.VisitCXXBaseClasses(Clang.GetCursorType(cursor), (cursor, client_data) =>
{
(Self self, bool* firstBase, bool hasVTable) = *(.)client_data;
Runtime.Assert(Clang.IsVirtualBase(cursor) == 0, "Virtual bases are not supported");
//self.WriteQueuedOpenSquirly();
if (*firstBase && hasVTable && !HasVTable(Clang.GetCursorDefinition(cursor)))
self.str.Append(self.cursorIndent, "private void* cppVtable;\n");
self.str.Append(self.cursorIndent);
self.AccessSpecifier(cursor);
self.str.Append("using ");
self.Type(Clang.GetCursorType(cursor));
self.str.Append(";\n");
*firstBase = false;
return .Continue;
}, &(this, &firstBase, hasVTable));
if (firstBase && hasVTable)
str.Append(cursorIndent, "private void* cppVtable;\n");
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
if (self.fileInfo.bitfield != default && Clang.Cursor_IsBitField(cursor) == 0)
self.DumpBitfieldStorage();
self.WriteCursor(cursor);
if (Clang.Cursor_IsAnonymousRecordDecl(cursor) != 0)
self.str..TrimEnd()..Append(";");
return .Continue;
}, Internal.UnsafeCastToPtr(this));
if (fileInfo.bitfield != default)
DumpBitfieldStorage();
}
protected virtual void Enum(CXCursor cursor)
{
WriteCustomAttributes(cursor);
AccessSpecifier(cursor);
str.Append("enum ");
if (Clang.Cursor_IsAnonymous(cursor) == 0)
GetNameInBindings(cursor, str);
str.Append(templateParams);
if (Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
if (cursor.kind == .PackedAttr)
return .Break;
return .Continue;
}, null) == 0)
{
str.Append(" : ");
Type(Clang.GetEnumDeclIntegerType(cursor));
str.Append(templateParamsWhere);
}
BeginBody!(cursor);
Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{
Self self = (.)Internal.UnsafeCastToObject(client_data);
Runtime.Assert(cursor.kind == .EnumConstantDecl);
self.BeginCursor(cursor);
self.WriteCustomAttributes(cursor);
self.GetNameInBindings(cursor, self.str);
self.WriteTokensAfterEquals(cursor);
//Clang.GetEnumConstantDeclValue(cursor).ToString(self.str);
self.str.Append(',');
return .Continue;
}, Internal.UnsafeCastToPtr(this));
}
protected virtual void TypeAlias(CXCursor cursor)
{
CXType type;
switch (cursor.kind)
{
case .TypedefDecl: type = Clang.GetTypedefDeclUnderlyingType(cursor);
case .TypeAliasDecl: type = Clang.GetCursorType(cursor);
default:
Runtime.FatalError("Unhandled type alias cursor kind");
}
if (type.kind == .Pointer) do
{
let pointee = Clang.GetPointeeType(type);
if (pointee.kind != .FunctionProto && pointee.kind != .FunctionNoProto)
break;
WriteCustomAttributes(cursor);
AccessSpecifier(cursor);
str.Append("function ");
WriteTypeAndName(cursor, Clang.GetResultType(pointee));
WriteFunctionProtoParams(pointee, ScopeTokenize!(cursor, unit));
str.Append(templateParamsWhere);
str.Append(';');
return;
}
WriteCustomAttributes(cursor);
AccessSpecifier(cursor);
str.Append("typealias");
WriteTypeAndName(cursor, type, .TypeAlias);
Runtime.Assert(templateParamsWhere.IsEmpty, "type aliases can't have constraints (yet)");
}
protected virtual void MacroDefinition(CXCursor cursor)
{
let tokens = ScopeTokenize!(cursor, unit);
WriteCustomAttributes(cursor);
str.Append("public const let ");
GetNameInBindings(cursor, str);
AllWhiteSpaceBetween(Clang.GetRangeEnd(Clang.GetTokenExtent(unit, tokens[0])), Clang.GetRangeStart(Clang.GetTokenExtent(unit, tokens[1])));
str.Append("= ");
WriteTokens(tokens[1...], Clang.GetNullLocation(), .Punctuation);
str.Append(';');
}
}