This commit is contained in:
Asuro
2025-03-07 00:37:44 +01:00
parent 467a4a0491
commit 96a122f932
17 changed files with 944 additions and 16 deletions

View File

@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.10)
project(MiniDefProj)
add_executable(minidef "${CMAKE_CURRENT_SOURCE_DIR}/src/MiniDef.cpp")
target_include_directories(minidef PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../bgfx.cmake/bx/include/"
"${CMAKE_CURRENT_SOURCE_DIR}/../bgfx.cmake/bx/include/compat/msvc/"
)
target_link_libraries(minidef bx)
target_compile_definitions(minidef PRIVATE "$<$<CONFIG:DEBUG>:BX_CONFIG_DEBUG=1>$<$<CONFIG:RELEASE>:BX_CONFIG_DEBUG=0>")

View File

@@ -0,0 +1,21 @@
type Texture
{
u32 Width
u32 Height
str Test Arr(3)
}
enum KnownType
{
i8 CName("int8_t")
i16 CName("int16_t")
i32 CName("int32_t")
i64 CName("int64_t")
u8 CName("uint8_t")
u16 CName("uint16_t")
u32 CName("uint32_t")
u64 CName("uint64_t")
f32 CName("float")
f64 CName("double")
str CName("char")
}

View File

@@ -0,0 +1,2 @@
#pragma once
#include <cstdint>

View File

@@ -0,0 +1,58 @@
#pragma once
#include "Def.h"
namespace Generated
{
struct Texture
{
uint32_t Width = {};
uint32_t Height = {};
char Test[3] = {};
};
struct KnownType
{
static constexpr int32_t EntryCount = 11;
enum class Enum
{
i8,
i16,
i32,
i64,
u8,
u16,
u32,
u64,
f32,
f64,
str,
};
static constexpr char EntryNames[EntryCount][64]
{
"i8",
"i16",
"i32",
"i64",
"u8",
"u16",
"u32",
"u64",
"f32",
"f64",
"str",
};
static constexpr char CName[EntryCount][64]
{
"int8_t",
"int16_t",
"int32_t",
"int64_t",
"uint8_t",
"uint16_t",
"uint32_t",
"uint64_t",
"float",
"double",
"char",
};
};
}

View File

@@ -0,0 +1,503 @@
#include "MiniDef.h"
#include "bx/string.h"
#include <bx/filepath.h>
#include <ctype.h>
#include <filesystem>
namespace WriteTemplates
{
constexpr char FileHeader[] =
R"END(#pragma once
#include "Def.h"
namespace Generated
{
)END";
constexpr char StructHeader1[] =
R"END( struct %s
{
)END";
constexpr char StructField4[] =
R"END( %s %s%s = %s;
)END";
constexpr char StructEnd[] =
R"END( };
)END";
constexpr char EnumHeader2[] =
R"END( struct %s
{
static constexpr int32_t EntryCount = %u;
enum class Enum
{
)END";
constexpr char EnumField1[] =
R"END( %s,
)END";
constexpr char EnumNamesStart2[] =
R"END( };
static constexpr char %s[EntryCount][%u]
{
)END";
constexpr char EnumNamesEntry1[] =
R"END( "%s",
)END";
constexpr char EnumNamesEnd[] =
R"END( };
};
)END";
constexpr char FileEnd[] =
R"END(}
)END";
} // namespace WriteTemplates
void DefinitionFile::PrintTypeName(char* buf, int32_t bufSize, Def::TypeUnion type)
{
if (int32_t(type.Known) < Generated::KnownType::EntryCount)
{
bx::strCopy(buf, bufSize, Generated::KnownType::CName[size_t(type.Known)]);
}
}
void DefinitionFile::GenerateCpp(const bx::FilePath& outDir)
{
bx::Error error;
bx::FileWriter writer;
bx::FilePath writePath = outDir;
std::filesystem::create_directory(outDir.getCPtr());
writePath.join("Generated.h");
writer.open(writePath, false, &error);
TemplateWriter Template{writer, error};
Template.Write(WriteTemplates::FileHeader);
for (int32_t typeIdx = 0; typeIdx < TypeCount; ++typeIdx)
{
Def::Type& t = Types[typeIdx];
Template.Write(WriteTemplates::StructHeader1, t.Name);
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
char Type[64]{0};
PrintTypeName(Type, sizeof(Type), t.FieldTypes[fieldIdx]);
char Array[32]{0};
uint32_t ArraySize = t.FieldArraySizes[fieldIdx];
if (ArraySize > 0)
{
bx::snprintf(Array, sizeof(Array), "[%u]", ArraySize);
}
Template.Write(WriteTemplates::StructField4, Type, t.FieldNames[fieldIdx], Array, "{}");
}
Template.Write(WriteTemplates::StructEnd);
}
for (int32_t enumIdx = 0; enumIdx < EnumCount; ++enumIdx)
{
Def::Enum& e = Enums[enumIdx];
Template.Write(WriteTemplates::EnumHeader2, e.Name, e.EntryCount);
for (int32_t entryIdx = 0; entryIdx < e.EntryCount; ++entryIdx)
{
Template.Write(WriteTemplates::EnumField1, e.EntryNames[entryIdx]);
}
Template.Write(WriteTemplates::EnumNamesStart2, "EntryNames", Def::MaxNameLength);
for (int32_t entryIdx = 0; entryIdx < e.EntryCount; ++entryIdx)
{
Template.Write(WriteTemplates::EnumNamesEntry1, e.EntryNames[entryIdx]);
}
for (int32_t extraIdx = 0; extraIdx < e.ExtraStringFieldCount; ++extraIdx)
{
Template.Write(WriteTemplates::EnumNamesStart2, e.ExtraStringFieldNames[extraIdx], Def::MaxNameLength);
for (int32_t entryIdx = 0; entryIdx < e.EntryCount; ++entryIdx)
{
Template.Write(WriteTemplates::EnumNamesEntry1, e.ExtraStringFields[entryIdx][extraIdx]);
}
}
Template.Write(WriteTemplates::EnumNamesEnd);
}
Template.Write(WriteTemplates::FileEnd);
writer.close();
}
bool Parser::CmpAdvance(bx::StringView expect, Result& Res)
{
uint32_t size = expect.getLength();
if (size > GetRemaining())
{
std::cout << "Failed to match " << expect.getPtr() << ", " << size << "<" << GetRemaining() << std::endl;
return false;
}
int32_t i = 0;
for (; i < size; ++i)
{
if (expect.getPtr()[i] == 0)
{
i--;
break;
}
if (expect.getPtr()[i] != ReadPtr[i]) return false;
}
Res = Advance(i);
return true;
}
Parser::Result Parser::ExpectChar(bx::StringView expect)
{
Result Res = OK;
if (!CmpAdvance(expect, Res))
{
std::cout << "Expected " << expect.getPtr() << std::endl;
return Error;
}
return Res;
}
Parser::Result Parser::SkipWhitespace(bool* SkippedNewLine)
{
int32_t Remaining = GetRemaining();
for (int32_t i = 0; i < Remaining; ++i)
{
if (*ReadPtr == 0) return EndOfFile;
if (SkippedNewLine != nullptr && (*ReadPtr == '\r' || *ReadPtr == '\n')) *SkippedNewLine = true;
if (isgraph(*ReadPtr)) return OK;
ReadPtr++;
}
return EndOfFile;
}
Parser::Result Parser::SkipLine()
{
int32_t Remaining = GetRemaining();
for (int32_t i = 0; i < Remaining; ++i)
{
if (*ReadPtr == 0) return EndOfFile;
if (*ReadPtr == '\n' && i < Remaining - 1)
{
ReadPtr++;
if (*ReadPtr == 0) return EndOfFile;
return OK;
}
ReadPtr++;
}
return EndOfFile;
}
Parser::Result Parser::Advance(int32_t Amount)
{
int32_t Remaining = GetRemaining();
if (Amount > Remaining)
{
ReadPtr += Remaining;
return EndOfFile;
}
ReadPtr += Amount;
return OK;
}
#define CHECK(x) \
Res = x; \
if (Res != OK) return Res;
Parser::Result Parser::Parse()
{
ReadPtr = &Buffer[0];
Parser::Result Res = Parser::OK;
while (Res == Parser::OK)
{
Res = SkipWhitespace();
if (Res == Parser::OK)
{
// Start of Line
if (*ReadPtr == '#')
{
Res = SkipLine();
continue;
}
Res = HandleFileStart();
}
}
return Res;
}
Parser::Result Parser::HandleFileStart()
{
Result Res = OK;
if (CmpAdvance("type", Res) && Res == OK)
{
return HandleType();
}
if (Res != OK) return Res;
if (CmpAdvance("enum", Res) && Res == OK)
{
return HandleEnum();
}
if (Res != OK) return Res;
std::cout << "Only valid here: type or enum!";
return Error;
}
Parser::Result Parser::HandleType()
{
Result Res = OK;
if (!Stack.Push(TokenType::TypeKeyword)) return Error;
CHECK(SkipWhitespace());
if (Definitions.TypeCount >= DefinitionFile::MaxTypes)
{
std::cout << "Too many types!" << std::endl;
return Error;
}
Def::Type& t = Definitions.Types[Definitions.TypeCount];
CHECK(ReadName(t.Name));
CHECK(SkipWhitespace());
CHECK(ExpectChar("{"));
CHECK(SkipWhitespace());
while (!CmpAdvance("}", Res))
{
if (t.FieldCount >= Def::MaxFields)
{
std::cout << "Too many fields in type!" << std::endl;
return Error;
}
CHECK(ReadTypeToken());
CHECK(SkipWhitespace());
CHECK(ReadName(t.FieldNames[t.FieldCount]));
bool NewLine = false;
CHECK(SkipWhitespace(&NewLine));
if (!NewLine)
{
Result Res;
if (CmpAdvance("Arr", Res) && Res == Result::OK)
{
CHECK(ExpectChar("("));
CHECK(ReadUint(t.FieldArraySizes[t.FieldCount]));
CHECK(ExpectChar(")"));
}
}
CHECK(SkipWhitespace());
t.FieldCount++;
}
Definitions.TypeCount++;
return Res;
}
Parser::Result Parser::HandleEnum()
{
Result Res = OK;
if (!Stack.Push(TokenType::EnumKeyword)) return Error;
CHECK(SkipWhitespace());
if (Definitions.EnumCount >= DefinitionFile::MaxTypes)
{
std::cout << "Too many enums!" << std::endl;
return Error;
}
Def::Enum& e = Definitions.Enums[Definitions.EnumCount];
CHECK(ReadName(e.Name));
CHECK(SkipWhitespace());
CHECK(ExpectChar("{"));
CHECK(SkipWhitespace());
while (!CmpAdvance("}", Res))
{
if (e.EntryCount >= Def::MaxFields)
{
std::cout << "Too many fields in enum!" << std::endl;
return Error;
}
CHECK(ReadName(e.EntryNames[e.EntryCount]));
CHECK(ReadOptionalEnumValues(e, e.EntryCount));
CHECK(SkipWhitespace());
e.EntryCount++;
}
Definitions.EnumCount++;
return Res;
}
Parser::Result Parser::ReadName(char* Target)
{
int32_t Remaining = GetRemaining();
for (int32_t i = 0; i < Remaining; ++i)
{
if (!IsValidNameCharacter(*ReadPtr))
{
if (i == 0)
{
std::cout << "Empty names are not allowed!" << std::endl;
return Error;
}
return OK;
}
if (i < Def::MaxNameLength)
{
Target[i] = *ReadPtr;
}
else
{
std::cout << "Name too long!" << std::endl;
return Error;
}
ReadPtr++;
}
return EndOfFile;
}
Parser::Result Parser::ReadUint(uint32_t& Out)
{
char DigitBuf[32]{0};
int32_t Remaining = GetRemaining();
if (Remaining == 0) return Result::EndOfFile;
int32_t Written = 0;
for (int32_t i = 0; i < Remaining; ++i)
{
if (*ReadPtr < '0' || *ReadPtr > '9') break;
DigitBuf[i] = *ReadPtr;
++ReadPtr;
++Written;
}
if (Written == 0)
{
std::cout << "No digits for uint: " << PrintLine() << std::endl;
return Result::Error;
}
if (!bx::fromString(&Out, DigitBuf))
{
std::cout << "Failed to parse uint: " << PrintLine() << std::endl;
return Result::Error;
}
return Result::OK;
}
Parser::Result Parser::ReadTypeToken()
{
Result Res = OK;
for (int32_t i = 0; i < Generated::KnownType::EntryCount; ++i)
{
if (CmpAdvance(Generated::KnownType::EntryNames[i], Res) && Res == OK)
{
Def::Type& t = Definitions.Types[Definitions.TypeCount];
t.FieldTypes[t.FieldCount].Known = Generated::KnownType::Enum(i);
return OK;
}
}
std::cout << "Unknown type token:" << std::endl << PrintLine() << std::endl;
return Error;
}
Parser::Result Parser::ReadOptionalEnumValues(Def::Enum& Enum, int32_t EntryIdx)
{
Result Res = OK;
bool SkippedNewLine = false;
CHECK(SkipWhitespace(&SkippedNewLine));
if (SkippedNewLine) return Res;
for (int32_t extraIdx = 0; extraIdx < Def::MaxExtraEnumFields; ++extraIdx)
{
CHECK(ReadName(Enum.ExtraStringFieldNames[extraIdx]));
CHECK(SkipWhitespace());
CHECK(ExpectChar("("));
CHECK(SkipWhitespace());
CHECK(ExpectChar("\""));
int32_t Remaining = GetRemaining();
for (int32_t i = 0; i < Remaining; ++i)
{
if (CmpAdvance("\"", Res)) break;
Enum.ExtraStringFields[extraIdx][EntryIdx][i] = *ReadPtr;
ReadPtr++;
}
if (Res != OK) return Res;
CHECK(SkipWhitespace());
CHECK(ExpectChar(")"));
CHECK(SkipWhitespace(&SkippedNewLine));
Enum.ExtraStringFieldCount = bx::max(Enum.ExtraStringFieldCount, extraIdx + 1);
if (SkippedNewLine) return Res;
}
std::cout << "Too many extra enum fields!" << std::endl;
return Error;
}
int32_t Parser::GetRemaining()
{
return &Buffer[BufferSize] - ReadPtr;
}
bool Log::Assert(bool Condition, const char* Text)
{
if (!Condition)
{
std::cout << "Assertion failed: " << Text << std::endl;
bx::debugBreak();
}
return !Condition;
}
char WriteBuffer[1024 * 1024]{0};
void TemplateWriter::Write(const char* Template, ...)
{
va_list ArgList;
va_start(ArgList, Template);
int32_t Count = bx::vsnprintf(WriteBuffer, sizeof(WriteBuffer), Template, ArgList);
va_end(ArgList);
bx::write(&Writer, WriteBuffer, Count, &Error);
}
Parser TestParser;
int main(int argc, const char** argv)
{
if (argc != 3)
{
std::cout << "invalid number of arguments!" << std::endl;
return 1;
}
bx::FilePath defPath(argv[1]);
bx::FilePath outPath(argv[2]);
bx::FileReader reader;
bx::Error error;
if (!reader.open(defPath, &error))
{
std::cout << "failed to find def file at " << error.getMessage().getCPtr() << std::endl;
return 1;
}
TestParser.BufferSize = bx::read(&reader, TestParser.Buffer, Parser::MaxBufferSize, &error);
Parser::Result Res = TestParser.Parse();
if (Res == Parser::EndOfFile)
{
std::cout << "Success!" << std::endl;
TestParser.Definitions.GenerateCpp(outPath);
}
return 0;
}

View File

@@ -0,0 +1,160 @@
#pragma once
#include <bx/file.h>
#include <cstdint>
#include <iostream>
#include "Gen/Generated.h"
#include "bx/string.h"
struct Log
{
static bool Assert(bool Condition, const char* Text);
};
enum class TokenType : uint8_t
{
StartOfFile,
TypeKeyword,
EnumKeyword,
Name,
ScopeStart,
ScopeEnd,
};
struct ParseStack
{
static constexpr size_t MaxSize = 32;
TokenType Stack[MaxSize] = {TokenType::StartOfFile};
int32_t StackIdx = 0;
TokenType GetCurrent()
{
return Stack[StackIdx];
}
bool Push(TokenType T)
{
if (Log::Assert(StackIdx < MaxSize - 1, "Inbalanced Push/Pop!")) return false;
StackIdx++;
Stack[StackIdx] = T;
return true;
}
TokenType Pop()
{
if (Log::Assert(StackIdx == 0, "Inbalanced Push/Pop!")) return TokenType::StartOfFile;
TokenType T = Stack[StackIdx];
StackIdx--;
return T;
}
};
namespace Def
{
constexpr int32_t MaxNameLength = 64;
constexpr int32_t MaxFields = 64;
constexpr int32_t MaxExtraEnumFields = 1;
union TypeUnion
{
Generated::KnownType::Enum Known;
uint32_t TypeIdx;
};
struct Type
{
int32_t FieldCount = 0;
TypeUnion FieldTypes[MaxFields];
char FieldNames[MaxFields][MaxNameLength];
uint32_t FieldArraySizes[MaxFields]{0};
char Name[MaxNameLength]{0};
};
struct Enum
{
int32_t EntryCount = 0;
char EntryNames[MaxFields][MaxNameLength];
int32_t ExtraStringFieldCount = 0;
char ExtraStringFieldNames[MaxExtraEnumFields][MaxNameLength];
char ExtraStringFields[MaxFields][MaxExtraEnumFields][MaxNameLength];
char Name[MaxNameLength]{0};
};
} // namespace Def
struct DefinitionFile
{
static constexpr int32_t MaxTypes = 256;
void PrintTypeName(char* buf, int32_t bufSize, Def::TypeUnion type);
void GenerateCpp(const bx::FilePath& outDir);
Def::Type Types[MaxTypes];
int32_t TypeCount = 0;
Def::Enum Enums[MaxTypes];
int32_t EnumCount = 0;
};
class Parser
{
public:
enum Result : uint8_t
{
OK,
Error,
EndOfFile,
};
static constexpr int32_t MaxBufferSize = 1024 * 1024;
static constexpr int32_t MaxTokens = UINT16_MAX;
static bool IsValidNameCharacter(char c)
{
return isgraph(c) && c != '"' && c != '(' && c != ')';
}
int32_t BufferSize = 0;
char Buffer[MaxBufferSize]{0};
char* ReadPtr = &Buffer[0];
DefinitionFile Definitions;
ParseStack Stack;
public:
Result Parse();
private:
int32_t GetRemaining();
Result SkipWhitespace(bool* SkippedNewLine = nullptr);
Result SkipLine();
bool CmpAdvance(bx::StringView expect, Result& Res);
Result ExpectChar(bx::StringView expect);
Result Advance(int32_t Amount);
Result HandleFileStart();
Result HandleType();
Result HandleEnum();
Result ReadName(char* Target);
Result ReadUint(uint32_t& Out);
Result ReadTypeToken();
Result ReadOptionalEnumValues(Def::Enum& Enum, int32_t EntryIdx);
std::string PrintLine()
{
char line[64]{0};
for (int32_t i = 0; i < sizeof(line) - 1; ++i)
{
line[i] = ReadPtr[0];
if (Advance(1) != Result::OK) break;
if (*ReadPtr == '\n' || *ReadPtr == '\r') break;
}
return std::string{line};
}
};
class TemplateWriter
{
private:
bx::WriterI& Writer;
bx::Error& Error;
public:
TemplateWriter(bx::WriterI& W, bx::Error& E) : Writer(W), Error(E)
{
}
void Write(const char* Template, ...);
};