Got the meta layer generating files, and incorporated it into the system. Replaced old node meta stuff with the new.

This commit is contained in:
Peter Slattery 2020-01-19 04:03:07 -08:00
parent 22c6fe15b6
commit 7cdb300388
12 changed files with 714 additions and 268 deletions

View File

@ -27,6 +27,74 @@
#include "gs_meta_error.h"
#include "gs_meta_lexer.h"
struct error_buffer
{
char* Backbuffer;
string* Contents;
};
#define ERROR_MAX_LENGTH 256
#define ERROR_BUFFER_SIZE 256
struct errors
{
error_buffer* Buffers;
u32 BuffersCount;
u32 Used;
};
internal void
PushFError (errors* Errors, char* Format, ...)
{
if (Errors->Used >= (Errors->BuffersCount * ERROR_BUFFER_SIZE))
{
Errors->BuffersCount += 1;
Errors->Buffers = (error_buffer*)realloc(Errors->Buffers, sizeof(error_buffer*) * Errors->BuffersCount);
error_buffer* NewBuffer = Errors->Buffers + (Errors->BuffersCount - 1);
NewBuffer->Backbuffer = (char*)malloc(sizeof(char) * ERROR_MAX_LENGTH * ERROR_BUFFER_SIZE);
NewBuffer->Contents = (string*)malloc(sizeof(string) * ERROR_BUFFER_SIZE);
for (u32 i = 0; i < ERROR_BUFFER_SIZE; i++)
{
NewBuffer->Contents[i].Memory = NewBuffer->Backbuffer + (i * ERROR_MAX_LENGTH);
NewBuffer->Contents[i].Max = ERROR_MAX_LENGTH;
NewBuffer->Contents[i].Length = 0;
}
}
u32 NewErrorIndex = Errors->Used++;
u32 BufferIndex = NewErrorIndex / ERROR_BUFFER_SIZE;
u32 IndexInBuffer = NewErrorIndex % ERROR_BUFFER_SIZE;
string* NewError = Errors->Buffers[BufferIndex].Contents + IndexInBuffer;
va_list Args;
va_start(Args, Format);
NewError->Length = PrintFArgsList(NewError->Memory, NewError->Max, Format, Args);
va_end(Args);
}
internal string*
TakeError (errors* Errors)
{
u32 NewErrorIndex = Errors->Used++;
u32 BufferIndex = NewErrorIndex / ERROR_BUFFER_SIZE;
u32 IndexInBuffer = NewErrorIndex % ERROR_BUFFER_SIZE;
string* NewError = Errors->Buffers[BufferIndex].Contents + IndexInBuffer;
return NewError;
}
internal void
PrintAllErrors (errors Errors)
{
for (u32 i = 0; i < Errors.Used; i++)
{
u32 BufferIndex = i / ERROR_BUFFER_SIZE;
u32 IndexInBuffer = i % ERROR_BUFFER_SIZE;
string Error = Errors.Buffers[BufferIndex].Contents[IndexInBuffer];
printf("%.*s\n", StringExpand(Error));
}
}
#include "foldhaus_meta_type_table.h"
error_list GlobalErrorList = {};
@ -57,6 +125,8 @@ struct token_iter
#define TOKEN_ITER_SNAPSHOTS_MAX 64
u32 SnapshotsUsed;
u32 Snapshots[TOKEN_ITER_SNAPSHOTS_MAX];
errors* Errors;
};
internal token*
@ -308,7 +378,7 @@ GetSecondsElapsed(s64 StartCycles, s64 EndCycles)
}
internal void
AddFileToSource(string RelativePath, gs_bucket<source_code_file>* SourceFiles)
AddFileToSource(string RelativePath, gs_bucket<source_code_file>* SourceFiles, errors* Errors)
{
source_code_file File = {0};
@ -328,7 +398,7 @@ AddFileToSource(string RelativePath, gs_bucket<source_code_file>* SourceFiles)
}
else
{
printf("Error: Could not load file %.*s\n", StringExpand(RelativePath));
PushFError(Errors, "Error: Could not load file %S\n", RelativePath);
}
}
@ -448,7 +518,6 @@ LongInt (token_iter* Iter, s32* TypeIndexOut, type_table TypeTable)
if (TokenAtEquals(Iter, "unsigned") ||
TokenAtEquals(Iter, "signed"))
{
Result = true;
}
if (TokenAtEquals(Iter, "long"))
@ -599,11 +668,7 @@ ParseType(token_iter* Iter, type_table* TypeTable, s32* TypeIndexOut)
NextToken(Iter);
Result = true;
}
else if (ShortInt(Iter, TypeIndexOut, *TypeTable))
{
Result = true;
}
else if (Int(Iter, TypeIndexOut, *TypeTable))
else if (LongLongInt(Iter, TypeIndexOut, *TypeTable))
{
Result = true;
}
@ -611,7 +676,11 @@ ParseType(token_iter* Iter, type_table* TypeTable, s32* TypeIndexOut)
{
Result = true;
}
else if (LongLongInt(Iter, TypeIndexOut, *TypeTable))
else if (ShortInt(Iter, TypeIndexOut, *TypeTable))
{
Result = true;
}
else if (Int(Iter, TypeIndexOut, *TypeTable))
{
Result = true;
}
@ -883,6 +952,7 @@ ParseFunctionDeclaration (token_iter* Iter, token* Identifier, gs_bucket<token*>
CopyMetaTagsAndClear(TagList, &FunctionPtr.MetaTags);
FunctionPtr.Type = TypeDef_FunctionPointer;
FunctionPtr.Pointer = true;
FunctionPtr.FunctionPtr = {};
FunctionPtr.FunctionPtr.ReturnTypeIndex = ReturnTypeIndex;
*Identifier = *Iter->TokenAt;
@ -959,13 +1029,14 @@ ParseTypedef(token_iter* Iter, gs_bucket<token*>* TagList, type_table* TypeTable
}
else
{
printf("unhandled typedef ");
string* Error = TakeError(Iter->Errors);
PrintF(Error, "unhandled typedef ");
while (!TokenAtEquals(Iter, ";"))
{
printf("%.*s ", StringExpand(Iter->TokenAt->Text));
PrintF(Error, "%S ", Iter->TokenAt->Text);
NextToken(Iter);
}
printf("\n");
PrintF(Error, "\n");
}
}
@ -974,6 +1045,91 @@ ParseTypedef(token_iter* Iter, gs_bucket<token*>* TagList, type_table* TypeTable
return Result;
}
internal b32
ParseEnum (token_iter* Iter, gs_bucket<token*>* TagList, type_table* TypeTable)
{
b32 Result = false;
PushSnapshot(Iter);
if (TokenAtEquals(Iter, "enum"))
{
if (Iter->TokenAt->Type == Token_Identifier)
{
type_definition EnumDecl = {};
EnumDecl.Identifier = Iter->TokenAt->Text;
EnumDecl.Size = sizeof(u32);
CopyMetaTagsAndClear(TagList, &EnumDecl.MetaTags);
EnumDecl.Type = TypeDef_Enum;
NextToken(Iter);
if (TokenAtEquals(Iter, "{"))
{
u32 EnumeratorAcc = 0;
while (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}")))
{
if (Iter->TokenAt->Type == Token_Identifier)
{
string EnumeratorIdentifier = Iter->TokenAt->Text;
NextToken(Iter);
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 = EnumeratorAcc;
if (Iter->TokenAt->Type == Token_Number)
{
parse_result ParsedExpr = ParseSignedInt(StringExpand(Iter->TokenAt->Text));
TempValue = ParsedExpr.SignedIntValue;
NextToken(Iter);
}
// 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 = EnumeratorAcc;
NextToken(Iter);
}
EnumeratorAcc = TempValue;
}
s32 EnumeratorValue = EnumeratorAcc++;
if (TokenAtEquals(Iter, ",") ||
StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}")))
{
EnumDecl.Enum.Identifiers.PushElementOnBucket(EnumeratorIdentifier);
EnumDecl.Enum.Values.PushElementOnBucket(EnumeratorValue);
}
else if (!StringsEqual(Iter->TokenAt->Text, MakeStringLiteral("}")))
{
Result = false;
break;
}
}
}
if (TokenAtEquals(Iter, "}") &&
TokenAtEquals(Iter, ";"))
{
PushTypeDefOnTypeTable(EnumDecl, TypeTable);
Result = true;
}
}
}
}
ApplySnapshotIfNotParsedAndPop(Result, Iter);
return Result;
}
internal void
PrintIndent (u32 Indent)
{
@ -1072,6 +1228,159 @@ PrintFunctionPtrDecl (type_definition* FnPtrDecl, type_table TypeTable)
printf(");");
}
struct typeinfo_generator
{
string_builder TypeList;
string_builder StructMembers;
string_builder TypeDefinitions;
u32 GeneratedInfoTypesCount;
u32 TypesMax;
b8* TypesGeneratedMask;
};
internal typeinfo_generator
InitTypeInfoGenerator(type_table TypeTable)
{
typeinfo_generator Result = {};
Result.TypesMax = TypeTable.Types.Used;
Result.TypesGeneratedMask = (b8*)malloc(sizeof(b8) * Result.TypesMax);
GSZeroMemory((u8*)Result.TypesGeneratedMask, Result.TypesMax);
WriteF(&Result.TypeList, "enum gsm_struct_type\n{\n");
WriteF(&Result.TypeDefinitions, "static gsm_struct_type_info StructTypes[] = {\n");
return Result;
}
internal void
FinishGeneratingTypes(typeinfo_generator* Generator)
{
WriteF(&Generator->TypeList, "gsm_StructTypeCount,\n};\n\n");
WriteF(&Generator->StructMembers, "\n");
WriteF(&Generator->TypeDefinitions, "};\n");
WriteF(&Generator->TypeDefinitions, "gsm_u32 StructTypesCount = %d;\n", Generator->GeneratedInfoTypesCount);
}
internal void
GenerateMetaTagInfo (gs_bucket<meta_tag> Tags, string_builder* Builder)
{
WriteF(Builder, "{");
for (u32 t = 0; t < Tags.Used; t++)
{
meta_tag* Tag = Tags.GetElementAtIndex(t);
WriteF(Builder, "{ \"%S\", %d }", Tag->Identifier, Tag->Identifier.Length);
if ((t + 1) < Tags.Used)
{
WriteF(Builder, ", ");
}
}
WriteF(Builder, "}, %d", Tags.Used);
}
internal void
GenerateStructMemberInfo (variable_decl* Member, string StructIdentifier, type_table TypeTable, typeinfo_generator* Gen)
{
WriteF(&Gen->StructMembers, "{ \"%S\", %d, ", Member->Identifier, Member->Identifier.Length);
WriteF(&Gen->StructMembers, "(u64)&((%S*)0)->%S ", StructIdentifier, Member->Identifier);
WriteF(&Gen->StructMembers, "},\n");
}
internal void
GenerateTypeInfo (type_definition* Type, u32 TypeIndex, type_table TypeTable, typeinfo_generator* Generator)
{
Generator->TypesGeneratedMask[TypeIndex] = true;
Generator->GeneratedInfoTypesCount++;
{
// NOTE(Peter): This block MUST come before generating
// type info for any member types. If it doesn't, it will screw
// up array ordering
// Lookup Enum
WriteF(&Generator->TypeList, "gsm_StructType_%S,\n", Type->Identifier);
// Type Info
WriteF(&Generator->TypeDefinitions, "{ gsm_StructType_%S, \"%S\", %d, %d, 0, 0, ",
Type->Identifier,
Type->Identifier, Type->Identifier.Length,
Type->Size
// TODO(Peter): include Meta Tags somehow
);
if ((Type->Type == TypeDef_Struct || Type->Type == TypeDef_Union) &&
Type->Struct.MemberDecls.Used > 0)
{
WriteF(&Generator->TypeDefinitions, "StructMembers_%S, %d },\n",
Type->Identifier,
Type->Struct.MemberDecls.Used);
}
else
{
WriteF(&Generator->TypeDefinitions, "0, 0 },\n");
}
}
if (Type->Type == TypeDef_Struct ||
Type->Type == TypeDef_Union)
{
for (u32 m = 0; m < Type->Struct.MemberDecls.Used; m++)
{
variable_decl* Member = Type->Struct.MemberDecls.GetElementAtIndex(m);
type_definition* MemberType = TypeTable.Types.GetElementAtIndex(Member->TypeIndex);
if (MemberType->Identifier.Length == 0) { continue; } // Don't gen info for anonymous struct and union members
if (Generator->TypesGeneratedMask[Member->TypeIndex]) { continue; }
GenerateTypeInfo(MemberType, Member->TypeIndex, TypeTable, Generator);
}
//
WriteF(&Generator->StructMembers, "static gsm_struct_member_type_info StructMembers_%S[] = {\n", Type->Identifier);
for (u32 m = 0; m < Type->Struct.MemberDecls.Used; m++)
{
variable_decl* Member = Type->Struct.MemberDecls.GetElementAtIndex(m);
type_definition* MemberType = TypeTable.Types.GetElementAtIndex(Member->TypeIndex);
if (MemberType->Identifier.Length > 0)
{
GenerateStructMemberInfo(Member, Type->Identifier, TypeTable, Generator);
}
else if (MemberType->Type == TypeDef_Struct ||
MemberType->Type == TypeDef_Union)
{
// Anonymous Members
for (u32 a = 0; a < MemberType->Struct.MemberDecls.Used; a++)
{
variable_decl* AnonMember = MemberType->Struct.MemberDecls.GetElementAtIndex(a);
GenerateStructMemberInfo(AnonMember, Type->Identifier, TypeTable, Generator);
}
}
}
WriteF(&Generator->StructMembers, "};\n", Type->Struct.MemberDecls.Used);
}
}
internal void
GenerateFilteredTypeInfo (string MetaTagFilter, type_table TypeTable, typeinfo_generator* Generator)
{
for (u32 i = 0; i < TypeTable.Types.Used; i++)
{
if (Generator->TypesGeneratedMask[i])
{
continue;
}
type_definition* Type = TypeTable.Types.GetElementAtIndex(i);
if (HasTag(MetaTagFilter, Type->MetaTags))
{
GenerateTypeInfo(Type, i, TypeTable, Generator);
}
}
}
// Step 1: Get All Tokens, for every file
// Step 2: Identify all preprocessor directives
// Step 3: Apply Preprocessor Directives && Generate Code
@ -1087,29 +1396,31 @@ int main(int ArgCount, char** ArgV)
return 0;
}
errors Errors = {0};
memory_arena SourceFileArena = {};
gs_bucket<source_code_file> SourceFiles;
gs_bucket<token> Tokens;
string_builder NodeTypeBlock = {};
Write(MakeStringLiteral("enum node_type\n{\n"), &NodeTypeBlock);
WriteF(&NodeTypeBlock, "enum node_type\n{\n");
string_builder NodeMembersBlock = {};
string_builder NodeSpecificationsBlock = {};
Write(MakeStringLiteral("node_specification NodeSpecifications[] = {\n"), &NodeSpecificationsBlock);
WriteF(&NodeSpecificationsBlock, "node_specification NodeSpecifications[] = {\n");
string_builder CallNodeProcBlock = {};
Write(MakeStringLiteral("internal void CallNodeProc(u32 SpecificationIndex, u8* Data, led* LEDs, s32 LEDsCount, r32 DeltaTime)\n{\n"), &CallNodeProcBlock);
Write(MakeStringLiteral("node_specification Spec = NodeSpecifications[SpecificationIndex];\n"), &CallNodeProcBlock);
Write(MakeStringLiteral("switch (Spec.Type)\n{\n"), &CallNodeProcBlock);
WriteF(&CallNodeProcBlock, "internal void CallNodeProc(u32 SpecificationIndex, u8* Data, led* LEDs, s32 LEDsCount, r32 DeltaTime)\n{\n");
WriteF(&CallNodeProcBlock, "node_specification Spec = NodeSpecifications[SpecificationIndex];\n");
WriteF(&CallNodeProcBlock, "switch (Spec.Type)\n{\n");
string CurrentWorkingDirectory = MakeString((char*)malloc(1024), 0, 1024);
if (ArgCount > 1)
{
string RootFile = MakeString(ArgV[1]);
AddFileToSource(RootFile, &SourceFiles);
AddFileToSource(RootFile, &SourceFiles, &Errors);
s32 LastSlash = ReverseSearchForCharInSet(RootFile, "\\/");
Assert(LastSlash > 0);
@ -1137,6 +1448,7 @@ int main(int ArgCount, char** ArgV)
Iter.LastToken = File->LastTokenIndex;
Iter.TokenAtIndex = Iter.FirstToken;
Iter.TokenAt = Tokens.GetElementAtIndex(Iter.TokenAtIndex);
Iter.Errors = &Errors;
while (Iter.TokenAtIndex < Iter.LastToken)
{
@ -1170,7 +1482,7 @@ int main(int ArgCount, char** ArgV)
ParseSuccess = true;
if (!FileAlreadyInSource(TempFilePath, SourceFiles))
{
AddFileToSource(TempFilePath, &SourceFiles);
AddFileToSource(TempFilePath, &SourceFiles, &Errors);
}
}
}
@ -1178,6 +1490,10 @@ int main(int ArgCount, char** ArgV)
{
ParseSuccess = true;
}
else if (ParseEnum(&Iter, &TagList, &TypeTable))
{
ParseSuccess = true;
}
else if (ParseStruct(&Iter, &TypeIndex, &TagList, &TypeTable))
{
ParseSuccess = true;
@ -1200,48 +1516,29 @@ int main(int ArgCount, char** ArgV)
type_definition* TypeDef = TypeTable.Types.GetElementAtIndex(i);
if (TypeDef->Type == TypeDef_Struct)
{
FixUpStructSize(i, TypeTable);
FixUpStructSize(i, TypeTable, &Errors);
}
else if (TypeDef->Type == TypeDef_Union)
{
FixUpUnionSize(i, TypeTable);
FixUpUnionSize(i, TypeTable, &Errors);
}
}
// Print All Structs
for (u32 i = 0; i < TypeTable.Types.Used; i++)
{
type_definition* TypeDef = TypeTable.Types.GetElementAtIndex(i);
#if 0
if ((TypeDef->Type == TypeDef_Struct || TypeDef->Type == TypeDef_Union) && TypeDef->Identifier.Length > 0)
{
PrintStructDecl(TypeDef, TypeTable);
printf("\n\n");
}
#endif
if (TypeDef->Type == TypeDef_FunctionPointer)
{
PrintFunctionPtrDecl(TypeDef, TypeTable);
printf("\n\n");
}
}
s64 Cycles_Preprocess = GetWallClock();
PrintAllErrors(Errors);
MakeStringBuffer(Buffer, 256);
// Close Types Block - overwrite the last comma and '\' newline character with newlines.
Write(MakeStringLiteral("NodeType_Count,\n};\n\n"), &NodeTypeBlock);
WriteF(&NodeTypeBlock, "NodeType_Count,\n};\n\n");
// Close Specifications Block
Write(MakeStringLiteral("};\n"), &NodeSpecificationsBlock);
PrintF(&Buffer, "s32 NodeSpecificationsCount = %d;\n\n", NodeProcCount);
Write(Buffer, &NodeSpecificationsBlock);
WriteF(&NodeSpecificationsBlock, "};\n");
WriteF(&NodeSpecificationsBlock, "s32 NodeSpecificationsCount = %d;\n\n", NodeProcCount);
// Close Call Node Proc Block
Write(MakeStringLiteral("}\n}\n"), &CallNodeProcBlock);
WriteF(&CallNodeProcBlock, "}\n}\n");
FILE* NodeGeneratedCPP = fopen("C:\\projects\\foldhaus\\src\\generated\\foldhaus_nodes_generated.cpp", "w");
if (NodeGeneratedCPP)
@ -1253,47 +1550,24 @@ int main(int ArgCount, char** ArgV)
fclose(NodeGeneratedCPP);
}
typeinfo_generator TypeGenerator = InitTypeInfoGenerator(TypeTable);
GenerateFilteredTypeInfo(MakeStringLiteral("node_struct"), TypeTable, &TypeGenerator);
FinishGeneratingTypes(&TypeGenerator);
FILE* TypeInfoH = fopen("C:\\projects\\foldhaus\\src\\generated\\gs_meta_generated_typeinfo.h", "w");
if (TypeInfoH)
{
WriteStringBuilderToFile(TypeGenerator.TypeList, TypeInfoH);
WriteStringBuilderToFile(TypeGenerator.StructMembers, TypeInfoH);
WriteStringBuilderToFile(TypeGenerator.TypeDefinitions, TypeInfoH);
fclose(NodeGeneratedCPP);
}
PrintErrorList(GlobalErrorList);
s64 TotalEnd = GetWallClock();
r32 TotalTime = GetSecondsElapsed(TotalStart, TotalEnd);
printf("Metaprogram Preproc Time: %.*f sec\n", 6, TotalTime);
#if 0
for (u32 i = 0; i < Structs.Used; i++)
{
seen_node_struct* Struct = Structs.GetElementAtIndex(i);
#ifdef PRINT_ALL_INFO
printf("\n");
for (u32 j = 0; j < Struct->MetaTags.Used; j++)
{
token* MetaTag = Struct->MetaTags.GetElementAtIndex(j);
printf("GSMetaTag(%.*s)\n", StringExpand(MetaTag->Text));
}
#endif
printf("struct %.*s\n", StringExpand(Struct->Name));
#ifdef PRINT_ALL_INFO
for (u32 j = 0; j < Struct->MemberDecls.Used; j++)
{
struct_member_decl* Member = Struct->MemberDecls.GetElementAtIndex(j);
for (u32 k = 0; k < Member->MetaTags.Used; k++)
{
token* MetaTag = Member->MetaTags.GetElementAtIndex(k);
printf(" GSMetaTag(%.*s)\n", StringExpand(MetaTag->Text));
}
printf(" %.*s %.*s\n", StringExpand(Member->Type), StringExpand(Member->Identifier));
}
#endif
}
#endif
__debugbreak();
//__debugbreak();
return 0;
}

View File

@ -0,0 +1,50 @@
//
// File: foldhaus_meta_include.h
// Author: Peter Slattery
// Creation Date: 2020-01-19
//
#ifndef FOLDHAUS_META_INCLUDE_H
//
// NOTE
// Include this file FIRST in any application utilizing the Foldhaus Meta system
// Include the generated files you wish to take advantage of at convenient locations
// in your application
//
typedef unsigned int gsm_u32;
typedef unsigned long long int gsm_u64;
#define GSMetaTag(ident, ...)
struct gsm_meta_tag
{
char* Tag;
gsm_u32 TagLength;
};
struct gsm_struct_member_type_info
{
char* Identifier;
gsm_u32 IdentifierLength;
gsm_u64 Offset;
};
struct gsm_struct_type_info
{
gsm_u32 Type;
char* Identifier;
gsm_u32 IdentifierLength;
gsm_u32 Size;
gsm_meta_tag* Tags;
gsm_u32 TagsCount;
gsm_struct_member_type_info* Members;
gsm_u32 MembersCount;
};
#define FOLDHAUS_META_INCLUDE_H
#endif // FOLDHAUS_META_INCLUDE_H

View File

@ -11,6 +11,7 @@ enum type_definition_type
// NOTE(Peter): tokens with this type require fixup later
TypeDef_Unknown,
TypeDef_Enum,
TypeDef_Struct,
TypeDef_Union,
TypeDef_BasicType,
@ -57,6 +58,15 @@ struct function_pointer_decl
gs_bucket<variable_decl> Parameters;
};
struct enum_decl
{
gs_bucket<string> Identifiers;
// TODO(Peter): I suppose there are ways to make an enum a 64 bit number
// for values. Probably need to handle that at some point
gs_bucket<u32> Values;
};
struct type_definition
{
string Identifier;
@ -67,6 +77,7 @@ struct type_definition
type_definition_type Type;
union
{
enum_decl Enum;
struct_decl Struct;
function_pointer_decl FunctionPtr;
};
@ -78,17 +89,19 @@ struct type_table
gs_bucket<type_definition> Types;
};
internal void
MetaTagBreakpoint(gs_bucket<meta_tag> Tags)
internal b32
HasTag(string Needle, gs_bucket<meta_tag> Tags)
{
b32 Result = false;
for (u32 i = 0; i < Tags.Used; i++)
{
meta_tag* Tag = Tags.GetElementAtIndex(i);
if (StringsEqual(Tag->Identifier, MakeStringLiteral("breakpoint")))
if (StringsEqual(Tag->Identifier, Needle))
{
__debugbreak();
Result = true;
}
}
return Result;
}
internal void
@ -136,7 +149,11 @@ PushTypeDefOnTypeTable(type_definition TypeDef, type_table* TypeTable)
s32 Index = -1;
s32 ExistingUndeclaredTypeIndex = GetIndexOfType(TypeDef.Identifier, *TypeTable);
if (ExistingUndeclaredTypeIndex < 0)
// NOTE(Peter): If the identifier length is zero, they will all match with the
// first anonymous struct/union member. So every anon struct/union gets its own
// typeef
if (ExistingUndeclaredTypeIndex < 0 || TypeDef.Identifier.Length == 0)
{
Index = TypeTable->Types.PushElementOnBucket(TypeDef);
}
@ -245,8 +262,8 @@ FindIndexOfMatchingType (type_definition Match, type_table TypeTable)
return Result;
}
internal void FixUpStructSize (s32 StructIndex, type_table TypeTable);
internal void FixUpUnionSize (s32 UnionIndex, type_table TypeTable);
internal void FixUpStructSize (s32 StructIndex, type_table TypeTable, errors* Errors);
internal void FixUpUnionSize (s32 UnionIndex, type_table TypeTable, errors* Errors);
internal void
FixupMemberType (variable_decl* Member, type_table TypeTable)
@ -279,12 +296,69 @@ CalculateStructMemberSize (variable_decl Member, type_definition MemberType)
}
internal void
FixUpStructSize (s32 StructIndex, type_table TypeTable)
FixupStructMember (variable_decl* Member, type_definition* MemberTypeDef, type_table TypeTable, errors* Errors)
{
// NOTE(Peter): There are a lot of cases where struct members which are pointers
// to other structs cause interesting behavior here.
// For example:
// struct foo { foo* Next; }
// could cause infinite loops if we try and fixup all structs with a size of 0
// which would happen in this case, because we wouldn't have parsed foo's size
// yet, but would begin fixing up foo because of the type of Next
// Another example:
// typedef struct bar bar;
// struct foo { bar* Bar; }
// struct bar { foo* Foo; }
// causes the exact same problem, but we cant detect it by just excluding
// fixing up StructIndex recursively.
//
// TL;DR
// The solution I've chosen to go with is just exclude all pointer members from
// causing recursive fixups. Those types should be fixed up at some point in the
// process, and we already know how big a pointer is in memory, no matter the type
if (!Member->Pointer)
{
if (MemberTypeDef->Size == 0)
{
if (MemberTypeDef->Type == TypeDef_Struct)
{
FixUpStructSize(Member->TypeIndex, TypeTable, Errors);
}
else if (MemberTypeDef->Type == TypeDef_Union)
{
FixUpUnionSize(Member->TypeIndex, TypeTable, Errors);
}
else
{
if (MemberTypeDef->Type == TypeDef_Unknown)
{
PushFError(Errors, "Union Error: TypeDef Unknown: %S\n", MemberTypeDef->Identifier);
}
else
{
// TODO(Peter): We don't parse all types yet, so for now, this is just an alert,
// not an assert;
#if 0
InvalidCodePath;
#else
PushFError(Errors, "Struct Error: TypeDef Size = 0. %S\n", MemberTypeDef->Identifier);
#endif
}
}
}
}
}
internal void
FixUpStructSize (s32 StructIndex, type_table TypeTable, errors* Errors)
{
type_definition* Struct = TypeTable.Types.GetElementAtIndex(StructIndex);
Assert(Struct->Type == TypeDef_Struct);
MetaTagBreakpoint(Struct->MetaTags);
if (HasTag(MakeStringLiteral("breakpoint"), Struct->MetaTags))
{
__debugbreak();
}
s32 SizeAcc = 0;
for (u32 j = 0; j < Struct->Struct.MemberDecls.Used; j++)
@ -292,52 +366,12 @@ FixUpStructSize (s32 StructIndex, type_table TypeTable)
variable_decl* Member = Struct->Struct.MemberDecls.GetElementAtIndex(j);
FixupMemberType(Member, TypeTable);
// TODO(Peter):
if (Member->TypeIndex >= 0)
{
type_definition* MemberTypeDef = TypeTable.Types.GetElementAtIndex(Member->TypeIndex);
// NOTE(Peter): There are a lot of cases where struct members which are pointers
// to other structs cause interesting behavior here.
// For example:
// struct foo { foo* Next; }
// could cause infinite loops if we try and fixup all structs with a size of 0
// which would happen in this case, because we wouldn't have parsed foo's size
// yet, but would begin fixing up foo because of the type of Next
// Another example:
// typedef struct bar bar;
// struct foo { bar* Bar; }
// struct bar { foo* Foo; }
// causes the exact same problem, but we cant detect it by just excluding
// fixing up StructIndex recursively.
//
// TL;DR
// The solution I've chosen to go with is just exclude all pointer members from
// causing recursive fixups. Those types should be fixed up at some point in the
// process, and we already know how big a pointer is in memory, no matter the type
if (!Member->Pointer)
{
if (MemberTypeDef->Size == 0)
{
if (MemberTypeDef->Type == TypeDef_Struct)
{
FixUpStructSize(Member->TypeIndex, TypeTable);
}
else if (MemberTypeDef->Type == TypeDef_Union)
{
FixUpUnionSize(Member->TypeIndex, TypeTable);
}
else
{
// TODO(Peter): We don't parse all types yet, so for now, this is just an alert,
// not an assert;
#if 0
InvalidCodePath;
#else
printf("Struct Error: TypeDef Size = 0. %.*s\n", StringExpand(MemberTypeDef->Identifier));
#endif
}
}
}
FixupStructMember(Member, MemberTypeDef, TypeTable, Errors);
u32 MemberSize = CalculateStructMemberSize(*Member, *MemberTypeDef);
SizeAcc += MemberSize;
@ -346,23 +380,25 @@ FixUpStructSize (s32 StructIndex, type_table TypeTable)
Struct->Size = SizeAcc;
// NOTE(Peter): Because its recursive (it makes sure all type sizes become known
// if it needs them) we should never get to the end of this function and not have
// the ability to tell how big something is.
// TODO(Peter): We don't parse all types yet however, so for now, this is just an alert,
// not an assert;
#if 0
Assert(Struct->Size != 0);
#else
if (Struct->Size == 0)
// NOTE(Peter): It is valid to have an empty struct, which would have a size of
// zero, hence the check here.
if (Struct->Size == 0 && Struct->Struct.MemberDecls.Used > 0)
{
printf("Struct Error: Struct Size = 0. %.*s\n", StringExpand(Struct->Identifier));
}
// NOTE(Peter): Because its recursive (it makes sure all type sizes become known
// if it needs them) we should never get to the end of this function and not have
// the ability to tell how big something is.
// TODO(Peter): We don't parse all types yet however, so for now, this is just an alert,
// not an assert;
#if 0
Assert(Struct->Size != 0);
#else
PushFError(Errors, "Struct Error: Struct Size = 0. %S\n", Struct->Identifier);
#endif
}
}
internal void
FixUpUnionSize (s32 UnionIndex, type_table TypeTable)
FixUpUnionSize (s32 UnionIndex, type_table TypeTable, errors* Errors)
{
type_definition* Union = TypeTable.Types.GetElementAtIndex(UnionIndex);
Assert(Union->Type == TypeDef_Union);
@ -376,28 +412,7 @@ FixUpUnionSize (s32 UnionIndex, type_table TypeTable)
if (Member->TypeIndex >= 0)
{
type_definition* MemberTypeDef = TypeTable.Types.GetElementAtIndex(Member->TypeIndex);
if (MemberTypeDef->Size == 0)
{
if (MemberTypeDef->Type == TypeDef_Struct)
{
FixUpStructSize(Member->TypeIndex, TypeTable);
}
else if(MemberTypeDef->Type == TypeDef_Union)
{
FixUpUnionSize(Member->TypeIndex, TypeTable);
}
else
{
// TODO(Peter): We don't parse all types yet, so for now, this is just an alert,
// not an assert;
#if 0
InvalidCodePath;
#else
printf("Union Error: TypeDef Size = 0. %.*s\n", StringExpand(MemberTypeDef->Identifier));
#endif
}
}
FixupStructMember(Member, MemberTypeDef, TypeTable, Errors);
s32 MemberSize = CalculateStructMemberSize(*Member, *MemberTypeDef);
BiggestMemberSize = GSMax(BiggestMemberSize, MemberSize);
}
@ -405,19 +420,21 @@ FixUpUnionSize (s32 UnionIndex, type_table TypeTable)
Union->Size = BiggestMemberSize;
// NOTE(Peter): Because its recursive (it makes sure all type sizes become known
// if it needs them) we should never get to the end of this function and not have
// the ability to tell how big something is
// TODO(Peter): We don't parse all types yet however, so for now, this is just an alert,
// not an assert;
#if 0
Assert(Union->Size != 0);
#else
if (Union->Size == 0)
// NOTE(Peter): It is valid to have an empty struct, which would have a size of
// zero, hence the check here.
if (Union->Size == 0 && Union->Struct.MemberDecls.Used > 0)
{
printf("Union Error: Struct Size = 0. %.*s\n", StringExpand(Union->Identifier));
}
// NOTE(Peter): Because its recursive (it makes sure all type sizes become known
// if it needs them) we should never get to the end of this function and not have
// the ability to tell how big something is
// TODO(Peter): We don't parse all types yet however, so for now, this is just an alert,
// not an assert;
#if 0
Assert(Union->Size != 0);
#else
PushFError(Errors, "Union Error: Struct Size = 0. %S\n", Union->Identifier);
#endif
}
}
type_definition CPPBasicTypes[] = {

View File

@ -104,6 +104,29 @@ GetNextToken (tokenizer* Tokenizer)
EatWhitespace(Tokenizer);
// Don't include comments in tokens
while (Tokenizer->At[0] && Tokenizer->At[0] == '/' && Tokenizer->At[1] && Tokenizer->At[1] == '/')
{
EatToNewLine(Tokenizer);
EatWhitespace(Tokenizer);
}
while(Tokenizer->At[0] && Tokenizer->At[0] == '/' && Tokenizer->At[1] && Tokenizer->At[1] == '*')
{
Tokenizer->At += 2;
while (*Tokenizer->At)
{
if (Tokenizer->At[0] && Tokenizer->At[0] == '*' && Tokenizer->At[1] && Tokenizer->At[1] == '/')
{
Tokenizer->At += 2;
break;
}
EatToNewLine(Tokenizer);
EatWhitespace(Tokenizer);
}
EatWhitespace(Tokenizer);
}
Result.Text = MakeString(Tokenizer->At, 1, 1);
// NOTE(Peter): Adding one because I want the tokenizer to work with clear to zero
@ -226,23 +249,6 @@ GetNextToken (tokenizer* Tokenizer)
Result.Text.Memory = Tokenizer->At;
Result.Text.Length = EatString(Tokenizer);
}
else if (C == '/' && Tokenizer->At[0] && Tokenizer->At[0] == '/')
{
Result.Type = Token_Comment;
Result.Text.Length += 1 + EatToNewLine(Tokenizer);
}
else if (C == '/' && Tokenizer->At[0] && Tokenizer->At[0] == '*')
{
s32 CommentLength = 1;
while (Tokenizer->At[0] && Tokenizer->At[0] != '*' &&
Tokenizer->At[1] && Tokenizer->At[1] != '/')
{
++Tokenizer->At;
CommentLength++;
}
Result.Text.Length += CommentLength;
}
// NOTE(Peter): This is after comment parsing so that the division operator
// falls through the comment case
else if (IsOperator(C)) { Result.Type = Token_Operator; }

View File

@ -5,24 +5,12 @@
//
#ifndef FOLDHAUS_APP_CPP
#define GSMetaTag(ident) // this is purely for the sake of the meta layer
#include "../meta/foldhaus_meta_include.h"
#include "foldhaus_platform.h"
#include "foldhaus_app.h"
GSMetaTag(node_struct);
struct test_data
{
r32* Ptr;
GSMetaTag(output_member);
u32 Output;
GSMetaTag(foo);
GSMetaTag(input_member);
u32 Input;
};
#include "./generated/gs_meta_generated_typeinfo.h"
internal v4
MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, rect WindowBounds)

View File

@ -20,6 +20,11 @@
#include "foldhaus_node.h"
// TODO(Peter): TEMPORARY
u32 NodeSpecificationsCount = 0;
node_specification* NodeSpecifications = 0;
#include "assembly_parser.cpp"
//#include "test_patterns.h"
@ -210,8 +215,8 @@ TestPatternThree(assembly* Assembly, r32 Time)
#include "foldhaus_text_entry.cpp"
#include "foldhaus_search_lister.cpp"
//#include "foldhaus_default_nodes.h"
#include "generated/foldhaus_nodes_generated.cpp"
#include "foldhaus_default_nodes.h"
#include "foldhaus_node.cpp"
FOLDHAUS_INPUT_COMMAND_PROC(EndCurrentOperationMode)

View File

@ -72,12 +72,20 @@ NODE_PROC(AddNodeProc, add_data)
//
/////////////////////////////////
NODE_STRUCT(sin_wave_data)
GSMetaTag(node_struct);
struct sin_wave_data
{
NODE_IN(r32, Period);
NODE_IN(r32, Min);
NODE_IN(r32, Max);
NODE_OUT(r32, Result);
GSMetaTag(node_input);
r32 Period;
GSMetaTag(node_input);
r32 Min;
GSMetaTag(node_input);
r32 Max;
GSMetaTag(node_input);
r32 Result;
r32 Accumulator;
};
@ -109,35 +117,41 @@ NODE_PROC(SinWave, sin_wave_data)
//
/////////////////////////////////
NODE_STRUCT(multiply_patterns_data)
GSMetaTag(node_struct);
struct multiply_patterns_data
{
NODE_COLOR_BUFFER_IN(A);
NODE_COLOR_BUFFER_IN(B);
NODE_COLOR_BUFFER_OUT(Result);
GSMetaTag(node_input);
color_buffer A;
GSMetaTag(node_input);
color_buffer B;
GSMetaTag(node_output);
color_buffer Result;
};
NODE_PROC(MultiplyPatterns, multiply_patterns_data)
{
led* LED = Data->ResultLEDs;
for (s32 l = 0; l < Data->ResultLEDCount; l++)
led* LED = Data->Result.LEDs;
for (s32 l = 0; l < Data->Result.LEDCount; l++)
{
Assert(LED->Index >= 0 && LED->Index < Data->ResultLEDCount);
Assert(LED->Index >= 0 && LED->Index < Data->Result.LEDCount);
s32 AR = Data->AColors[LED->Index].R;
s32 AG = Data->AColors[LED->Index].G;
s32 AB = Data->AColors[LED->Index].B;
s32 AR = Data->A.Colors[LED->Index].R;
s32 AG = Data->A.Colors[LED->Index].G;
s32 AB = Data->A.Colors[LED->Index].B;
s32 BR = Data->BColors[LED->Index].R;
s32 BG = Data->BColors[LED->Index].G;
s32 BB = Data->BColors[LED->Index].B;
s32 BR = Data->B.Colors[LED->Index].R;
s32 BG = Data->B.Colors[LED->Index].G;
s32 BB = Data->B.Colors[LED->Index].B;
s32 RCombined = (AR * BR) / 255;
s32 GCombined = (AG * BG) / 255;
s32 BCombined = (AB * BB) / 255;
Data->ResultColors[LED->Index].R = (u8)RCombined;
Data->ResultColors[LED->Index].G = (u8)GCombined;
Data->ResultColors[LED->Index].B = (u8)BCombined;
Data->Result.Colors[LED->Index].R = (u8)RCombined;
Data->Result.Colors[LED->Index].G = (u8)GCombined;
Data->Result.Colors[LED->Index].B = (u8)BCombined;
LED++;
}

View File

@ -12,19 +12,12 @@ typedef enum node_type node_type;
#define DEFAULT_NODE_DIMENSION v2{125, 150}
#define NODE_COLOR_BUFFER \
led* LEDs; \
pixel* Colors; \
s32 LEDCount;
#define NAMED_NODE_COLOR_BUFFER(name) \
led* name##LEDs; \
pixel* name##Colors; \
s32 name##LEDCount;
#define NODE_COLOR_BUFFER_INOUT NODE_COLOR_BUFFER
#define NODE_COLOR_BUFFER_IN(name) NAMED_NODE_COLOR_BUFFER(name)
#define NODE_COLOR_BUFFER_OUT(name) NAMED_NODE_COLOR_BUFFER(name)
struct color_buffer
{
led* LEDs;
pixel* Colors;
s32 LEDCount;
};
// TODO(Peter): Generate this
enum struct_member_type
@ -115,7 +108,8 @@ void proc_name(input_type* Data, r32 DeltaTime)
struct output_node_data
{
NODE_COLOR_BUFFER_IN(Result);
GSMetaTag(node_input);
color_buffer Result;
};
void OutputNode(output_node_data* Data, r32 DeltaTime)

View File

@ -24,7 +24,7 @@ PipeSearchStringToDestination (text_entry* Input)
case TextTranslateTo_R32:
{
parse_result FloatParseResult = ParseFloat(Input->Buffer.Memory, Input->Buffer.Length);
parse_result FloatParseResult = ParseFloat(StringExpand(Input->Buffer));
*Input->Destination.FloatDest = FloatParseResult.FloatValue;
}break;

View File

@ -0,0 +1,67 @@
enum gsm_struct_type
{
gsm_StructType_sin_wave_data,
gsm_StructType_r32,
gsm_StructType_multiply_patterns_data,
gsm_StructType_color_buffer,
gsm_StructType_led,
gsm_StructType_s32,
gsm_StructType_v4,
gsm_StructType_float,
gsm_StructType_pixel,
gsm_StructType_u8,
gsm_StructTypeCount,
};
static gsm_struct_member_type_info StructMembers_sin_wave_data[] = {
{ "Period", 6, (u64)&((sin_wave_data*)0)->Period },
{ "Min", 3, (u64)&((sin_wave_data*)0)->Min },
{ "Max", 3, (u64)&((sin_wave_data*)0)->Max },
{ "Result", 6, (u64)&((sin_wave_data*)0)->Result },
{ "Accumulator", 11, (u64)&((sin_wave_data*)0)->Accumulator },
};
static gsm_struct_member_type_info StructMembers_v4[] = {
{ "x", 1, (u64)&((v4*)0)->x },
{ "y", 1, (u64)&((v4*)0)->y },
{ "z", 1, (u64)&((v4*)0)->z },
{ "w", 1, (u64)&((v4*)0)->w },
{ "r", 1, (u64)&((v4*)0)->r },
{ "g", 1, (u64)&((v4*)0)->g },
{ "b", 1, (u64)&((v4*)0)->b },
{ "a", 1, (u64)&((v4*)0)->a },
{ "E", 1, (u64)&((v4*)0)->E },
};
static gsm_struct_member_type_info StructMembers_led[] = {
{ "Index", 5, (u64)&((led*)0)->Index },
{ "Position", 8, (u64)&((led*)0)->Position },
};
static gsm_struct_member_type_info StructMembers_pixel[] = {
{ "R", 1, (u64)&((pixel*)0)->R },
{ "G", 1, (u64)&((pixel*)0)->G },
{ "B", 1, (u64)&((pixel*)0)->B },
{ "Channels", 8, (u64)&((pixel*)0)->Channels },
};
static gsm_struct_member_type_info StructMembers_color_buffer[] = {
{ "LEDs", 4, (u64)&((color_buffer*)0)->LEDs },
{ "Colors", 6, (u64)&((color_buffer*)0)->Colors },
{ "LEDCount", 8, (u64)&((color_buffer*)0)->LEDCount },
};
static gsm_struct_member_type_info StructMembers_multiply_patterns_data[] = {
{ "A", 1, (u64)&((multiply_patterns_data*)0)->A },
{ "B", 1, (u64)&((multiply_patterns_data*)0)->B },
{ "Result", 6, (u64)&((multiply_patterns_data*)0)->Result },
};
static gsm_struct_type_info StructTypes[] = {
{ gsm_StructType_sin_wave_data, "sin_wave_data", 13, 20, 0, 0, StructMembers_sin_wave_data, 5 },
{ gsm_StructType_r32, "r32", 3, 4, 0, 0, 0, 0 },
{ gsm_StructType_multiply_patterns_data, "multiply_patterns_data", 22, 60, 0, 0, StructMembers_multiply_patterns_data, 3 },
{ gsm_StructType_color_buffer, "color_buffer", 12, 20, 0, 0, StructMembers_color_buffer, 3 },
{ gsm_StructType_led, "led", 3, 20, 0, 0, StructMembers_led, 2 },
{ gsm_StructType_s32, "s32", 3, 4, 0, 0, 0, 0 },
{ gsm_StructType_v4, "v4", 2, 16, 0, 0, StructMembers_v4, 3 },
{ gsm_StructType_float, "float", 5, 4, 0, 0, 0, 0 },
{ gsm_StructType_pixel, "pixel", 5, 3, 0, 0, StructMembers_pixel, 2 },
{ gsm_StructType_u8, "u8", 2, 1, 0, 0, 0, 0 },
};
gsm_u32 StructTypesCount = 10;

View File

@ -491,7 +491,7 @@ PANEL_RENDER_PROC(NodeGraph_Render)
string TitleString = MakeStringLiteral("Available Nodes");
DrawListElement(TitleString, &List, Mouse, RenderBuffer, State->Interface);
for (s32 i = 0; i < NodeSpecificationsCount; i++)
for (u32 i = 0; i < NodeSpecificationsCount; i++)
{
node_specification Spec = NodeSpecifications[i];
string NodeName = MakeString(Spec.Name, Spec.NameLength);

View File

@ -6,13 +6,18 @@
#ifndef TEST_PATTERNS_H
NODE_STRUCT(solid_color_data)
GSMetaTag(node_struct)
struct solid_color_data
{
NODE_IN(v4, Color);
NODE_COLOR_BUFFER_OUT(Result);
GSMetaTag(node_input);
v4 Color;
GSMetaTag(node_output);
color_buffer Result;
};
NODE_PROC(SolidColorProc, solid_color_data)
GSMetaTag(node_proc); // :TagParamsForNodeParamStructs
void SolidColorProc, solid_color_data* Data)
{
u8 R = (u8)GSClamp(0.f, (Data->Color.r * 255), 255.f);
u8 G = (u8)GSClamp(0.f, (Data->Color.g * 255), 255.f);
@ -30,15 +35,24 @@ NODE_PROC(SolidColorProc, solid_color_data)
}
}
NODE_STRUCT(vertical_color_fade_data)
GSMetaTag(node_struct)
struct vertical_color_fade_data
{
NODE_IN(v4, Color);
NODE_IN(r32, Min);
NODE_IN(r32, Max);
NODE_COLOR_BUFFER_OUT(Result);
GSMetaTag(node_input);
v4 Color;
GSMetaTag(node_input);
r32 Min;
GSMetaTag(node_input);
r32 Max;
GSMetaTag(node_output);
color_buffer Result;
};
NODE_PROC(VerticalColorFadeProc, vertical_color_fade_data)
GSMetaTag(node_proc); // :TagParamsForNodeParamStructs
void VerticalColorFadeProc, vertical_color_fade_data* Data)
{
r32 R = (Data->Color.r * 255);
r32 G = (Data->Color.g * 255);
@ -62,19 +76,36 @@ NODE_PROC(VerticalColorFadeProc, vertical_color_fade_data)
}
// Original -> DiscPatterns.pde : Revolving Discs
NODE_STRUCT(revolving_discs_data)
GSMetaTag(node_struct)
struct revolving_discs_data
{
NODE_IN(r32, Rotation);
NODE_IN(r32, ThetaZ);
NODE_IN(r32, ThetaY);
NODE_IN(r32, DiscWidth);
NODE_IN(r32, InnerRadius);
NODE_IN(r32, OuterRadius);
NODE_IN(v4, Color);
NODE_COLOR_BUFFER_OUT(Result);
GSMetaTag(node_input);
r32 Rotation;
GSMetaTag(node_input);
r32 ThetaZ;
GSMetaTag(node_input);
r32 ThetaY;
GSMetaTag(node_input);
r32 DiscWidth;
GSMetaTag(node_input);
r32 InnerRadius;
GSMetaTag(node_input);
r32 OuterRadius;
GSMetaTag(node_input);
v4 Color;
GSMetaTag(node_output);
color_buffer Result;
};
NODE_PROC(RevolvingDiscs, revolving_discs_data)
GSMetaTag(node_proc); // :TagParamsForNodeParamStructs
void RevolvingDiscs, revolving_discs_data* Data)
{
DEBUG_TRACK_FUNCTION;