2020-01-21 05:11:07 +00:00
|
|
|
//
|
|
|
|
// File: gs_meta.cpp
|
|
|
|
// Author: Peter Slattery
|
|
|
|
// Creation Date: 2020-01-19
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Usage
|
|
|
|
//
|
|
|
|
// GSMetaTag(<tag name>) to give commands to the meta layer
|
|
|
|
//
|
|
|
|
// Tag Values
|
|
|
|
//
|
|
|
|
// breakpoint
|
|
|
|
// will cause the meta layer to break in the debugger when it reaches
|
|
|
|
// that point in processing the file
|
|
|
|
// TODO: specify which stage you want it to break at
|
|
|
|
|
|
|
|
#ifndef GS_META_CPP
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#include <stdio.h>
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
#include <gs_language.h>
|
|
|
|
#include <gs_bucket.h>
|
|
|
|
#include "..\src\gs_platform.h"
|
|
|
|
#include <gs_memory_arena.h>
|
|
|
|
#include <gs_string.h>
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
#include "gs_meta_lexer.h"
|
|
|
|
#include "gs_meta_error.h"
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
#include "foldhaus_meta_type_table.h"
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
struct source_code_file
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
string Path;
|
|
|
|
s32 FileSize;
|
|
|
|
string Contents;
|
|
|
|
|
|
|
|
s32 FirstTokenIndex;
|
|
|
|
s32 LastTokenIndex;
|
2019-10-30 14:28:02 +00:00
|
|
|
};
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
struct token_iter
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
gs_bucket<token>* Tokens;
|
|
|
|
token* TokenAt;
|
|
|
|
s32 TokenAtIndex;
|
|
|
|
s32 FirstToken;
|
|
|
|
s32 LastToken;
|
|
|
|
|
|
|
|
#define TOKEN_ITER_SNAPSHOTS_MAX 64
|
|
|
|
u32 SnapshotsUsed;
|
|
|
|
u32 Snapshots[TOKEN_ITER_SNAPSHOTS_MAX];
|
|
|
|
|
|
|
|
errors* Errors;
|
2019-10-30 14:28:02 +00:00
|
|
|
};
|
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
struct gsm_profiler_scope
|
|
|
|
{
|
|
|
|
s64 StartTime;
|
|
|
|
s64 EndTime;
|
|
|
|
r32 Seconds;
|
|
|
|
r32 LongestSeconds;
|
|
|
|
u32 CallCount;
|
|
|
|
string Category;
|
|
|
|
string Identifier;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gsm_profiler_category
|
|
|
|
{
|
|
|
|
string Identifier;
|
|
|
|
r32 TotalTime;
|
|
|
|
u32 SubscopesCount;
|
|
|
|
gsm_profiler_scope* LongestSubscope;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gsm_profiler
|
|
|
|
{
|
|
|
|
gs_bucket<gsm_profiler_scope> Scopes;
|
|
|
|
gs_bucket<gsm_profiler_category> Categories;
|
|
|
|
};
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
struct gs_meta_preprocessor
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
errors Errors;
|
|
|
|
|
|
|
|
gs_bucket<source_code_file> SourceFiles;
|
|
|
|
gs_bucket<token> Tokens;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
gs_bucket<token> TagList;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
type_table TypeTable;
|
|
|
|
|
|
|
|
// Performance
|
|
|
|
s64 PreprocessorStartTime;
|
2020-01-21 05:11:07 +00:00
|
|
|
s64 TokenizeTime;
|
|
|
|
s64 PreprocTime;
|
|
|
|
s64 FixupTime;
|
2020-01-20 01:48:57 +00:00
|
|
|
s64 PreprocessorEndTime;
|
2020-01-21 05:11:07 +00:00
|
|
|
|
|
|
|
gsm_profiler Profiler;
|
2019-10-30 14:28:02 +00:00
|
|
|
};
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
// ------------------------
|
|
|
|
// Timing / Performance
|
|
|
|
// ------------------------
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal s64
|
|
|
|
GetWallClock ()
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
LARGE_INTEGER Time;
|
|
|
|
if (!QueryPerformanceCounter(&Time))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
s32 Error = GetLastError();
|
|
|
|
InvalidCodePath;
|
|
|
|
}
|
|
|
|
return (s64)Time.QuadPart;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal s64
|
|
|
|
GetPerformanceFrequency ()
|
|
|
|
{
|
|
|
|
LARGE_INTEGER Frequency;
|
|
|
|
if (!QueryPerformanceFrequency(&Frequency))
|
|
|
|
{
|
|
|
|
s32 Error = GetLastError();
|
|
|
|
InvalidCodePath;
|
|
|
|
}
|
|
|
|
return (s64)Frequency.QuadPart;
|
|
|
|
}
|
|
|
|
internal r32
|
2020-01-21 05:11:07 +00:00
|
|
|
GetSecondsElapsed(s64 CyclesCount)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
s64 Frequency = GetPerformanceFrequency();
|
2020-01-21 05:11:07 +00:00
|
|
|
r32 SecondsElapsed = (r32)(CyclesCount) / (r32)(Frequency);
|
2020-01-20 01:48:57 +00:00
|
|
|
return SecondsElapsed;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
internal r32
|
|
|
|
GetSecondsElapsed(s64 StartCycles, s64 EndCycles)
|
|
|
|
{
|
|
|
|
return GetSecondsElapsed(EndCycles - StartCycles);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal gsm_profiler_scope*
|
|
|
|
FindMatchingScope(gsm_profiler* Profiler, string Category, string Identifier)
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* Result = 0;
|
|
|
|
for (u32 i = 0; i < Profiler->Scopes.Used; i++)
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* Scope = Profiler->Scopes.GetElementAtIndex(i);
|
|
|
|
if (StringsEqual(Scope->Identifier, Identifier) &&
|
|
|
|
StringsEqual(Scope->Category, Category))
|
|
|
|
{
|
|
|
|
Result = Scope;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal gsm_profiler_scope*
|
|
|
|
BeginScope(gsm_profiler* Profiler, string Category, string Identifier)
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* Scope = FindMatchingScope(Profiler, Category, Identifier);
|
|
|
|
if (!Scope)
|
|
|
|
{
|
|
|
|
Scope = Profiler->Scopes.TakeElement();
|
|
|
|
*Scope = {};
|
|
|
|
}
|
|
|
|
Scope->Category = Category;
|
|
|
|
Scope->Identifier = Identifier;
|
|
|
|
Scope->StartTime = GetWallClock();
|
|
|
|
return Scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal gsm_profiler_scope*
|
|
|
|
BeginScope(gsm_profiler* Profiler, char* Category, char* Identifier)
|
|
|
|
{
|
|
|
|
return BeginScope(Profiler, MakeStringLiteral(Category), MakeStringLiteral(Identifier));
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
EndScope(gsm_profiler_scope* Scope)
|
|
|
|
{
|
|
|
|
Scope->EndTime = GetWallClock();
|
|
|
|
r32 Seconds = GetSecondsElapsed(Scope->StartTime, Scope->EndTime);
|
|
|
|
Scope->Seconds += Seconds;
|
|
|
|
if (Seconds > Scope->LongestSeconds)
|
|
|
|
{
|
|
|
|
Scope->LongestSeconds = Seconds;
|
|
|
|
}
|
|
|
|
Scope->CallCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal gsm_profiler_category*
|
|
|
|
GetCategory(string Identifier, gsm_profiler* Profiler)
|
|
|
|
{
|
|
|
|
gsm_profiler_category* Result = 0;
|
|
|
|
|
|
|
|
for (u32 i = 0; i < Profiler->Categories.Used; i++)
|
|
|
|
{
|
|
|
|
gsm_profiler_category* Category = Profiler->Categories.GetElementAtIndex(i);
|
|
|
|
if (StringsEqual(Identifier, Category->Identifier))
|
|
|
|
{
|
|
|
|
Result = Category;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Result == 0)
|
|
|
|
{
|
|
|
|
Result = Profiler->Categories.TakeElement();
|
|
|
|
*Result = {};
|
|
|
|
Result->Identifier = Identifier;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
FinishProfiler(gsm_profiler* Profiler)
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < Profiler->Scopes.Used; i++)
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* Scope = Profiler->Scopes.GetElementAtIndex(i);
|
|
|
|
gsm_profiler_category* Category = GetCategory(Scope->Category, Profiler);
|
|
|
|
Category->TotalTime += Scope->Seconds;
|
|
|
|
Category->SubscopesCount++;
|
|
|
|
|
|
|
|
if (!Category->LongestSubscope ||
|
|
|
|
Scope->Seconds > Category->LongestSubscope->Seconds)
|
|
|
|
{
|
|
|
|
Category->LongestSubscope = Scope;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
PrintAllCategories(gsm_profiler* Profiler)
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < Profiler->Categories.Used; i++)
|
|
|
|
{
|
|
|
|
gsm_profiler_category* Category = Profiler->Categories.GetElementAtIndex(i);
|
|
|
|
printf("Category: %.*s Total Time: %.*f Count: %d\n",
|
|
|
|
StringExpand(Category->Identifier),
|
|
|
|
6, Category->TotalTime,
|
|
|
|
Category->SubscopesCount);
|
|
|
|
printf(" Longest Scope: %.*s Total Time: %.*f Longest Time: %.*f Call Count: %d\n",
|
|
|
|
StringExpand(Category->LongestSubscope->Identifier),
|
|
|
|
6, Category->LongestSubscope->Seconds,
|
|
|
|
6, Category->LongestSubscope->LongestSeconds,
|
|
|
|
Category->LongestSubscope->CallCount);
|
|
|
|
|
|
|
|
if (StringsEqual(Category->Identifier, MakeStringLiteral("parse")))
|
|
|
|
{
|
|
|
|
for (u32 j = 0; j < Profiler->Scopes.Used; j++)
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* Scope = Profiler->Scopes.GetElementAtIndex(j);
|
|
|
|
if (StringsEqual(Scope->Category, Category->Identifier))
|
|
|
|
{
|
|
|
|
printf(" Time: %.*f Call Count: %d Scope: %.*s\n",
|
|
|
|
6, Scope->Seconds,
|
|
|
|
Scope->CallCount,
|
|
|
|
StringExpand(Scope->Identifier));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
// ------------------------
|
|
|
|
// Token Iterator
|
|
|
|
// ------------------------
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal token*
|
|
|
|
NextToken (token_iter* Iter)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
if (Iter->TokenAtIndex < Iter->LastToken)
|
|
|
|
{
|
|
|
|
Iter->TokenAtIndex++;
|
|
|
|
Iter->TokenAt = Iter->Tokens->GetElementAtIndex(Iter->TokenAtIndex);
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
return Iter->TokenAt;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
|
|
|
TokenAtEquals(token_iter* Iter, char* String)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
if (StringEqualsCharArray(Iter->TokenAt->Text, String))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
NextToken(Iter);
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
|
|
|
TokenAtEquals(token_iter* Iter, token_type Type)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
if (Iter->TokenAt->Type == Type)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
NextToken(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-20 01:48:57 +00:00
|
|
|
TokenAtEquals(token_iter* Iter, token_type Type, token* Token)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
2020-01-20 01:48:57 +00:00
|
|
|
if (Iter->TokenAt->Type == Type)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-20 01:48:57 +00:00
|
|
|
*Token = *Iter->TokenAt;
|
|
|
|
NextToken(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
2020-01-20 01:48:57 +00:00
|
|
|
PushSnapshot (token_iter* Iter)
|
|
|
|
{
|
|
|
|
Iter->Snapshots[Iter->SnapshotsUsed++] = Iter->TokenAtIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
PopSnapshot (token_iter* Iter)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
if (Iter->SnapshotsUsed > 0)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Iter->SnapshotsUsed -= 1;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
ApplySnapshot (token_iter* Iter)
|
|
|
|
{
|
|
|
|
u32 SnapshotIndex = Iter->SnapshotsUsed;
|
|
|
|
u32 SnapshotPoint = Iter->Snapshots[SnapshotIndex];
|
|
|
|
Iter->TokenAtIndex = SnapshotPoint;
|
|
|
|
Iter->TokenAt = Iter->Tokens->GetElementAtIndex(SnapshotPoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
ApplySnapshotIfNotParsedAndPop(b32 ParseSuccess, token_iter* Iter)
|
|
|
|
{
|
|
|
|
PopSnapshot(Iter);
|
|
|
|
if (!ParseSuccess)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal s32
|
|
|
|
GetFileSize (char* FileName)
|
|
|
|
{
|
|
|
|
s32 Result = 0;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
FILE* ReadFile = fopen(FileName, "r");
|
|
|
|
if (ReadFile)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
fseek(ReadFile, 0, SEEK_END);
|
|
|
|
size_t FileSize = ftell(ReadFile);
|
|
|
|
fseek(ReadFile, 0, SEEK_SET);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = (s32)FileSize;
|
|
|
|
fclose(ReadFile);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
// -------------------------
|
|
|
|
// Source File Handling
|
|
|
|
// -------------------------
|
|
|
|
|
|
|
|
internal s32
|
|
|
|
ReadEntireFileAndNullTerminate (source_code_file* File, errors* Errors)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
s32 LengthRead = 0;
|
|
|
|
|
|
|
|
FILE* ReadFile = fopen(File->Path.Memory, "r");
|
2019-10-30 14:28:02 +00:00
|
|
|
if (ReadFile)
|
|
|
|
{
|
|
|
|
fseek(ReadFile, 0, SEEK_END);
|
|
|
|
size_t FileSize = ftell(ReadFile);
|
|
|
|
fseek(ReadFile, 0, SEEK_SET);
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
Assert(File->Contents.Memory == 0);
|
|
|
|
File->Contents.Max = (s32)FileSize + 1;
|
|
|
|
File->Contents.Memory = (char*)malloc(File->Contents.Max);
|
|
|
|
|
|
|
|
size_t ReadSize = fread(File->Contents.Memory, 1, FileSize, ReadFile);
|
|
|
|
File->Contents.Memory[FileSize] = 0;
|
|
|
|
File->Contents.Length = (s32)ReadSize;
|
|
|
|
|
|
|
|
LengthRead = (s32)ReadSize + 1;
|
2019-10-30 14:28:02 +00:00
|
|
|
fclose(ReadFile);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
PushFError(Errors, "Could Not Read File: %S", File->Path);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
return LengthRead;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
|
|
|
FileAlreadyInSource(string Path, gs_bucket<source_code_file> SourceFiles)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
for (u32 i = 0; i < SourceFiles.Used; i++)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
source_code_file* File = SourceFiles.GetElementAtIndex(i);
|
|
|
|
if (StringsEqual(File->Path, Path))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
break;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
return Result;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal void
|
|
|
|
AddFileToSource(string RelativePath, gs_bucket<source_code_file>* SourceFiles, errors* Errors)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
source_code_file File = {0};
|
|
|
|
|
|
|
|
File.FirstTokenIndex = -1;
|
|
|
|
File.LastTokenIndex = -1;
|
|
|
|
|
|
|
|
u32 PathLength = RelativePath.Length + 1;
|
|
|
|
File.Path = MakeString((char*)malloc(sizeof(char) * PathLength), 0, PathLength);
|
|
|
|
CopyStringTo(RelativePath, &File.Path);
|
|
|
|
NullTerminate(&File.Path);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
File.FileSize = ReadEntireFileAndNullTerminate(&File, Errors);
|
|
|
|
|
|
|
|
if (File.FileSize > 0)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
SourceFiles->PushElementOnBucket(File);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
else
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
PushFError(Errors, "Error: Could not load file %S\n", RelativePath);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
2020-01-20 01:48:57 +00:00
|
|
|
TokenizeFile (source_code_file* File, gs_bucket<token>* Tokens)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
tokenizer Tokenizer = {};
|
|
|
|
Tokenizer.At = File->Contents.Memory;
|
|
|
|
Tokenizer.Memory = File->Contents.Memory;
|
|
|
|
Tokenizer.MemoryLength = File->Contents.Max;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
token* LastToken = 0;
|
|
|
|
while(AtValidPosition(Tokenizer))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
token NewToken = GetNextToken(&Tokenizer);
|
|
|
|
u32 TokenIndex = Tokens->PushElementOnBucket(NewToken);
|
|
|
|
if (File->FirstTokenIndex < 0)
|
|
|
|
{
|
|
|
|
File->FirstTokenIndex = (s32)TokenIndex;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
File->LastTokenIndex = Tokens->Used - 1;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
// ------------------------
|
|
|
|
// Parsing
|
|
|
|
// ------------------------
|
|
|
|
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseMetaTag(token_iter* Iter, gs_meta_preprocessor* Meta)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseMetaTag"));
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, "GSMetaTag") &&
|
|
|
|
TokenAtEquals(Iter, "("))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
token MetaIdentifier = {0};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &MetaIdentifier))
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
Meta->TagList.PushElementOnBucket(MetaIdentifier);
|
2020-01-20 01:48:57 +00:00
|
|
|
if (StringsEqual(MetaIdentifier.Text, MakeStringLiteral("breakpoint")))
|
|
|
|
{
|
|
|
|
// NOTE(Peter): This is not a temporary breakpoint. It is
|
|
|
|
// used to be able to break the meta program at specific points
|
|
|
|
// throughout execution
|
|
|
|
__debugbreak();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, ")") &&
|
|
|
|
TokenAtEquals(Iter, ";"))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
}
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(ProfilerScope);
|
2019-10-30 14:28:02 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-20 01:48:57 +00:00
|
|
|
ParseSignedness (token_iter* Iter)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
// NOTE(Peter): This doesn't really do much at the moment, but
|
|
|
|
// I want all signedness parsing to happen in one place in case
|
|
|
|
// we ever need to do anything with it.
|
|
|
|
|
2019-10-30 14:28:02 +00:00
|
|
|
b32 Result = false;
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "unsigned") ||
|
|
|
|
TokenAtEquals(Iter, "signed"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
2019-10-30 14:28:02 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ShortInt (token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
ParseSignedness(Iter);
|
|
|
|
if (TokenAtEquals(Iter, "short"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
if (TokenAtEquals(Iter, "int"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
if (Result)
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("short int"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
Int (token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
ParseSignedness(Iter);
|
|
|
|
if (TokenAtEquals(Iter, "int"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
if (Result)
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("int"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
return Result;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
LongInt (token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ParseSignedness(Iter);
|
|
|
|
if (TokenAtEquals(Iter, "long"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
if (TokenAtEquals(Iter, "int"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
if (Result)
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("long int"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
LongLongInt (token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
ParseSignedness(Iter);
|
|
|
|
if (TokenAtEquals(Iter, "long"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, "long"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
if (TokenAtEquals(Iter, "int"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
if (Result)
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("long long int"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
return Result;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseChar(token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
ParseSignedness(Iter);
|
|
|
|
if (TokenAtEquals(Iter, "char"))
|
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("char"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
else if (TokenAtEquals(Iter, "wchar_t"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("wchar_t"), TypeTable);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
return Result;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseBool(token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "bool"))
|
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("bool"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
return Result;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseFloat(token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "float"))
|
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("float"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
return Result;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseDouble(token_iter* Iter, type_table_handle* TypeHandleOut, type_table TypeTable)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "double"))
|
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("double"), TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
return Result;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
// :UndeclaredType
|
|
|
|
// NOTE(Peter): If TypeIndexOut is -1, you need to call NextToken after this
|
|
|
|
// function to advance past the type identifier.
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseType(token_iter* Iter, gs_meta_preprocessor* Meta, type_table_handle* TypeHandleOut)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseType"));
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = InvalidTypeTableHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
if (ParseChar(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
ParseBool(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
LongLongInt(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
LongInt(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
ShortInt(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
Int(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
ParseFloat(Iter, TypeHandleOut, Meta->TypeTable) ||
|
|
|
|
ParseDouble(Iter, TypeHandleOut, Meta->TypeTable))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
}
|
|
|
|
else if (StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("void")))
|
|
|
|
{
|
|
|
|
NextToken(Iter);
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = GetTypeHandle(MakeStringLiteral("void"), Meta->TypeTable);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
else
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfileInnerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseTypeInner"));
|
|
|
|
|
|
|
|
*TypeHandleOut = GetTypeHandle(Iter->TokenAt->Text, Meta->TypeTable);
|
|
|
|
if (TypeHandleIsValid(*TypeHandleOut))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
NextToken(Iter);
|
|
|
|
}
|
|
|
|
else if(Iter->TokenAt->Type == Token_Identifier)
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
// NOTE(Peter): In this case, we believe we are at a type identifier,
|
|
|
|
// however, it hasn't been declared yet. This is due to the fact that we
|
|
|
|
// tokenize files, then parse them, then import the files they include, and
|
|
|
|
// then begin tokenizing, parsing, etc for those files.
|
|
|
|
// In the case that we get an as-of-yet undeclared type, we leave it
|
|
|
|
// up to the calling site to determine what to do with that information
|
|
|
|
// :UndeclaredType
|
2020-01-21 05:11:07 +00:00
|
|
|
*TypeHandleOut = InvalidTypeTableHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
|
|
|
|
EndScope(ProfileInnerScope);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(ProfilerScope);
|
2019-10-30 14:28:02 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
|
|
|
ParsePointer (token_iter* Iter)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
if (TokenAtEquals(Iter, "*"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
|
|
|
ParseConstVolatile (token_iter* Iter)
|
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, "volatile") ||
|
|
|
|
TokenAtEquals(Iter, "const"))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseVariableDecl(token_iter* Iter, gs_bucket<variable_decl>* VariableList, gs_meta_preprocessor* Meta)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseVariableDecl"));
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (ParseConstVolatile(Iter))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
// NOTE(Peter): we don't do anything with this atm
|
|
|
|
// dont have a reason to just yet
|
|
|
|
// :UnusedConstVolatile
|
|
|
|
}
|
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
type_table_handle TypeHandle = InvalidTypeTableHandle;
|
|
|
|
if (ParseType(Iter, Meta, &TypeHandle))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
// :UndeclaredType
|
2020-01-21 05:11:07 +00:00
|
|
|
if (!TypeHandleIsValid(TypeHandle))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
TypeHandle = PushUndeclaredType(Iter->TokenAt->Text, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
NextToken(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
b32 IsPointer = ParsePointer(Iter);
|
|
|
|
|
|
|
|
if (ParseConstVolatile(Iter))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
// :UnusedConstVolatile
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
for(;;) {
|
2020-01-20 01:48:57 +00:00
|
|
|
token IdentifierToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &IdentifierToken))
|
|
|
|
{
|
|
|
|
// Array Notationg ie r32 x[2];
|
|
|
|
// NOTE(Peter): True initially because if there is no array notation, we
|
|
|
|
// are still ok to proceed
|
|
|
|
b32 ArrayParseSuccess = true;
|
|
|
|
u32 ArrayCount = 0;
|
|
|
|
if (TokenAtEquals(Iter, "["))
|
|
|
|
{
|
|
|
|
// NOTE(Peter): Once we get to this point, we have to complete the entire
|
|
|
|
// array notation before we have successfully parsed, hence setting
|
|
|
|
// ArrayParseSucces to false here.
|
|
|
|
ArrayParseSuccess = false;
|
|
|
|
token NumberToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Number, &NumberToken))
|
|
|
|
{
|
|
|
|
parse_result ParseArrayCount = ParseUnsignedInt(StringExpand(NumberToken.Text));
|
|
|
|
ArrayCount = ParseArrayCount.UnsignedIntValue;
|
2020-01-21 05:11:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO(Peter): Actually handle const expr for arrays
|
|
|
|
while (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("]")))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
NextToken(Iter);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "]"))
|
|
|
|
{
|
|
|
|
ArrayParseSuccess = true;
|
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ArrayParseSuccess)
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
|
|
|
|
variable_decl* Decl = VariableList->TakeElement();
|
|
|
|
*Decl = {};
|
|
|
|
Decl->Identifier = IdentifierToken.Text;
|
2020-01-21 05:11:07 +00:00
|
|
|
Decl->TypeHandle = TypeHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
Decl->Pointer = IsPointer;
|
|
|
|
Decl->ArrayCount = ArrayCount;
|
2020-01-21 05:11:07 +00:00
|
|
|
CopyMetaTagsAndClear(&Meta->TagList, &Decl->MetaTags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (StringsEqual(Iter->TokenAt->Text, MakeStringLiteral(",")))
|
|
|
|
{
|
|
|
|
// NOTE(Peter): There are two ways we enter this case
|
|
|
|
// 1. We are parsing a declaration list ie. r32 x, y, z;
|
|
|
|
// 2. We are parsing a function parameter list ie void proc(r32 x, u32 y)
|
|
|
|
// In this instance, we could still be parsing a declaration list
|
|
|
|
// ie. this is valid: void proc(r32 x, y, double z)
|
|
|
|
|
|
|
|
// This first snapshot is so we can rewind to before the comma in the event that
|
|
|
|
// we are parsing a function parameter list
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
NextToken(Iter);
|
|
|
|
|
|
|
|
// This second snapshot is so we can rewind to just _after_ the comma
|
|
|
|
// and continue parsing in the event that we are in a declaration list
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier) &&
|
|
|
|
(TokenAtEquals(Iter, ",") || TokenAtEquals(Iter, ";")))
|
|
|
|
{
|
|
|
|
// We are in a declaration list (case 1)
|
|
|
|
ApplySnapshotIfNotParsedAndPop(false, Iter);
|
|
|
|
PopSnapshot(Iter); // We don't need the first snapshot in this case
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// We are in a function parameter list (case 2)
|
|
|
|
ApplySnapshotIfNotParsedAndPop(false, Iter);
|
|
|
|
ApplySnapshotIfNotParsedAndPop(false, Iter);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(ProfilerScope);
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
|
|
|
StructOrUnion(token_iter* Iter, type_definition_type* Type)
|
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
if (TokenAtEquals(Iter, "struct"))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
*Type = TypeDef_Struct;
|
|
|
|
}
|
|
|
|
else if (TokenAtEquals(Iter, "union"))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
*Type = TypeDef_Union;
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
// NOTE(Peter): ContainingStruct will be 0 in all cases except when the struct or union
|
|
|
|
// is anonymous. In those cases, it MUST be the struct or union
|
|
|
|
// containing the anonymous struct/union
|
2020-01-20 01:48:57 +00:00
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseStruct(token_iter* Iter, type_table_handle* StructTypeHandleOut, gs_meta_preprocessor* Meta, type_definition* ContainingStruct = 0)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseStruct"));
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
2020-01-21 05:11:07 +00:00
|
|
|
*StructTypeHandleOut = InvalidTypeTableHandle;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
type_definition_type DeclType;
|
|
|
|
if (StructOrUnion(Iter, &DeclType))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
token IdentifierToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &IdentifierToken)) {}
|
|
|
|
|
|
|
|
// TODO(Peter): Handle name coming after the struct
|
|
|
|
if (TokenAtEquals(Iter, "{"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
type_definition StructDecl = {};
|
2020-01-21 05:11:07 +00:00
|
|
|
if (IdentifierToken.Text.Length > 0)
|
|
|
|
{
|
|
|
|
StructDecl.Identifier = IdentifierToken.Text;
|
|
|
|
StructDecl.Struct.IsAnonymous = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Assert(ContainingStruct);
|
|
|
|
Assert(ContainingStruct->Identifier.Length > 0);
|
|
|
|
// NOTE(Peter): I'm not sure this is neccessary, but I don't know what
|
|
|
|
// cases that its not true would be so I'm asserting just to find out
|
|
|
|
Assert(ContainingStruct->Type == TypeDef_Union ||
|
|
|
|
ContainingStruct->Type == TypeDef_Struct);
|
|
|
|
|
|
|
|
string AnonStructIdentifier = {};
|
|
|
|
AnonStructIdentifier.Max = 256;
|
|
|
|
AnonStructIdentifier.Memory = (char*)malloc(sizeof(char) * AnonStructIdentifier.Max);
|
|
|
|
|
|
|
|
PrintF(&AnonStructIdentifier, "%S_%d", ContainingStruct->Identifier, ContainingStruct->Struct.MemberDecls.Used);
|
|
|
|
|
|
|
|
StructDecl.Identifier = AnonStructIdentifier;
|
|
|
|
StructDecl.Struct.IsAnonymous = true;
|
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
StructDecl.Type = DeclType;
|
2020-01-21 05:11:07 +00:00
|
|
|
CopyMetaTagsAndClear(&Meta->TagList, &StructDecl.MetaTags);
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
while (!TokenAtEquals(Iter, "}"))
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
type_table_handle MemberStructTypeHandle = InvalidTypeTableHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
variable_decl MemberDecl = {};
|
2020-01-21 05:11:07 +00:00
|
|
|
if (ParseMetaTag(Iter, Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseVariableDecl(Iter, &StructDecl.Struct.MemberDecls, Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
if (!TokenAtEquals(Iter, ";"))
|
|
|
|
{
|
|
|
|
PushFError(Iter->Errors, "No semicolon after struct member variable declaration. %S", StructDecl.Identifier);
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseStruct(Iter, &MemberStructTypeHandle, Meta, &StructDecl))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
// NOTE(Peter): Pretty sure, since we just parsed the struct, that
|
2020-01-21 05:11:07 +00:00
|
|
|
// MemberStructTypeIndex should never be Invalid (unknown type).
|
2020-01-20 01:48:57 +00:00
|
|
|
// Putting this Assert here for now, but remove if there's a valid
|
|
|
|
// reason that you might not be able to find a struct just parsed at
|
|
|
|
// this point.
|
2020-01-21 05:11:07 +00:00
|
|
|
Assert(TypeHandleIsValid(MemberStructTypeHandle));
|
2020-01-20 01:48:57 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
MemberDecl.TypeHandle = MemberStructTypeHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
StructDecl.Struct.MemberDecls.PushElementOnBucket(MemberDecl);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// NOTE(Peter): One of the things that falls through here is
|
|
|
|
// cpp template stuff. Eventually, we should be able to use
|
|
|
|
// this meta layer to get rid of them all together, and then
|
|
|
|
// we can just disallow CPP templates
|
|
|
|
NextToken(Iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, ";"))
|
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
*StructTypeHandleOut = PushTypeDefOnTypeTable(StructDecl, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(ProfilerScope);
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ( type *? identifier, ... )
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseFunctionParameterList (token_iter* Iter, type_definition* FunctionPtrDecl, gs_meta_preprocessor* Meta)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, "("))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
|
|
|
|
while(!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral(")")))
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
if (ParseVariableDecl(Iter, &FunctionPtrDecl->FunctionPtr.Parameters, Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
if (TokenAtEquals(Iter, Token_Comma))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else if (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral(")")))
|
|
|
|
{
|
|
|
|
Result = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, ")"))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseFunctionDeclaration (token_iter* Iter, token* Identifier, gs_meta_preprocessor* Meta)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
type_table_handle ReturnTypeHandle = InvalidTypeTableHandle;
|
|
|
|
if (ParseType(Iter, Meta, &ReturnTypeHandle))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
if (!TypeHandleIsValid(ReturnTypeHandle))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
ReturnTypeHandle = PushUndeclaredType(Iter->TokenAt->Text, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
NextToken(Iter);
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 IsPointer = ParsePointer(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, Identifier))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
type_definition FunctionPtr = {};
|
|
|
|
FunctionPtr.Identifier = Identifier->Text;
|
|
|
|
FunctionPtr.Size = sizeof(void*);
|
2020-01-21 05:11:07 +00:00
|
|
|
CopyMetaTagsAndClear(&Meta->TagList, &FunctionPtr.MetaTags);
|
2020-01-20 01:48:57 +00:00
|
|
|
FunctionPtr.Type = TypeDef_FunctionPointer;
|
|
|
|
FunctionPtr.Pointer = true;
|
|
|
|
FunctionPtr.FunctionPtr = {};
|
2020-01-21 05:11:07 +00:00
|
|
|
FunctionPtr.FunctionPtr.ReturnTypeHandle = ReturnTypeHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
if (ParseFunctionParameterList(Iter, &FunctionPtr, Meta))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, ";"))
|
|
|
|
{
|
|
|
|
Result = true;
|
2020-01-21 05:11:07 +00:00
|
|
|
PushTypeDefOnTypeTable(FunctionPtr, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
if (!Result)
|
|
|
|
{
|
|
|
|
*Identifier = {0};
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseTypedef(token_iter* Iter, gs_meta_preprocessor* Meta)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseTypedef"));
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "typedef"))
|
|
|
|
{
|
|
|
|
token TypeToken = {0};
|
2020-01-21 05:11:07 +00:00
|
|
|
type_table_handle TypeHandle = InvalidTypeTableHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(Iter, "struct") &&
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseStruct(Iter, &TypeHandle, Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseFunctionDeclaration(Iter, &TypeToken, Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseType(Iter, Meta, &TypeHandle))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
if (!TypeHandleIsValid(TypeHandle))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
TypeHandle = PushUndeclaredType(Iter->TokenAt->Text, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
NextToken(Iter);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
b32 IsPointer = ParsePointer(Iter);
|
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
type_definition* BasisType = GetTypeDefinition(TypeHandle, Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
type_definition NewType = {};
|
|
|
|
NewType.Size = BasisType->Size;
|
2020-01-21 05:11:07 +00:00
|
|
|
CopyMetaTagsAndClear(&Meta->TagList, &NewType.MetaTags);
|
2020-01-20 01:48:57 +00:00
|
|
|
NewType.Type = BasisType->Type;
|
|
|
|
if (NewType.Type == TypeDef_Struct ||
|
|
|
|
NewType.Type == TypeDef_Union)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
NewType.Struct = BasisType->Struct;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
NewType.Pointer = BasisType->Pointer || IsPointer;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
token IdentifierToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &IdentifierToken))
|
|
|
|
{
|
|
|
|
NewType.Identifier = IdentifierToken.Text;
|
2020-01-21 05:11:07 +00:00
|
|
|
PushTypeDefOnTypeTable(NewType, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string* Error = TakeError(Iter->Errors);
|
|
|
|
PrintF(Error, "unhandled typedef ");
|
|
|
|
while (!TokenAtEquals(Iter, ";"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
PrintF(Error, "%S ", Iter->TokenAt->Text);
|
|
|
|
NextToken(Iter);
|
|
|
|
}
|
|
|
|
PrintF(Error, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(ProfilerScope);
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
2020-01-21 05:11:07 +00:00
|
|
|
ParseEnum (token_iter* Iter, gs_meta_preprocessor* Meta)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseEnum"));
|
2020-01-20 01:48:57 +00:00
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "enum"))
|
|
|
|
{
|
|
|
|
token IdentifierToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &IdentifierToken))
|
|
|
|
{
|
|
|
|
type_definition EnumDecl = {};
|
|
|
|
EnumDecl.Identifier = IdentifierToken.Text;
|
|
|
|
EnumDecl.Size = sizeof(u32);
|
2020-01-21 05:11:07 +00:00
|
|
|
CopyMetaTagsAndClear(&Meta->TagList, &EnumDecl.MetaTags);
|
2020-01-20 01:48:57 +00:00
|
|
|
EnumDecl.Type = TypeDef_Enum;
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "{"))
|
|
|
|
{
|
|
|
|
u32 EnumAcc = 0;
|
|
|
|
|
|
|
|
while (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}")))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
token EnumIdentifierToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &EnumIdentifierToken))
|
|
|
|
{
|
|
|
|
if (TokenAtEquals(Iter, "="))
|
|
|
|
{
|
|
|
|
// TODO(Peter): TempValue is just here until we handle all
|
|
|
|
// const expr that could define an enum value. Its there so
|
|
|
|
// that if the first token of an expression is a number,
|
|
|
|
// we can avoid using anything from the expression.
|
|
|
|
u32 TempValue = EnumAcc;
|
|
|
|
token NumberToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Number, &NumberToken))
|
|
|
|
{
|
|
|
|
parse_result ParsedExpr = ParseSignedInt(StringExpand(NumberToken.Text));
|
|
|
|
TempValue = ParsedExpr.SignedIntValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(Peter): Handle setting enums equal to other kinds
|
|
|
|
// of const exprs.
|
|
|
|
// We're skipping a whole bunch of stuff now
|
|
|
|
while (!(StringsEqual(Iter->TokenAt->Text, MakeStringLiteral(",")) ||
|
|
|
|
StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}"))))
|
|
|
|
{
|
|
|
|
TempValue = EnumAcc;
|
|
|
|
NextToken(Iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
EnumAcc = TempValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 EnumValue = EnumAcc++;
|
|
|
|
if (TokenAtEquals(Iter, ",") ||
|
|
|
|
StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}")))
|
|
|
|
{
|
|
|
|
EnumDecl.Enum.Identifiers.PushElementOnBucket(EnumIdentifierToken.Text);
|
|
|
|
EnumDecl.Enum.Values.PushElementOnBucket(EnumValue);
|
|
|
|
}
|
|
|
|
else if (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}")))
|
|
|
|
{
|
|
|
|
Result = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, "}") &&
|
|
|
|
TokenAtEquals(Iter, ";"))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
PushTypeDefOnTypeTable(EnumDecl, &Meta->TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
Result = true;
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(ProfilerScope);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal b32
|
|
|
|
ParseFunction (token_iter* Iter, gs_meta_preprocessor* Meta)
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* ProfilerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseFunction"));
|
|
|
|
b32 Result = false;
|
|
|
|
PushSnapshot(Iter);
|
|
|
|
|
|
|
|
type_table_handle ReturnTypeHandle = InvalidTypeTableHandle;
|
|
|
|
if (ParseType(Iter, Meta, &ReturnTypeHandle))
|
|
|
|
{
|
|
|
|
token IdentifierToken = {};
|
|
|
|
if (TokenAtEquals(Iter, Token_Identifier, &IdentifierToken) &&
|
|
|
|
TokenAtEquals(Iter, "("))
|
|
|
|
{
|
|
|
|
gsm_profiler_scope* ProfilerInnerScope = BeginScope(&Meta->Profiler,
|
|
|
|
MakeStringLiteral("parse"),
|
|
|
|
MakeStringLiteral("ParseFunctionInner"));
|
|
|
|
type_definition FunctionDecl = {};
|
|
|
|
FunctionDecl.Identifier = IdentifierToken.Text;
|
|
|
|
FunctionDecl.Function.ReturnTypeHandle = ReturnTypeHandle;
|
|
|
|
CopyMetaTagsAndClear(&Meta->TagList, &FunctionDecl.MetaTags);
|
|
|
|
FunctionDecl.Type = TypeDef_Function;
|
|
|
|
FunctionDecl.Function.Parameters = {};
|
|
|
|
|
|
|
|
while (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral(")")))
|
|
|
|
{
|
|
|
|
if (ParseVariableDecl(Iter, &FunctionDecl.Function.Parameters, Meta))
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!TokenAtEquals(Iter, ","))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TokenAtEquals(Iter, ")"))
|
|
|
|
{
|
|
|
|
Result = true;
|
|
|
|
PushTypeDefOnTypeTable(FunctionDecl, &Meta->TypeTable);
|
|
|
|
}
|
|
|
|
EndScope(ProfilerInnerScope);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplySnapshotIfNotParsedAndPop(Result, Iter);
|
|
|
|
EndScope(ProfilerScope);
|
2020-01-20 01:48:57 +00:00
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
PrintIndent (u32 Indent)
|
|
|
|
{
|
|
|
|
for (u32 i = 0; i < Indent; i++)
|
|
|
|
{
|
|
|
|
printf(" ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void PrintStructDecl (type_definition* StructDecl, type_table TypeTable, u32 Indent);
|
|
|
|
|
|
|
|
internal void
|
|
|
|
PrintVariableDecl (variable_decl Member, type_table TypeTable, u32 Indent = 0)
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
type_definition* MemberTypeDef = GetTypeDefinition(Member.TypeHandle, TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
if ((MemberTypeDef->Type == TypeDef_Struct || MemberTypeDef->Type == TypeDef_Union)
|
|
|
|
&& MemberTypeDef->Identifier.Length == 0)
|
|
|
|
{
|
|
|
|
PrintStructDecl(MemberTypeDef, TypeTable, Indent);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PrintIndent(Indent);
|
2020-01-21 05:11:07 +00:00
|
|
|
if (!TypeHandleIsValid(Member.TypeHandle))
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
printf("???? ");
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
printf("%.*s ", StringExpand(MemberTypeDef->Identifier));
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (Member.Pointer)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
printf("* ");
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
printf("%.*s", StringExpand(Member.Identifier));
|
|
|
|
|
|
|
|
if (Member.ArrayCount > 0)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
printf("[%d]", Member.ArrayCount);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
PrintStructDecl (type_definition* StructDecl, type_table TypeTable, u32 Indent = 0)
|
|
|
|
{
|
|
|
|
Assert(StructDecl->Type == TypeDef_Struct ||
|
|
|
|
StructDecl->Type == TypeDef_Union);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
PrintIndent(Indent);
|
|
|
|
if (StructDecl->Type == TypeDef_Struct)
|
|
|
|
{
|
|
|
|
printf("struct ");
|
|
|
|
}
|
|
|
|
else if (StructDecl->Type == TypeDef_Union)
|
|
|
|
{
|
|
|
|
printf("union ");
|
|
|
|
}
|
|
|
|
else { InvalidCodePath; }
|
|
|
|
|
|
|
|
if (StructDecl->Identifier.Length > 0)
|
|
|
|
{
|
|
|
|
printf("%.*s ", StringExpand(StructDecl->Identifier));
|
|
|
|
}
|
|
|
|
printf("{\n");
|
|
|
|
|
|
|
|
for (u32 MemberIndex = 0; MemberIndex < StructDecl->Struct.MemberDecls.Used; MemberIndex++)
|
|
|
|
{
|
|
|
|
variable_decl* Member = StructDecl->Struct.MemberDecls.GetElementAtIndex(MemberIndex);
|
|
|
|
PrintVariableDecl(*Member, TypeTable, Indent + 1);
|
|
|
|
printf(";\n");
|
|
|
|
}
|
|
|
|
PrintIndent(Indent);
|
|
|
|
printf("} ( size = %d ) ", StructDecl->Size);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
internal void
|
|
|
|
PrintFunctionPtrDecl (type_definition* FnPtrDecl, type_table TypeTable)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
type_definition* ReturnType = GetTypeDefinition(FnPtrDecl->FunctionPtr.ReturnTypeHandle, TypeTable);
|
2020-01-20 01:48:57 +00:00
|
|
|
printf("%.*s ", StringExpand(ReturnType->Identifier));
|
|
|
|
|
|
|
|
if (FnPtrDecl->Identifier.Length > 0)
|
|
|
|
{
|
|
|
|
printf("%.*s ", StringExpand(FnPtrDecl->Identifier));
|
|
|
|
}
|
|
|
|
printf("(");
|
|
|
|
|
|
|
|
for (u32 MemberIndex = 0; MemberIndex < FnPtrDecl->FunctionPtr.Parameters.Used; MemberIndex++)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
variable_decl* Param = FnPtrDecl->FunctionPtr.Parameters.GetElementAtIndex(MemberIndex);
|
|
|
|
PrintVariableDecl(*Param, TypeTable, 0);
|
|
|
|
printf(", ");
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
printf(");");
|
|
|
|
}
|
|
|
|
|
|
|
|
internal gs_meta_preprocessor
|
|
|
|
PreprocessProgram (char* SourceFile)
|
|
|
|
{
|
|
|
|
gs_meta_preprocessor Meta = {};
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* TotalScope = BeginScope(&Meta.Profiler, "total", "total");
|
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
Meta.PreprocessorStartTime = GetWallClock();
|
|
|
|
|
|
|
|
PopulateTableWithDefaultCPPTypes(&Meta.TypeTable);
|
|
|
|
|
|
|
|
string CurrentWorkingDirectory = MakeString((char*)malloc(1024), 0, 1024);
|
|
|
|
|
|
|
|
string RootFile = MakeString(SourceFile);
|
|
|
|
AddFileToSource(RootFile, &Meta.SourceFiles, &Meta.Errors);
|
|
|
|
|
|
|
|
s32 LastSlash = ReverseSearchForCharInSet(RootFile, "\\/");
|
|
|
|
if (LastSlash <= 0)
|
2019-10-30 14:28:02 +00:00
|
|
|
{
|
2020-01-20 01:48:57 +00:00
|
|
|
PushFError(&Meta.Errors, "%S: File path invalid.", RootFile);
|
|
|
|
return Meta;
|
|
|
|
}
|
|
|
|
|
|
|
|
string RootPath = Substring(RootFile, 0, LastSlash + 1);
|
|
|
|
CopyStringTo(RootPath, &CurrentWorkingDirectory);
|
|
|
|
|
|
|
|
for (u32 SourceFileIdx = 0; SourceFileIdx < Meta.SourceFiles.Used; SourceFileIdx++)
|
|
|
|
{
|
|
|
|
source_code_file* File = Meta.SourceFiles.GetElementAtIndex(SourceFileIdx);
|
2020-01-21 05:11:07 +00:00
|
|
|
|
|
|
|
gsm_profiler_scope* FileScope = BeginScope(&Meta.Profiler,
|
|
|
|
MakeStringLiteral("file"),
|
|
|
|
File->Path);
|
|
|
|
|
|
|
|
gsm_profiler_scope* TokenizeScope = BeginScope(&Meta.Profiler,
|
|
|
|
MakeStringLiteral("tokenize"),
|
|
|
|
File->Path);
|
2020-01-20 01:48:57 +00:00
|
|
|
TokenizeFile(File, &Meta.Tokens);
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(TokenizeScope);
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* PreprocScope = BeginScope(&Meta.Profiler,
|
|
|
|
MakeStringLiteral("preproc"),
|
|
|
|
File->Path);
|
2020-01-20 01:48:57 +00:00
|
|
|
token_iter Iter = {};
|
|
|
|
Iter.Tokens = &Meta.Tokens;
|
|
|
|
Iter.FirstToken = File->FirstTokenIndex;
|
|
|
|
Iter.LastToken = File->LastTokenIndex;
|
|
|
|
Iter.TokenAtIndex = Iter.FirstToken;
|
|
|
|
Iter.TokenAt = Meta.Tokens.GetElementAtIndex(Iter.TokenAtIndex);
|
|
|
|
Iter.Errors = &Meta.Errors;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
while (Iter.TokenAtIndex < Iter.LastToken)
|
|
|
|
{
|
|
|
|
b32 ParseSuccess = false;
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
type_table_handle TypeHandle = InvalidTypeTableHandle;
|
2020-01-20 01:48:57 +00:00
|
|
|
if (TokenAtEquals(&Iter, "#include"))
|
|
|
|
{
|
|
|
|
token* IncludeFile = Iter.TokenAt;
|
|
|
|
|
|
|
|
// NOTE(Peter): For now we aren't going in and preprocessing the header files
|
|
|
|
// we include from the system
|
|
|
|
// Token_Operator is used to check if the include is of the form '#include <header.h>'
|
|
|
|
// and skip it.
|
|
|
|
// TODO(Peter): This is only a rough approximation of ignoring system headers
|
|
|
|
// TODO(Peter): We should actually see what parsing system headers would entail
|
|
|
|
if (IncludeFile->Type != Token_Operator)
|
|
|
|
{
|
|
|
|
string TempFilePath = IncludeFile->Text;
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* IncludeScope = BeginScope(&Meta.Profiler,
|
|
|
|
MakeStringLiteral("include"),
|
|
|
|
TempFilePath);
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
// NOTE(Peter): if the path is NOT absolute ie "C:\etc
|
|
|
|
if (!(IsAlpha(TempFilePath.Memory[0]) &&
|
|
|
|
TempFilePath.Memory[1] == ':' &&
|
|
|
|
TempFilePath.Memory[2] == '\\'))
|
|
|
|
{
|
|
|
|
TempFilePath = CurrentWorkingDirectory;
|
|
|
|
ConcatString(IncludeFile->Text, &TempFilePath);
|
|
|
|
NullTerminate(&TempFilePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParseSuccess = true;
|
|
|
|
if (!FileAlreadyInSource(TempFilePath, Meta.SourceFiles))
|
|
|
|
{
|
|
|
|
AddFileToSource(TempFilePath, &Meta.SourceFiles, &Meta.Errors);
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(IncludeScope);
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if(ParseMetaTag(&Iter, &Meta))
|
|
|
|
{
|
|
|
|
ParseSuccess = true;
|
|
|
|
}
|
|
|
|
else if (ParseEnum(&Iter, &Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
ParseSuccess = true;
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseStruct(&Iter, &TypeHandle, &Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
ParseSuccess = true;
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseTypedef(&Iter, &Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
ParseSuccess = true;
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
else if (ParseFunction(&Iter, &Meta))
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
|
|
|
ParseSuccess = true;
|
|
|
|
}
|
2019-10-30 14:28:02 +00:00
|
|
|
|
2020-01-20 01:48:57 +00:00
|
|
|
if (!ParseSuccess)
|
|
|
|
{
|
|
|
|
NextToken(&Iter);
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(PreprocScope);
|
|
|
|
EndScope(FileScope);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
|
|
|
|
// Type Table Fixup
|
2020-01-21 05:11:07 +00:00
|
|
|
gsm_profiler_scope* FixupScope = BeginScope(&Meta.Profiler, "fixup", "fixup");
|
|
|
|
|
|
|
|
for (u32 b = 0; b < Meta.TypeTable.TypeBucketsCount; b++)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
type_table_hash_bucket Bucket = Meta.TypeTable.Types[b];
|
|
|
|
for (u32 i = 0; i < TYPE_TABLE_BUCKET_MAX; i++)
|
2020-01-20 01:48:57 +00:00
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
if (Bucket.Keys[i] > 0)
|
|
|
|
{
|
|
|
|
type_table_handle Handle = {};
|
|
|
|
Handle.BucketIndex = b;
|
|
|
|
Handle.IndexInBucket = i;
|
|
|
|
|
|
|
|
type_definition* TypeDef = GetTypeDefinitionUnsafe(Handle, Meta.TypeTable);
|
|
|
|
if (TypeDef)
|
|
|
|
{
|
|
|
|
if (TypeDef->Type == TypeDef_Struct)
|
|
|
|
{
|
|
|
|
FixUpStructSize(Handle, Meta.TypeTable, &Meta.Errors);
|
|
|
|
}
|
|
|
|
else if (TypeDef->Type == TypeDef_Union)
|
|
|
|
{
|
|
|
|
FixUpUnionSize(Handle, Meta.TypeTable, &Meta.Errors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-20 01:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
EndScope(FixupScope);
|
|
|
|
EndScope(TotalScope);
|
2020-01-20 01:48:57 +00:00
|
|
|
return Meta;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
FinishMetaprogram(gs_meta_preprocessor* Meta)
|
|
|
|
{
|
2020-01-21 05:11:07 +00:00
|
|
|
FinishProfiler(&Meta->Profiler);
|
2020-01-20 01:48:57 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
PrintAllErrors(Meta->Errors);
|
2020-01-20 01:48:57 +00:00
|
|
|
|
2020-01-21 05:11:07 +00:00
|
|
|
printf("\nMetaprogram Performance:\n");
|
|
|
|
PrintAllCategories(&Meta->Profiler);
|
2019-10-30 14:28:02 +00:00
|
|
|
}
|
2020-01-21 05:11:07 +00:00
|
|
|
|
|
|
|
#define GS_META_CPP
|
|
|
|
#endif // GS_META_CPP
|