1577 lines
46 KiB
Beef
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(';');
|
|
}
|
|
}
|