c part ready to go
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
build
|
||||
recovery
|
||||
BeefSpace_User.toml
|
||||
|
||||
clang-c.h
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "Clang-C"]
|
||||
path = Clang-C
|
||||
url = https://git.unicon-gmbh.de/BeefBindings/Clang-C.git
|
||||
@@ -1,6 +1,6 @@
|
||||
FileVersion = 1
|
||||
Dependencies = {corlib = "*", "Clang-C.git" = {Git = "https://git.unicon-gmbh.de/BeefBindings/Clang-C.git"}}
|
||||
|
||||
[Project]
|
||||
Name = "Cpp2Beef"
|
||||
TargetType = "BeefLib"
|
||||
StartupObject = "Cpp2Beef.Program"
|
||||
StartupObject = "ClangC.Setup.Program"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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]
|
||||
StartupProject = "Cpp2Beef"
|
||||
|
||||
6
BeefSpace_Lock.toml
Normal file
6
BeefSpace_Lock.toml
Normal 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
1
Clang-C
Submodule
Submodule Clang-C added at 86db0167f1
6
CxxBuilder/.gitignore
vendored
Normal file
6
CxxBuilder/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
build
|
||||
recovery
|
||||
test
|
||||
BeefSpace_User.toml
|
||||
|
||||
dist/*
|
||||
29
CxxBuilder/BeefProj.toml
Normal file
29
CxxBuilder/BeefProj.toml
Normal 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"
|
||||
5
CxxBuilder/BeefSpace.toml
Normal file
5
CxxBuilder/BeefSpace.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
FileVersion = 1
|
||||
Projects = {CxxBuilder = {Path = "."}}
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "CxxBuilder"
|
||||
137
CxxBuilder/src/FileMatcher.bf
Normal file
137
CxxBuilder/src/FileMatcher.bf
Normal 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
134
CxxBuilder/src/Program.bf
Normal 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
3
Setup/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
build
|
||||
recovery
|
||||
BeefSapce_user.toml
|
||||
5
Setup/BeefProj.toml
Normal file
5
Setup/BeefProj.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
FileVersion = 1
|
||||
|
||||
[Project]
|
||||
Name = "Cpp2Beef.Setup"
|
||||
StartupObject = "Cpp2Beef.Setup.Program"
|
||||
5
Setup/BeefSpace.toml
Normal file
5
Setup/BeefSpace.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
FileVersion = 1
|
||||
Projects = {"Cpp2Beef.Setup" = {Path = "."}}
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "Cpp2Beef.Setup"
|
||||
27
Setup/src/Program.bf
Normal file
27
Setup/src/Program.bf
Normal 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
171
src/ClangC.bf
Normal 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;
|
||||
}
|
||||
}
|
||||
172
src/Generator.bf
172
src/Generator.bf
@@ -38,6 +38,8 @@ abstract class Cpp2BeefGenerator
|
||||
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:
|
||||
@@ -93,7 +95,7 @@ abstract class Cpp2BeefGenerator
|
||||
}
|
||||
}
|
||||
|
||||
void WriteBlock(Block block)
|
||||
protected void WriteBlock(Block block)
|
||||
{
|
||||
StringView target;
|
||||
switch (block)
|
||||
@@ -131,6 +133,8 @@ abstract class Cpp2BeefGenerator
|
||||
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);
|
||||
@@ -170,7 +174,7 @@ abstract class Cpp2BeefGenerator
|
||||
MacroDefinition(cursor);
|
||||
|
||||
case .InclusionDirective, .MacroExpansion, .CXXBaseSpecifier, .CXXAccessSpecifier,
|
||||
.LinkageSpec, .TemplateTypeParameter, .NonTypeTemplateParameter: // ignored
|
||||
.LinkageSpec, .TemplateTypeParameter, .NonTypeTemplateParameter, .StaticAssert: // ignored
|
||||
default: Debug.WriteLine(scope $"Unhandled cursor: {_}");
|
||||
}
|
||||
}
|
||||
@@ -222,7 +226,8 @@ abstract class Cpp2BeefGenerator
|
||||
protected class FileInfo : this(CXSourceLocation prevEnd, CXFile file)
|
||||
{
|
||||
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;
|
||||
private Dictionary<String, FileInfo> fileInfos = new .(32) ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
@@ -251,6 +256,18 @@ abstract class Cpp2BeefGenerator
|
||||
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() {}
|
||||
|
||||
@@ -305,7 +322,7 @@ abstract class Cpp2BeefGenerator
|
||||
#endif
|
||||
CXTranslationUnit_Flags unitFlags = .SkipFunctionBodies | .DetailedPreprocessingRecord;
|
||||
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));
|
||||
ModifyWrapperPrintingPolicy(printingPolicy);
|
||||
@@ -318,14 +335,14 @@ abstract class Cpp2BeefGenerator
|
||||
|
||||
{
|
||||
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));
|
||||
self.currentWritter = self.GetWriterForHeader(header);
|
||||
if (self.currentWritter == null) return .Continue;
|
||||
|
||||
if (cursor.kind == .MacroDefinition)
|
||||
{
|
||||
self.unitMacros.Add(.(line, file), cursor);
|
||||
self.unitMacros[.(line, file)] = cursor;
|
||||
return .Continue;
|
||||
}
|
||||
|
||||
@@ -429,7 +446,7 @@ abstract class Cpp2BeefGenerator
|
||||
|
||||
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.AppendF($"{line}:{col}");
|
||||
outString.Append(" in file ", GetFilePath!(file));
|
||||
@@ -451,7 +468,7 @@ abstract class Cpp2BeefGenerator
|
||||
StringView file;
|
||||
uint32 offset;
|
||||
{
|
||||
Clang.GetSpellingLocation(location, let cxfile, ?, ?, out offset);
|
||||
Clang.GetFileLocation(location, let cxfile, ?, ?, out offset);
|
||||
char8* ptr = Clang.GetFileContents(unit, cxfile, let size);
|
||||
file = .(ptr, (.)size);
|
||||
}
|
||||
@@ -467,7 +484,7 @@ abstract class Cpp2BeefGenerator
|
||||
StringView file;
|
||||
uint32 offset;
|
||||
{
|
||||
Clang.GetSpellingLocation(from, let cxfile, ?, ?, out offset);
|
||||
Clang.GetFileLocation(from, let cxfile, ?, ?, out offset);
|
||||
char8* ptr = Clang.GetFileContents(unit, cxfile, let size);
|
||||
file = .(ptr, (.)size);
|
||||
}
|
||||
@@ -487,8 +504,8 @@ abstract class Cpp2BeefGenerator
|
||||
Clang.EqualLocations(end, Clang.GetNullLocation()) != 0) return;
|
||||
|
||||
StringView file;
|
||||
Clang.GetSpellingLocation(start, let startFile, ?, ?, let startOffset);
|
||||
Clang.GetSpellingLocation(end, let endfile, ?, ?, let endOffset);
|
||||
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);
|
||||
@@ -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)
|
||||
{
|
||||
let location = GetCursorAnchor(cursor);
|
||||
if (cursor.kind == .MacroDefinition)
|
||||
{
|
||||
GetIndentation(Clang.GetCursorLocation(cursor), str);
|
||||
if (block.HasValue) WriteBlock(block.Value);
|
||||
GetIndentation(location, str);
|
||||
return;
|
||||
}
|
||||
cursorIndent.Clear();
|
||||
GetIndentation(Clang.GetCursorLocation(cursor), cursorIndent);
|
||||
WriteComments(Clang.GetCursorLocation(cursor), block);
|
||||
GetIndentation(location, cursorIndent);
|
||||
WriteComments(location, block);
|
||||
}
|
||||
protected virtual void WriteComments(CXSourceLocation writeUntil, Block? block = null)
|
||||
{
|
||||
Clang.GetSpellingLocation(writeUntil, let curFile, let curLine, let curColumn, let curOffset);
|
||||
Clang.GetSpellingLocation(fileInfo.prevEnd, let prevFile, let prevLine, let prevColumn, let prevOffset);
|
||||
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();
|
||||
if (Clang.File_IsEqual(curFile, prevFile) == 0) Runtime.FatalError(scope $"{GetFilePath!(curFile)} != {GetFilePath!(prevFile)}");
|
||||
|
||||
var between = Clang.GetRange(
|
||||
Clang.GetLocationForOffset(unit, prevFile, prevOffset),
|
||||
@@ -579,18 +607,23 @@ abstract class Cpp2BeefGenerator
|
||||
{
|
||||
case .Comment:
|
||||
case .Punctuation:
|
||||
switch (fileInfo.queuedToken)
|
||||
mixin QueuedToken(var queuedToken, StringView spelling)
|
||||
{
|
||||
case .LSquirly: if (GetTokenSpelling!(token) != "{") continue;
|
||||
case .RSquirly: if (GetTokenSpelling!(token) != "}") continue;
|
||||
case .None: continue;
|
||||
}
|
||||
fileInfo.queuedToken = .None;
|
||||
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;
|
||||
default: continue;
|
||||
}
|
||||
let location = Clang.GetTokenLocation(unit, token);
|
||||
Clang.GetSpellingLocation(location, ?, let startLine, ?, ?);
|
||||
Clang.GetFileLocation(location, ?, let startLine, ?, ?);
|
||||
WriteLinesUntil!(startLine, location);
|
||||
if (!isWritingQueuedToken)
|
||||
{
|
||||
@@ -671,6 +704,13 @@ abstract class Cpp2BeefGenerator
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -751,6 +791,7 @@ abstract class Cpp2BeefGenerator
|
||||
{
|
||||
if (Clang.GetTokenKind(token) == .Punctuation && { let spelling = GetTokenSpelling!(token); spelling == "," || spelling == ")" })
|
||||
{
|
||||
if (Clang.GetTokenKind(last) != .Identifier) break;
|
||||
str.Append(' ');
|
||||
str.Append(GetTokenSpelling!(last));
|
||||
break;
|
||||
@@ -779,26 +820,27 @@ abstract class Cpp2BeefGenerator
|
||||
|
||||
protected mixin BeginBody(CXCursor cursor)
|
||||
{
|
||||
fileInfo.prevEnd = Clang.GetCursorLocation(cursor);
|
||||
fileInfo.queuedToken = .LSquirly;
|
||||
fileInfo.prevEnd = GetCursorAnchor(cursor);
|
||||
fileInfo.queuedTokens |= .LSquirly;
|
||||
|
||||
// [Friend] is needed in case you use BeginBody! in your code
|
||||
if (canChangeBlock)
|
||||
{
|
||||
defer:mixin { canChangeBlock = true; }
|
||||
canChangeBlock = false;
|
||||
defer:mixin { this.[Friend]canChangeBlock = true; }
|
||||
this.[Friend]canChangeBlock = false;
|
||||
}
|
||||
|
||||
defer:mixin
|
||||
{
|
||||
if (defferedWrapperWrite != null)
|
||||
if (this.[Friend]defferedWrapperWrite != null)
|
||||
{
|
||||
str.Append("\n\n", cursorIndent, "const String _wrapperText = \"\\n\"\n",
|
||||
defferedWrapperWrite, ";");
|
||||
delete defferedWrapperWrite;
|
||||
defferedWrapperWrite = null;
|
||||
this.[Friend]defferedWrapperWrite, ";");
|
||||
delete this.[Friend]defferedWrapperWrite;
|
||||
this.[Friend]defferedWrapperWrite = null;
|
||||
}
|
||||
|
||||
if (fileInfo.queuedToken == .LSquirly)
|
||||
if (fileInfo.queuedTokens.HasFlag(.LSquirly))
|
||||
{
|
||||
switch (cursor.kind)
|
||||
{
|
||||
@@ -807,11 +849,10 @@ abstract class Cpp2BeefGenerator
|
||||
default:
|
||||
str.Append(" {}");
|
||||
}
|
||||
fileInfo.queuedToken = .None;
|
||||
fileInfo.queuedTokens = .None;
|
||||
}
|
||||
else
|
||||
fileInfo.queuedToken = .RSquirly;
|
||||
|
||||
fileInfo.queuedTokens |= .RSquirly;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1053,7 +1094,7 @@ abstract class Cpp2BeefGenerator
|
||||
}
|
||||
if (Clang.Cursor_IsInlineNamespace(cursor) != 0)
|
||||
{
|
||||
GetIndentation(Clang.GetCursorLocation(cursor), str);
|
||||
GetIndentation(GetCursorAnchor(cursor), str);
|
||||
str.Append("public static using ");
|
||||
GetNameInBindings(cursor, str);
|
||||
str.Append(";\n");
|
||||
@@ -1215,11 +1256,61 @@ abstract class Cpp2BeefGenerator
|
||||
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, Clang.GetCursorType(cursor));
|
||||
WriteTypeAndName(cursor, type);
|
||||
str.Append(';');
|
||||
}
|
||||
|
||||
@@ -1380,11 +1471,16 @@ abstract class Cpp2BeefGenerator
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user