439 lines
10 KiB
C++
439 lines
10 KiB
C++
/* ========================================================================
|
|
$File: $
|
|
$Date: $
|
|
$Revision: $
|
|
$Creator: Casey Muratori $
|
|
$Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $
|
|
======================================================================== */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <memory.h>
|
|
|
|
struct meta_struct
|
|
{
|
|
char *Name;
|
|
meta_struct *Next;
|
|
};
|
|
static meta_struct *FirstMetaStruct;
|
|
|
|
static char *
|
|
ReadEntireFileIntoMemoryAndNullTerminate(char *FileName)
|
|
{
|
|
char *Result = 0;
|
|
|
|
FILE *File = fopen(FileName, "r");
|
|
if(File)
|
|
{
|
|
fseek(File, 0, SEEK_END);
|
|
size_t FileSize = ftell(File);
|
|
fseek(File, 0, SEEK_SET);
|
|
|
|
Result = (char *)malloc(FileSize + 1);
|
|
fread(Result, FileSize, 1, File);
|
|
Result[FileSize] = 0;
|
|
|
|
fclose(File);
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
enum token_type
|
|
{
|
|
Token_Unknown,
|
|
|
|
Token_OpenParen,
|
|
Token_CloseParen,
|
|
Token_Colon,
|
|
Token_Semicolon,
|
|
Token_Asterisk,
|
|
Token_OpenBracket,
|
|
Token_CloseBracket,
|
|
Token_OpenBrace,
|
|
Token_CloseBrace,
|
|
|
|
Token_String,
|
|
Token_Identifier,
|
|
|
|
Token_EndOfStream,
|
|
};
|
|
struct token
|
|
{
|
|
token_type Type;
|
|
|
|
size_t TextLength;
|
|
char *Text;
|
|
};
|
|
|
|
struct tokenizer
|
|
{
|
|
char *At;
|
|
};
|
|
|
|
inline bool
|
|
IsEndOfLine(char C)
|
|
{
|
|
bool Result = ((C == '\n') ||
|
|
(C == '\r'));
|
|
|
|
return(Result);
|
|
}
|
|
|
|
inline bool
|
|
IsWhitespace(char C)
|
|
{
|
|
bool Result = ((C == ' ') ||
|
|
(C == '\t') ||
|
|
(C == '\v') ||
|
|
(C == '\f') ||
|
|
IsEndOfLine(C));
|
|
|
|
return(Result);
|
|
}
|
|
|
|
inline bool
|
|
IsAlpha(char C)
|
|
{
|
|
bool Result = (((C >= 'a') && (C <= 'z')) ||
|
|
((C >= 'A') && (C <= 'Z')));
|
|
|
|
return(Result);
|
|
}
|
|
|
|
inline bool
|
|
IsNumber(char C)
|
|
{
|
|
bool Result = ((C >= '0') && (C <= '9'));
|
|
|
|
return(Result);
|
|
}
|
|
|
|
inline bool
|
|
TokenEquals(token Token, char *Match)
|
|
{
|
|
char *At = Match;
|
|
for(int Index = 0;
|
|
Index < Token.TextLength;
|
|
++Index, ++At)
|
|
{
|
|
if((*At == 0) ||
|
|
(Token.Text[Index] != *At))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
}
|
|
|
|
bool Result = (*At == 0);
|
|
return(Result);
|
|
}
|
|
|
|
static void
|
|
EatAllWhitespace(tokenizer *Tokenizer)
|
|
{
|
|
for(;;)
|
|
{
|
|
if(IsWhitespace(Tokenizer->At[0]))
|
|
{
|
|
++Tokenizer->At;
|
|
}
|
|
else if((Tokenizer->At[0] == '/') &&
|
|
(Tokenizer->At[1] == '/'))
|
|
{
|
|
Tokenizer->At += 2;
|
|
while(Tokenizer->At[0] && !IsEndOfLine(Tokenizer->At[0]))
|
|
{
|
|
++Tokenizer->At;
|
|
}
|
|
}
|
|
else if((Tokenizer->At[0] == '/') &&
|
|
(Tokenizer->At[1] == '*'))
|
|
{
|
|
Tokenizer->At += 2;
|
|
while(Tokenizer->At[0] &&
|
|
!((Tokenizer->At[0] == '*') &&
|
|
(Tokenizer->At[1] == '/')))
|
|
{
|
|
++Tokenizer->At;
|
|
}
|
|
|
|
if(Tokenizer->At[0] == '*')
|
|
{
|
|
Tokenizer->At += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static token
|
|
GetToken(tokenizer *Tokenizer)
|
|
{
|
|
EatAllWhitespace(Tokenizer);
|
|
|
|
token Token = {};
|
|
Token.TextLength = 1;
|
|
Token.Text = Tokenizer->At;
|
|
char C = Tokenizer->At[0];
|
|
++Tokenizer->At;
|
|
switch(C)
|
|
{
|
|
case '\0': {Token.Type = Token_EndOfStream;} break;
|
|
|
|
case '(': {Token.Type = Token_OpenParen;} break;
|
|
case ')': {Token.Type = Token_CloseParen;} break;
|
|
case ':': {Token.Type = Token_Colon;} break;
|
|
case ';': {Token.Type = Token_Semicolon;} break;
|
|
case '*': {Token.Type = Token_Asterisk;} break;
|
|
case '[': {Token.Type = Token_OpenBracket;} break;
|
|
case ']': {Token.Type = Token_CloseBracket;} break;
|
|
case '{': {Token.Type = Token_OpenBrace;} break;
|
|
case '}': {Token.Type = Token_CloseBrace;} break;
|
|
|
|
case '"':
|
|
{
|
|
Token.Type = Token_String;
|
|
|
|
Token.Text = Tokenizer->At;
|
|
|
|
while(Tokenizer->At[0] &&
|
|
Tokenizer->At[0] != '"')
|
|
{
|
|
if((Tokenizer->At[0] == '\\') &&
|
|
Tokenizer->At[1])
|
|
{
|
|
++Tokenizer->At;
|
|
}
|
|
++Tokenizer->At;
|
|
}
|
|
|
|
Token.TextLength = Tokenizer->At - Token.Text;
|
|
if(Tokenizer->At[0] == '"')
|
|
{
|
|
++Tokenizer->At;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
if(IsAlpha(C))
|
|
{
|
|
Token.Type = Token_Identifier;
|
|
|
|
while(IsAlpha(Tokenizer->At[0]) ||
|
|
IsNumber(Tokenizer->At[0]) ||
|
|
(Tokenizer->At[0] == '_'))
|
|
{
|
|
++Tokenizer->At;
|
|
}
|
|
|
|
Token.TextLength = Tokenizer->At - Token.Text;
|
|
}
|
|
#if 0
|
|
else if(IsNumeric(C))
|
|
{
|
|
ParseNumber();
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
Token.Type = Token_Unknown;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return(Token);
|
|
}
|
|
|
|
static bool
|
|
RequireToken(tokenizer *Tokenizer, token_type DesiredType)
|
|
{
|
|
token Token = GetToken(Tokenizer);
|
|
bool Result = (Token.Type == DesiredType);
|
|
return(Result);
|
|
}
|
|
|
|
static void
|
|
ParseIntrospectionParams(tokenizer *Tokenizer)
|
|
{
|
|
for(;;)
|
|
{
|
|
token Token = GetToken(Tokenizer);
|
|
if((Token.Type == Token_CloseParen) ||
|
|
(Token.Type == Token_EndOfStream))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
ParseMember(tokenizer *Tokenizer, token StructTypeToken, token MemberTypeToken)
|
|
{
|
|
#if 1
|
|
bool Parsing = true;
|
|
bool IsPointer = false;
|
|
while(Parsing)
|
|
{
|
|
token Token = GetToken(Tokenizer);
|
|
switch(Token.Type)
|
|
{
|
|
case Token_Asterisk:
|
|
{
|
|
IsPointer = true;
|
|
} break;
|
|
|
|
case Token_Identifier:
|
|
{
|
|
printf(" {%s, MetaType_%.*s, \"%.*s\", (u32)&((%.*s *)0)->%.*s},\n",
|
|
IsPointer ? "MetaMemberFlag_IsPointer" : "0",
|
|
MemberTypeToken.TextLength, MemberTypeToken.Text,
|
|
Token.TextLength, Token.Text,
|
|
StructTypeToken.TextLength, StructTypeToken.Text,
|
|
Token.TextLength, Token.Text);
|
|
} break;
|
|
|
|
case Token_Semicolon:
|
|
case Token_EndOfStream:
|
|
{
|
|
|
|
Parsing = false;
|
|
} break;
|
|
}
|
|
}
|
|
#else
|
|
token Token = GetToken(Tokenizer);
|
|
switch(Token.Type)
|
|
{
|
|
case Token_Asterisk:
|
|
{
|
|
ParseMember(Tokenizer, Token);
|
|
} break;
|
|
|
|
case Token_Identifier:
|
|
{
|
|
printf("DEBUG_VALUE(%.*s);\n", Token.TextLength, Token.Text);
|
|
} break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
ParseStruct(tokenizer *Tokenizer)
|
|
{
|
|
token NameToken = GetToken(Tokenizer);
|
|
if(RequireToken(Tokenizer, Token_OpenBrace))
|
|
{
|
|
printf("member_definition MembersOf_%.*s[] = \n", NameToken.TextLength, NameToken.Text);
|
|
printf("{\n");
|
|
for(;;)
|
|
{
|
|
token MemberToken = GetToken(Tokenizer);
|
|
if(MemberToken.Type == Token_CloseBrace)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ParseMember(Tokenizer, NameToken, MemberToken);
|
|
}
|
|
}
|
|
printf("};\n");
|
|
|
|
meta_struct *Meta = (meta_struct *)malloc(sizeof(meta_struct));
|
|
Meta->Name = (char *)malloc(NameToken.TextLength + 1);
|
|
memcpy(Meta->Name, NameToken.Text, NameToken.TextLength);
|
|
Meta->Name[NameToken.TextLength] = 0;
|
|
Meta->Next = FirstMetaStruct;
|
|
FirstMetaStruct = Meta;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ParseIntrospectable(tokenizer *Tokenizer)
|
|
{
|
|
if(RequireToken(Tokenizer, Token_OpenParen))
|
|
{
|
|
ParseIntrospectionParams(Tokenizer);
|
|
|
|
token TypeToken = GetToken(Tokenizer);
|
|
if(TokenEquals(TypeToken, "struct"))
|
|
{
|
|
ParseStruct(Tokenizer);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ERROR: Introspection is only supported for structs right now :(\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ERROR: Missing parentheses.\n");
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int ArgCount, char **Args)
|
|
{
|
|
char *FileNames[] =
|
|
{
|
|
"handmade_sim_region.h",
|
|
"handmade_platform.h",
|
|
"handmade_math.h",
|
|
"handmade_world.h",
|
|
};
|
|
for(int FileIndex = 0;
|
|
FileIndex < (sizeof(FileNames)/sizeof(FileNames[0]));
|
|
++FileIndex)
|
|
{
|
|
char *FileContents = ReadEntireFileIntoMemoryAndNullTerminate(FileNames[FileIndex]);
|
|
|
|
tokenizer Tokenizer = {};
|
|
Tokenizer.At = FileContents;
|
|
|
|
bool Parsing = true;
|
|
while(Parsing)
|
|
{
|
|
token Token = GetToken(&Tokenizer);
|
|
switch(Token.Type)
|
|
{
|
|
case Token_EndOfStream:
|
|
{
|
|
Parsing = false;
|
|
} break;
|
|
|
|
case Token_Unknown:
|
|
{
|
|
} break;
|
|
|
|
case Token_Identifier:
|
|
{
|
|
if(TokenEquals(Token, "introspect"))
|
|
{
|
|
ParseIntrospectable(&Tokenizer);
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
// printf("%d: %.*s\n", Token.Type, Token.TextLength, Token.Text);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("#define META_HANDLE_TYPE_DUMP(MemberPtr, NextIndentLevel) \\\n");
|
|
for(meta_struct *Meta = FirstMetaStruct;
|
|
Meta;
|
|
Meta = Meta->Next)
|
|
{
|
|
printf(" case MetaType_%s: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_%s), MembersOf_%s, MemberPtr, (NextIndentLevel));} break; %s\n",
|
|
Meta->Name, Meta->Name, Meta->Name,
|
|
Meta->Next ? "\\" : "");
|
|
}
|
|
}
|