c part ready to go

This commit is contained in:
Rune
2026-02-22 15:25:43 +01:00
parent 397c0e320c
commit 806f27381a
17 changed files with 674 additions and 43 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
build build
recovery recovery
BeefSpace_User.toml BeefSpace_User.toml
clang-c.h

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "Clang-C"]
path = Clang-C
url = https://git.unicon-gmbh.de/BeefBindings/Clang-C.git

View File

@@ -1,6 +1,6 @@
FileVersion = 1 FileVersion = 1
Dependencies = {corlib = "*", "Clang-C.git" = {Git = "https://git.unicon-gmbh.de/BeefBindings/Clang-C.git"}}
[Project] [Project]
Name = "Cpp2Beef" Name = "Cpp2Beef"
TargetType = "BeefLib" StartupObject = "ClangC.Setup.Program"
StartupObject = "Cpp2Beef.Program"

View File

@@ -1,5 +1,6 @@
FileVersion = 1 FileVersion = 1
Projects = {Cpp2Beef = {Path = "."}} Projects = {Cpp2Beef = {Path = "."}, "Clang-C.git" = {Git = "https://git.unicon-gmbh.de/BeefBindings/Clang-C.git"}}
Unlocked = ["Clang-C.git"]
[Workspace] [Workspace]
StartupProject = "Cpp2Beef" StartupProject = "Cpp2Beef"

6
BeefSpace_Lock.toml Normal file
View File

@@ -0,0 +1,6 @@
FileVersion = 1
[Locks."Clang-C.git".Git]
URL = "https://git.unicon-gmbh.de/BeefBindings/Clang-C.git"
Tag = ""
Hash = "a64e7a0cac26e4844385b87e595e27e95b46e5a0"

1
Clang-C Submodule

Submodule Clang-C added at 86db0167f1

6
CxxBuilder/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
build
recovery
test
BeefSpace_User.toml
dist/*

29
CxxBuilder/BeefProj.toml Normal file
View File

@@ -0,0 +1,29 @@
FileVersion = 1
[Project]
Name = "CxxBuilder"
StartupObject = "CxxBuilder.Program"
[Configs.Debug.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Debug.Win64]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Release.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Release.Win64]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Paranoid.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Paranoid.Win64]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Test.Win32]
TargetDirectory = "$(ProjectDir)/dist"
[Configs.Test.Win64]
TargetDirectory = "$(ProjectDir)/dist"

View File

@@ -0,0 +1,5 @@
FileVersion = 1
Projects = {CxxBuilder = {Path = "."}}
[Workspace]
StartupProject = "CxxBuilder"

View File

@@ -0,0 +1,137 @@
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
namespace CxxBuilder;
static class FileMatcher
{
public static void HandleMatches(Span<StringView> patterns, StringView directory, delegate void(StringView match) callback)
{
Runtime.Assert(Directory.Exists(directory), scope $"No such directory {directory}");
void Dir(StringView dir)
{
for (let element in Directory.Enumerate(dir))
{
let path = element.GetFilePath(..scope .());
path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
if (element.IsDirectory)
{
Dir(path);
continue;
}
let relpath = Path.GetRelativePath(path, directory, ..scope .());
for (let pattern in patterns)
if (IsMatch(pattern, relpath))
{
callback(relpath);
break;
}
}
}
Dir(directory);
}
public static bool IsMatch(StringView pattern, StringView path)
{
var pattern, path;
bool Matches(StringView pattern, char8 c)
{
if (pattern.IsEmpty) return false;
switch (pattern[0])
{
case '*':
if (pattern.Length > 1 && pattern[1] == '*')
return true;
return !Path.IsDirectorySeparatorChar(c);
case '?': return true;
case '\\':
Runtime.Assert(pattern.Length > 1, "Expected escape sequence");
return c == pattern[1];
case '/': return Path.IsDirectorySeparatorChar(c);
case '[':
int i = 0;
char8 Next()
{
if (pattern.Length <= ++i)
Runtime.FatalError("Character class not closed");
return pattern[i];
}
bool negated = false;
char8 current;
switch (Next())
{
case '^', '!': negated = true; fallthrough;
case '\\': current = Next();
case ']': return false;
default: current = _;
}
while (true)
{
if (c == current) return !negated;
switch (Next())
{
case ']': return negated;
case '-':
let end = Next();
if (c >= current && c <= end)
return !negated;
current = Next();
case '\\': current = Next();
default: current = _;
}
}
default:
return c == _;
}
}
reduce: while (true)
{
if (path.IsEmpty) return true;
if (pattern.IsEmpty) return false;
if (!Matches(pattern, path[0])) return false;
switch (pattern[0])
{
case '*':
StringView lazy;
if (pattern.Length > 1)
{
lazy = pattern;
lazy.RemoveFromStart(pattern[1] == '*' ? 2 : 1);
}
else lazy = .();
defer { pattern = lazy; }
while (true)
{
if (IsMatch(lazy, path)) continue reduce;
if (!Matches(pattern, path[0])) continue reduce;
path.RemoveFromStart(1);
if (path.IsEmpty) return lazy.IsEmpty;
}
case '[':
bool escaped = false;
while (true)
{
let c = pattern[0];
pattern.RemoveFromStart(1);
if (c == ']' && !escaped)
{
path.RemoveFromStart(1);
continue reduce;
}
escaped = c == '\\';
}
case '\\':
pattern.RemoveFromStart(2);
path.RemoveFromStart(1);
default:
pattern.RemoveFromStart(1);
path.RemoveFromStart(1);
}
}
}
}

134
CxxBuilder/src/Program.bf Normal file
View File

@@ -0,0 +1,134 @@
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
namespace CxxBuilder;
static class Program
{
[CLink] static extern int32 system(char8*);
public static int Main(String[] args)
{
mixin PrintUsage()
{
Console.WriteLine("""
Simple and fast build tool using clang and ninja.
Usage: $(Var CxxBuilderExe) <patterns...> -- src=<> target=<> builddir=<> output=<> [cflags=<>]
Example: $(Var CxxBuilderExe) **.c vk_*.cpp -- "src=$(ProjectDir)/MyLib/src" target=$(TargetTriple) "builddir=$(BuildDir)" output=MyLib cflags=-Isome/dir
src - The source dir, all patterns are rooted here
target - the llvm target triple passed to clang
builddir - contains build artifacts and ninja files
output - the name of the output object archive $builddir/$output.lib on windows and $builddir/$output.a elsewhere
cflags - additional flags passed to clang
""");
return 1;
}
StringView src = null, target = null, builddir = null, output = null, cflags = null;
var iter = args.GetEnumerator();
skipPatterns: do
{
for (let arg in iter)
if (arg == "--") break skipPatterns;
PrintUsage!();
}
for (let arg in iter)
{
var parts = arg.Split('=', 2);
StringView key = parts.GetNext().Value;
StringView value;
switch (parts.GetNext())
{
case .Err: PrintUsage!();
case .Ok(out value):
}
key.Trim();
value.Trim();
switch (key)
{
case "src": Runtime.Assert(src.IsNull, "Duplicate var 'src'"); src = value;
case "target": Runtime.Assert(target.IsNull, "Duplicate var 'target'"); target = value;
case "builddir": Runtime.Assert(builddir.IsNull, "Duplicate var 'builddir'"); builddir = value;
case "output": Runtime.Assert(output.IsNull, "Duplicate var 'output'"); output = value;
case "cflags": Runtime.Assert(cflags.IsNull, "Duplicate var 'cflags'"); cflags = value;
default: PrintUsage!();
}
}
Runtime.Assert(!src.IsNull, "Missing var 'src'");
Runtime.Assert(!target.IsNull, "Missing var 'target'");
Runtime.Assert(!builddir.IsNull, "Missing var 'builddir'");
Runtime.Assert(!builddir.IsNull, "Missing var 'output'");
{
StreamWriter writer = scope .()..Create(scope $"{builddir}/build.ninja");
{
String buffer = scope .(1024);
let currentDir = Directory.GetCurrentDirectory(..scope .(128));
void WriteVarPath(StringView key, StringView value)
{
buffer.Append(key);
buffer.Append(" = ");
Path.GetAbsolutePath(value, currentDir, buffer);
buffer.Append('\n');
}
WriteVarPath("src", src);
WriteVarPath("builddir", builddir);
buffer.AppendF($"""
target = {target}
cflags = {cflags}
""");
writer.Write(buffer);
}
writer.Write("""
\n
cc = clang
ar = ar
rule cc
command = $cc $cflags -target $target -MD -MF $out.d -c -o $out $in
deps = gcc
depfile = $out.d
description = Building $in
rule ar
command = $ar crs $out $in
description = Creating static lib $out
""");
String ar = scope .(1024);
ar.AppendF($"\nbuild $builddir/{output}.{target.Contains("windows") ? "lib" : "a"}: ar");
int pcount = 0;
for (let arg in args)
{
if (arg == "--") break;
pcount++;
}
StringView[] patterns = scope .[pcount];
for (var pattern in ref patterns)
{
pattern = args[@pattern];
}
void HandleMatch(StringView match)
{
writer.Write($"build $builddir/{match}.o: cc $src/{match}\n");
ar.Append(" $builddir/", match, ".o");
}
let abssrc = Path.GetAbsolutePath(src, Directory.GetCurrentDirectory(..scope .(128)), ..scope .(128));
FileMatcher.HandleMatches(patterns, abssrc, scope => HandleMatch);
writer.WriteLine(ar);
}
let ret = system(scope $"ninja -C {builddir}");
if (ret != 0)
Console.WriteLine($"Failed to build {output}");
return ret;
}
}

3
Setup/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
build
recovery
BeefSapce_user.toml

5
Setup/BeefProj.toml Normal file
View File

@@ -0,0 +1,5 @@
FileVersion = 1
[Project]
Name = "Cpp2Beef.Setup"
StartupObject = "Cpp2Beef.Setup.Program"

5
Setup/BeefSpace.toml Normal file
View File

@@ -0,0 +1,5 @@
FileVersion = 1
Projects = {"Cpp2Beef.Setup" = {Path = "."}}
[Workspace]
StartupProject = "Cpp2Beef.Setup"

27
Setup/src/Program.bf Normal file
View File

@@ -0,0 +1,27 @@
using System;
using System.IO;
namespace Cpp2Beef.Setup;
static class Program
{
[CLink] static extern int32 system(char8*);
static void RunCommand(char8* cmd)
{
Console.WriteLine($"> {StringView(cmd)}");
if (system(cmd) != 0)
Runtime.FatalError("Command failed");
}
public static int Main(String[] args)
{
RunCommand("beefbuild -workspace=\"../CxxBuilder\" -config=Release");
String setCxxBuilderExe = scope .(512);
Path.GetAbsolutePath("../CxxBuilder/dist/CxxBuilder", Directory.GetCurrentDirectory(..scope .(128)), setCxxBuilderExe);
#if BF_PLATFORM_WINDOWS
setCxxBuilderExe.Append(".exe");
#endif
File.WriteAllText("../CxxBuilder/dist/CxxBuilderPath.txt", setCxxBuilderExe);
return 0;
}
}

171
src/ClangC.bf Normal file
View File

@@ -0,0 +1,171 @@
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using Cpp2Beef;
using LibClang;
namespace ClangC.Setup;
class ClangCGenerator : Cpp2BeefGenerator, this(Span<char8*> args)
{
protected override Span<char8*> Args => args;
protected override Cpp2BeefGenerator.Flags Flags => .None;
public Dictionary<String, StreamWriter> writers = new .(8) ~ DeleteDictionaryAndKeysAndValues!(_);
protected override StreamWriter GetWriterForHeader(StringView header)
{
if (header.IsNull || !header.Contains("clang-c")) return null;
let headerPath = Path.GetActualPathName(header, ..scope .(header.Length));
if (writers.TryAddAlt(headerPath, let keyPtr, let valuePtr))
{
String outPath = scope .(64);
outPath.Append("../Cpp2Beef_dist/Clang-C/src/");
Path.GetFileNameWithoutExtension(headerPath, outPath);
outPath.Append(".bf");
*keyPtr = new .(headerPath);
*valuePtr = new .()..Create(outPath)..Write("""
// This file was auto-generated by Cpp2Beef
using System;
using System.Interop;
namespace LibClang;
""");
}
return *valuePtr;
}
protected override Block GetCursorBlock(CXCursor cursor)
{
if (cursor.kind == .FunctionDecl)
return .CustomBlock("Clang");
return base.GetCursorBlock(cursor);
}
protected override 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 ||
ScopeCXString!(Clang.ParamCommandComment_GetParamName(param)) != spelling) continue;
let paragraph = Clang.BlockCommandComment_GetParagraph(param);
if (Clang.Comment_GetNumChildren(paragraph) == 0) continue;
let paragraphChild = Clang.Comment_GetChild(paragraph, 0);
if (Clang.Comment_GetKind(paragraphChild) != .Text) continue;
StringView paragraphText = ScopeCXString!(Clang.TextComment_GetText(paragraphChild));
if (paragraphText..TrimStart().StartsWith("[out]")) return true;
else break;
}
return base.IsOutParam(arg, method);
}
protected override void GetNameInBindings(CXCursor cursor, String outString)
{
switch (cursor.kind)
{
case .FunctionDecl:
StringView spelling = GetCursorSpelling!(cursor);
if (spelling.StartsWith("clang_")) spelling.RemoveFromStart(6);
bool upper = true;
for (let c in spelling)
{
if (upper)
{
outString.Append(c.ToUpper);
upper = false;
}
else
outString.Append(c);
if (c == '_') upper = true;
}
case .EnumConstantDecl:
let parentSpelling = GetCursorSpelling!(Clang.GetCursorSemanticParent(cursor));
var spelling = GetCursorSpelling!(cursor);
int i = 0;
for (; i < parentSpelling.Length && i < spelling.Length
&& parentSpelling[i] == spelling[i]; i++) {}
spelling.RemoveFromStart(i);
if (spelling.StartsWith('_')) spelling.RemoveFromStart(1);
Compiler.Identifier.GetSourceName(spelling, outString);
default:
base.GetNameInBindings(cursor, outString);
}
}
protected override void HandleCursor(CXCursor cursor)
{
if (cursor.kind == .TypedefDecl) do
{
let type = Clang.GetTypedefDeclUnderlyingType(cursor);
if (type.kind != .Pointer) break;
let pointee = Clang.GetPointeeType(type);
if (pointee.kind == .FunctionProto || pointee.kind == .FunctionNoProto ||
(pointee.kind == .Void && GetCursorSpelling!(cursor) == "CXClientData")) break;
BeginCursor(cursor);
AccessSpecifier(cursor);
str.Append("struct ");
GetNameInBindings(cursor, str);
str.Append(" : int {}");
return;
}
base.HandleCursor(cursor);
}
protected override void WriteCustomAttributes(CXCursor cursor)
{
if (cursor.kind == .FunctionDecl)
str.Append("[Import(Clang.dll)] ");
base.WriteCustomAttributes(cursor);
}
}
static class Program
{
[CLink] extern static int32 system(char8*);
const String[?] clangFiles = .(
"BuildSystem",
"CXCompilationDatabase",
"CXDiagnostic",
"CXErrorCode",
"CXFile",
"CXSourceLocation",
"CXString",
"Documentation",
"ExternC",
"FatalErrorHandler",
"Index",
"Platform",
"Rewrite",
);
const String clangHeaders = "./Clang-C/clang-c";
const String IclangHeaders = "-IClang-C";
public static int Main(String[] args)
{
ClangCGenerator generator = scope .(char8*[?]("--language=c", IclangHeaders));
Directory.CreateDirectory(clangHeaders);
{
StreamWriter includeAll = scope .()..Create("clang-c.h");
for (let file in clangFiles)
{
//if (system(scope $"curl -o {clangHeaders}/{file}.h https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang/include/clang-c/{file}.h") != 0)
// Runtime.FatalError(scope $"Failed to download clang-c/{file}");
includeAll.Write($"#include <clang-c/{file}.h>\n");
}
}
Directory.CreateDirectory("clang-c");
generator.Generate("clang-c.h", null);
return 0;
}
}

View File

@@ -38,6 +38,8 @@ abstract class Cpp2BeefGenerator
case "uint16_t": outString.Append("uint16"); case "uint16_t": outString.Append("uint16");
case "uint32_t": outString.Append("uint32"); case "uint32_t": outString.Append("uint32");
case "uint64_t": outString.Append("uint64"); case "uint64_t": outString.Append("uint64");
case "intptr_t": outString.Append("c_intptr");
case "uintptr_t": outString.Append("c_uintptr");
default: fallthrough getName; default: fallthrough getName;
} }
default: default:
@@ -93,7 +95,7 @@ abstract class Cpp2BeefGenerator
} }
} }
void WriteBlock(Block block) protected void WriteBlock(Block block)
{ {
StringView target; StringView target;
switch (block) switch (block)
@@ -131,6 +133,8 @@ abstract class Cpp2BeefGenerator
switch (cursor.kind) switch (cursor.kind)
{ {
case .StructDecl, .ClassDecl, .UnionDecl, .EnumDecl: case .StructDecl, .ClassDecl, .UnionDecl, .EnumDecl:
if (Clang.Cursor_IsAnonymous(cursor) != 0)
return;
if (templateParams.IsEmpty && Clang.EqualCursors(cursor, Clang.GetTypeDeclaration(Clang.GetCursorType(cursor))) == 0) if (templateParams.IsEmpty && Clang.EqualCursors(cursor, Clang.GetTypeDeclaration(Clang.GetCursorType(cursor))) == 0)
return; return;
BeginCursor(cursor); BeginCursor(cursor);
@@ -170,7 +174,7 @@ abstract class Cpp2BeefGenerator
MacroDefinition(cursor); MacroDefinition(cursor);
case .InclusionDirective, .MacroExpansion, .CXXBaseSpecifier, .CXXAccessSpecifier, case .InclusionDirective, .MacroExpansion, .CXXBaseSpecifier, .CXXAccessSpecifier,
.LinkageSpec, .TemplateTypeParameter, .NonTypeTemplateParameter: // ignored .LinkageSpec, .TemplateTypeParameter, .NonTypeTemplateParameter, .StaticAssert: // ignored
default: Debug.WriteLine(scope $"Unhandled cursor: {_}"); default: Debug.WriteLine(scope $"Unhandled cursor: {_}");
} }
} }
@@ -222,7 +226,8 @@ abstract class Cpp2BeefGenerator
protected class FileInfo : this(CXSourceLocation prevEnd, CXFile file) protected class FileInfo : this(CXSourceLocation prevEnd, CXFile file)
{ {
public append String block = .(64); public append String block = .(64);
public enum { None, LSquirly, RSquirly } queuedToken = .None; public enum { None = 0, LSquirly = 1, RSquirly = 0b10 } queuedTokens = .None;
public (CXType type, int32 curWidth) bitfield = default;
} }
protected FileInfo fileInfo = null; protected FileInfo fileInfo = null;
private Dictionary<String, FileInfo> fileInfos = new .(32) ~ DeleteDictionaryAndKeysAndValues!(_); private Dictionary<String, FileInfo> fileInfos = new .(32) ~ DeleteDictionaryAndKeysAndValues!(_);
@@ -251,6 +256,18 @@ abstract class Cpp2BeefGenerator
ScopeCXString!:mixin(Clang.GetTokenSpelling(unit, 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 PreGeneration() {}
protected virtual void PostGeneration() {} protected virtual void PostGeneration() {}
@@ -305,7 +322,7 @@ abstract class Cpp2BeefGenerator
#endif #endif
CXTranslationUnit_Flags unitFlags = .SkipFunctionBodies | .DetailedPreprocessingRecord; CXTranslationUnit_Flags unitFlags = .SkipFunctionBodies | .DetailedPreprocessingRecord;
unit = Clang.ParseTranslationUnit(index, headerPath, args.Ptr, (.)args.Length, null, 0, (.)unitFlags); unit = Clang.ParseTranslationUnit(index, headerPath, args.Ptr, (.)args.Length, null, 0, (.)unitFlags);
if (unit == null) return .Err(.ParsingFailed); if (unit == default) return .Err(.ParsingFailed);
printingPolicy = Clang.GetCursorPrintingPolicy(Clang.GetTranslationUnitCursor(unit)); printingPolicy = Clang.GetCursorPrintingPolicy(Clang.GetTranslationUnitCursor(unit));
ModifyWrapperPrintingPolicy(printingPolicy); ModifyWrapperPrintingPolicy(printingPolicy);
@@ -318,14 +335,14 @@ abstract class Cpp2BeefGenerator
{ {
let location = Clang.GetCursorLocation(cursor); let location = Clang.GetCursorLocation(cursor);
Clang.GetSpellingLocation(location, let file, let line, ?, ?); Clang.GetFileLocation(location, let file, let line, ?, ?);
let header = ScopeCXString!(Clang.GetFileName(file)); let header = ScopeCXString!(Clang.GetFileName(file));
self.currentWritter = self.GetWriterForHeader(header); self.currentWritter = self.GetWriterForHeader(header);
if (self.currentWritter == null) return .Continue; if (self.currentWritter == null) return .Continue;
if (cursor.kind == .MacroDefinition) if (cursor.kind == .MacroDefinition)
{ {
self.unitMacros.Add(.(line, file), cursor); self.unitMacros[.(line, file)] = cursor;
return .Continue; return .Continue;
} }
@@ -429,7 +446,7 @@ abstract class Cpp2BeefGenerator
protected void FatalErrorMsg(String outString) protected void FatalErrorMsg(String outString)
{ {
Clang.GetSpellingLocation(Clang.GetCursorLocation(currentCursor), let file, let line, let col, ?); Clang.GetFileLocation(Clang.GetCursorLocation(currentCursor), let file, let line, let col, ?);
outString.Append("in cursor '", GetCursorSpelling!(currentCursor), "' at line "); outString.Append("in cursor '", GetCursorSpelling!(currentCursor), "' at line ");
outString.AppendF($"{line}:{col}"); outString.AppendF($"{line}:{col}");
outString.Append(" in file ", GetFilePath!(file)); outString.Append(" in file ", GetFilePath!(file));
@@ -451,7 +468,7 @@ abstract class Cpp2BeefGenerator
StringView file; StringView file;
uint32 offset; uint32 offset;
{ {
Clang.GetSpellingLocation(location, let cxfile, ?, ?, out offset); Clang.GetFileLocation(location, let cxfile, ?, ?, out offset);
char8* ptr = Clang.GetFileContents(unit, cxfile, let size); char8* ptr = Clang.GetFileContents(unit, cxfile, let size);
file = .(ptr, (.)size); file = .(ptr, (.)size);
} }
@@ -467,7 +484,7 @@ abstract class Cpp2BeefGenerator
StringView file; StringView file;
uint32 offset; uint32 offset;
{ {
Clang.GetSpellingLocation(from, let cxfile, ?, ?, out offset); Clang.GetFileLocation(from, let cxfile, ?, ?, out offset);
char8* ptr = Clang.GetFileContents(unit, cxfile, let size); char8* ptr = Clang.GetFileContents(unit, cxfile, let size);
file = .(ptr, (.)size); file = .(ptr, (.)size);
} }
@@ -487,8 +504,8 @@ abstract class Cpp2BeefGenerator
Clang.EqualLocations(end, Clang.GetNullLocation()) != 0) return; Clang.EqualLocations(end, Clang.GetNullLocation()) != 0) return;
StringView file; StringView file;
Clang.GetSpellingLocation(start, let startFile, ?, ?, let startOffset); Clang.GetFileLocation(start, let startFile, ?, ?, let startOffset);
Clang.GetSpellingLocation(end, let endfile, ?, ?, let endOffset); Clang.GetFileLocation(end, let endfile, ?, ?, let endOffset);
if (Clang.File_IsEqual(startFile, endfile) == 0) return; if (Clang.File_IsEqual(startFile, endfile) == 0) return;
char8* ptr = Clang.GetFileContents(unit, startFile, let size); char8* ptr = Clang.GetFileContents(unit, startFile, let size);
file = .(ptr, (.)size); file = .(ptr, (.)size);
@@ -501,24 +518,35 @@ abstract class Cpp2BeefGenerator
} }
} }
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) protected virtual void WriteComments(CXCursor cursor, Block? block = null)
{ {
let location = GetCursorAnchor(cursor);
if (cursor.kind == .MacroDefinition) if (cursor.kind == .MacroDefinition)
{ {
GetIndentation(Clang.GetCursorLocation(cursor), str); if (block.HasValue) WriteBlock(block.Value);
GetIndentation(location, str);
return; return;
} }
cursorIndent.Clear(); cursorIndent.Clear();
GetIndentation(Clang.GetCursorLocation(cursor), cursorIndent); GetIndentation(location, cursorIndent);
WriteComments(Clang.GetCursorLocation(cursor), block); WriteComments(location, block);
} }
protected virtual void WriteComments(CXSourceLocation writeUntil, Block? block = null) protected virtual void WriteComments(CXSourceLocation writeUntil, Block? block = null)
{ {
Clang.GetSpellingLocation(writeUntil, let curFile, let curLine, let curColumn, let curOffset); Clang.GetFileLocation(writeUntil, let curFile, let curLine, let curColumn, let curOffset);
Clang.GetSpellingLocation(fileInfo.prevEnd, let prevFile, let prevLine, let prevColumn, let prevOffset); Clang.GetFileLocation(fileInfo.prevEnd, let prevFile, let prevLine, let prevColumn, let prevOffset);
defer { fileInfo.prevEnd = writeUntil; } defer { fileInfo.prevEnd = writeUntil; }
if (Clang.File_IsEqual(curFile, prevFile) == 0) Runtime.FatalError(); if (Clang.File_IsEqual(curFile, prevFile) == 0) Runtime.FatalError(scope $"{GetFilePath!(curFile)} != {GetFilePath!(prevFile)}");
var between = Clang.GetRange( var between = Clang.GetRange(
Clang.GetLocationForOffset(unit, prevFile, prevOffset), Clang.GetLocationForOffset(unit, prevFile, prevOffset),
@@ -579,18 +607,23 @@ abstract class Cpp2BeefGenerator
{ {
case .Comment: case .Comment:
case .Punctuation: case .Punctuation:
switch (fileInfo.queuedToken) mixin QueuedToken(var queuedToken, StringView spelling)
{ {
case .LSquirly: if (GetTokenSpelling!(token) != "{") continue; if (fileInfo.queuedTokens.HasFlag(queuedToken) && GetTokenSpelling!(token) == spelling)
case .RSquirly: if (GetTokenSpelling!(token) != "}") continue; {
case .None: continue; fileInfo.queuedTokens ^= queuedToken;
}
fileInfo.queuedToken = .None;
isWritingQueuedToken = true; isWritingQueuedToken = true;
}
}
QueuedToken!(decltype(fileInfo.queuedTokens).LSquirly, "{");
QueuedToken!(decltype(fileInfo.queuedTokens).RSquirly, "}");
if (!isWritingQueuedToken) continue;
default: continue; default: continue;
} }
let location = Clang.GetTokenLocation(unit, token); let location = Clang.GetTokenLocation(unit, token);
Clang.GetSpellingLocation(location, ?, let startLine, ?, ?); Clang.GetFileLocation(location, ?, let startLine, ?, ?);
WriteLinesUntil!(startLine, location); WriteLinesUntil!(startLine, location);
if (!isWritingQueuedToken) if (!isWritingQueuedToken)
{ {
@@ -671,6 +704,13 @@ abstract class Cpp2BeefGenerator
case .StructDecl, .ClassDecl, .UnionDecl: Record(decl); case .StructDecl, .ClassDecl, .UnionDecl: Record(decl);
default: Runtime.FatalError(scope $"Unhandled anon type: {_}"); 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; break;
} }
@@ -751,6 +791,7 @@ abstract class Cpp2BeefGenerator
{ {
if (Clang.GetTokenKind(token) == .Punctuation && { let spelling = GetTokenSpelling!(token); spelling == "," || spelling == ")" }) if (Clang.GetTokenKind(token) == .Punctuation && { let spelling = GetTokenSpelling!(token); spelling == "," || spelling == ")" })
{ {
if (Clang.GetTokenKind(last) != .Identifier) break;
str.Append(' '); str.Append(' ');
str.Append(GetTokenSpelling!(last)); str.Append(GetTokenSpelling!(last));
break; break;
@@ -779,26 +820,27 @@ abstract class Cpp2BeefGenerator
protected mixin BeginBody(CXCursor cursor) protected mixin BeginBody(CXCursor cursor)
{ {
fileInfo.prevEnd = Clang.GetCursorLocation(cursor); fileInfo.prevEnd = GetCursorAnchor(cursor);
fileInfo.queuedToken = .LSquirly; fileInfo.queuedTokens |= .LSquirly;
// [Friend] is needed in case you use BeginBody! in your code
if (canChangeBlock) if (canChangeBlock)
{ {
defer:mixin { canChangeBlock = true; } defer:mixin { this.[Friend]canChangeBlock = true; }
canChangeBlock = false; this.[Friend]canChangeBlock = false;
} }
defer:mixin defer:mixin
{ {
if (defferedWrapperWrite != null) if (this.[Friend]defferedWrapperWrite != null)
{ {
str.Append("\n\n", cursorIndent, "const String _wrapperText = \"\\n\"\n", str.Append("\n\n", cursorIndent, "const String _wrapperText = \"\\n\"\n",
defferedWrapperWrite, ";"); this.[Friend]defferedWrapperWrite, ";");
delete defferedWrapperWrite; delete this.[Friend]defferedWrapperWrite;
defferedWrapperWrite = null; this.[Friend]defferedWrapperWrite = null;
} }
if (fileInfo.queuedToken == .LSquirly) if (fileInfo.queuedTokens.HasFlag(.LSquirly))
{ {
switch (cursor.kind) switch (cursor.kind)
{ {
@@ -807,11 +849,10 @@ abstract class Cpp2BeefGenerator
default: default:
str.Append(" {}"); str.Append(" {}");
} }
fileInfo.queuedToken = .None; fileInfo.queuedTokens = .None;
} }
else else
fileInfo.queuedToken = .RSquirly; fileInfo.queuedTokens |= .RSquirly;
} }
} }
@@ -1053,7 +1094,7 @@ abstract class Cpp2BeefGenerator
} }
if (Clang.Cursor_IsInlineNamespace(cursor) != 0) if (Clang.Cursor_IsInlineNamespace(cursor) != 0)
{ {
GetIndentation(Clang.GetCursorLocation(cursor), str); GetIndentation(GetCursorAnchor(cursor), str);
str.Append("public static using "); str.Append("public static using ");
GetNameInBindings(cursor, str); GetNameInBindings(cursor, str);
str.Append(";\n"); str.Append(";\n");
@@ -1215,11 +1256,61 @@ abstract class Cpp2BeefGenerator
str.Append(';'); 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) 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); WriteCustomAttributes(cursor);
AccessSpecifier(cursor); AccessSpecifier(cursor);
WriteTypeAndName(cursor, Clang.GetCursorType(cursor)); WriteTypeAndName(cursor, type);
str.Append(';'); str.Append(';');
} }
@@ -1380,11 +1471,16 @@ abstract class Cpp2BeefGenerator
Clang.VisitChildren(cursor, (cursor, parent, client_data) => Clang.VisitChildren(cursor, (cursor, parent, client_data) =>
{ {
Self self = (.)Internal.UnsafeCastToObject(client_data); Self self = (.)Internal.UnsafeCastToObject(client_data);
if (self.fileInfo.bitfield != default && Clang.Cursor_IsBitField(cursor) == 0)
self.DumpBitfieldStorage();
self.WriteCursor(cursor); self.WriteCursor(cursor);
if (Clang.Cursor_IsAnonymousRecordDecl(cursor) != 0) if (Clang.Cursor_IsAnonymousRecordDecl(cursor) != 0)
self.str..TrimEnd()..Append(";"); self.str..TrimEnd()..Append(";");
return .Continue; return .Continue;
}, Internal.UnsafeCastToPtr(this)); }, Internal.UnsafeCastToPtr(this));
if (fileInfo.bitfield != default)
DumpBitfieldStorage();
} }
protected virtual void Enum(CXCursor cursor) protected virtual void Enum(CXCursor cursor)