4coder-non-source/test_data/lots_of_files/simple_preprocessor.cpp

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 ? "\\" : "");
}
}