diff --git a/brightness.txt b/brightness.txt new file mode 100644 index 0000000..e69de29 diff --git a/code/core/core.h b/code/core/core.h index 53fecc1..b063622 100644 --- a/code/core/core.h +++ b/code/core/core.h @@ -6,6 +6,7 @@ #include #include #include +#include //- sixten: Base types @@ -72,6 +73,10 @@ typedef intptr_t smm; #define InvalidCodepath Assert(!"Invalid codepath") #define InvalidDefaultCase default: { Assert(!"Invalid codepath"); } break +//- sixten: Compiler warning helpers + +#define UnusedVariable(Var) ((void)(Var)) + //- sixten: Array & pointer manipulation #define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) @@ -169,6 +174,9 @@ auto __Temp = (Element)->Next->Prev;\ (Element)->Next->Prev = (Element)->Prev->Next;\ (Element)->Prev->Next = __Temp; +#define SenDLLIsEmpty(Sentinel)\ +((Sentinel)->Next==(Sentinel)) + //- sixten: Stringify #define _Stringify(x) #x diff --git a/code/core/core_math.cpp b/code/core/core_math.cpp index ceb2175..f669061 100644 --- a/code/core/core_math.cpp +++ b/code/core/core_math.cpp @@ -519,6 +519,12 @@ inline range1_r32 Intersection(range1_r32 A, range1_r32 B) return(Result); } +inline range1_r32 Pad(range1_r32 Range, r32 Value) +{ + range1_r32 Result = Range1R32(Range.Min-Value, Range.Max+Value); + return(Result); +} + inline range1_s32 Range1S32(s32 A, s32 B) { range1_s32 Result = {Min(A, B), Max(A, B)}; @@ -549,6 +555,12 @@ inline range1_s32 Intersection(range1_s32 A, range1_s32 B) return(Result); } +inline range1_s32 Pad(range1_s32 Range, s32 Value) +{ + range1_s32 Result = Range1S32(Range.Min-Value, Range.Max+Value); + return(Result); +} + inline range1_s64 Range1S64(s64 A, s64 B) { range1_s64 Result = {Min(A, B), Max(A, B)}; @@ -579,6 +591,12 @@ inline range1_s64 Intersection(range1_s64 A, range1_s64 B) return(Result); } +inline range1_s64 Pad(range1_s64 Range, s64 Value) +{ + range1_s64 Result = Range1S64(Range.Min-Value, Range.Max+Value); + return(Result); +} + inline range2_r32 Range2R32(v2_r32 A, v2_r32 B) { range2_r32 Result = {Min(A, B), Max(A, B)}; diff --git a/code/core/core_math.h b/code/core/core_math.h index 741d7b7..e63b9ac 100644 --- a/code/core/core_math.h +++ b/code/core/core_math.h @@ -333,18 +333,21 @@ inline b32 InRange(range1_r32 Range, r32 Value); inline b32 Contains(range1_r32 Range, r32 Value); inline r32 DimOfRange(range1_r32 Range); inline range1_r32 Intersection(range1_r32 A, range1_r32 B); +inline range1_r32 Pad(range1_r32 Range, r32 Value); inline range1_s32 Range1S32(s32 A, s32 B); inline b32 InRange(range1_s32 Range, s32 Value); inline b32 Contains(range1_s32 Range, s32 Value); inline s32 DimOfRange(range1_s32 Range); inline range1_s32 Intersection(range1_s32 A, range1_s32 B); +inline range1_s32 Pad(range1_s32 Range, s32 Value); inline range1_s64 Range1S64(s64 A, s64 B); inline b32 InRange(range1_s64 Range, s64 Value); inline b32 Contains(range1_s64 Range, s64 Value); inline s64 DimOfRange(range1_s64 Range); inline range1_s64 Intersection(range1_s64 A, range1_s64 B); +inline range1_s64 Pad(range1_s64 Range, s64 Value); inline range2_r32 Range2R32(v2_r32 A, v2_r32 B); inline b32 InRange(range2_r32 Range, v2_r32 Value); diff --git a/code/core/core_memory.cpp b/code/core/core_memory.cpp index 6dc3bb5..3ed1392 100644 --- a/code/core/core_memory.cpp +++ b/code/core/core_memory.cpp @@ -115,6 +115,11 @@ static void ArenaPopTo(memory_arena *Arena, u64 Position) } } +static void ArenaPop(memory_arena *Arena, u64 Amount) +{ + ArenaPopTo(Arena, Max(Arena->Position-Amount, (s64)sizeof(memory_arena))); +} + static void ArenaClear(memory_arena *Arena) { ArenaPopTo(Arena, sizeof(*Arena)); diff --git a/code/core/core_memory.h b/code/core/core_memory.h index d9e9e60..dcf1b49 100644 --- a/code/core/core_memory.h +++ b/code/core/core_memory.h @@ -42,6 +42,7 @@ static void ArenaRelease(memory_arena *Arena); static void *ArenaPushNoClear(memory_arena *Arena, u64 Size); static void *ArenaPush(memory_arena *Arena, u64 Size); static void ArenaPopTo(memory_arena *Arena, u64 Position); +static void ArenaPop(memory_arena *Arena, u64 Amount); static void ArenaClear(memory_arena *Arena); static void ArenaSetAlign(memory_arena *Arena, u64 Align); #define PushArray(Arena, type, Count) (type *)ArenaPush((Arena), sizeof(type)*(Count)) diff --git a/code/core/core_string.cpp b/code/core/core_string.cpp index ee174fe..dcc734e 100644 --- a/code/core/core_string.cpp +++ b/code/core/core_string.cpp @@ -294,6 +294,15 @@ static string StringFromCodepoint(memory_arena *Arena, u32 Codepoint) return(Result); } +static r64 DoubleFromString(string String) +{ + temporary_memory Scratch = GetScratch(); + string NullTerminated = PushString(Scratch.Arena, String); + r64 Result = strtod((char *)NullTerminated.Data, 0); + ReleaseScratch(Scratch); + return(Result); +} + //- sixten: Replacing static string RemoveAll(memory_arena *Arena, string Text, char ToRemove) @@ -578,7 +587,7 @@ static u32 EncodeUTF8Codepoint(u8 *Dest, u32 Codepoint) return(Size); } -static string_decode DecodeUTF16Codepoint(u8 *Data, s64 Count) +static string_decode DecodeUTF16Codepoint(u16 *Data, s64 Count) { string_decode Result = {'#', 1}; if(Data[0] < 0xD800 || 0xDFFF < Data[0]) @@ -622,7 +631,7 @@ static s64 UTF8IndexFromOffset(string String, s64 Offset) u8 *StringBegin = String.Data; u8 *StringEnd = StringBegin+String.Count; u8 *Byte = StringBegin; - for(;Byte < StringEnd && Offset > 1; Offset -= 1) + for(;Byte < StringEnd && Offset > 0; Offset -= 1) { Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size; } @@ -682,6 +691,54 @@ static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint) return(Length); } +static string String8FromString16(memory_arena *Arena, string16 String) +{ + s64 AllocGuess = String.Count*3+1; + u8 *Memory = PushArray(Arena, u8, AllocGuess); + u16 *StringBegin = String.Data; + u16 *StringEnd = StringBegin+String.Count; + u16 *Source = StringBegin; + u8 *Dest = Memory; + for(;Source < StringEnd;) + { + string_decode Decode = DecodeUTF16Codepoint(Source, StringEnd-Source); + Dest += EncodeUTF8Codepoint(Dest, Decode.Codepoint); + Source += Decode.Size; + } + *Dest = 0; + s64 AllocUsed = Dest-Memory; + s64 AllocUnused = AllocGuess-AllocUsed; + Assert(AllocUnused >= 0); + ArenaPop(Arena, AllocUnused); + + string Result = MakeString(Memory, AllocUsed); + return(Result); +} + +static string16 String16FromString8(memory_arena *Arena, string String) +{ + s64 AllocGuess = String.Count*2+1; + u16 *Memory = PushArray(Arena, u16, AllocGuess); + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+String.Count; + u8 *Source = StringBegin; + u16 *Dest = Memory; + for(;Source < StringEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Source, StringEnd-Source); + Dest += EncodeUTF16Codepoint(Dest, Decode.Codepoint); + Source += Decode.Size; + } + *Dest = 0; + s64 AllocUsed = Dest-Memory; + s64 AllocUnused = AllocGuess-AllocUsed; + Assert(AllocUnused >= 0); + ArenaPop(Arena, AllocUnused); + + string16 Result = {AllocUsed, Memory}; + return(Result); +} + //~ sixten: Text point static text_point TextPointFromOffset(string String, s64 Offset) diff --git a/code/core/core_string.h b/code/core/core_string.h index 9890e57..d09ae17 100644 --- a/code/core/core_string.h +++ b/code/core/core_string.h @@ -12,6 +12,12 @@ struct string u8 *Data; }; +struct string16 +{ + s64 Count; + u16 *Data; +}; + typedef string buffer; struct string_node @@ -94,6 +100,7 @@ static string PushCString(memory_arena *Arena, char *String); static s64 ConvertStringToS64(string String); static string ConvertS64ToString(memory_arena *Arena, s64 Value); static string StringFromCodepoint(memory_arena *Arena, u32 Codepoint); +static r64 DoubleFromString(string String); //- sixten: Replacing @@ -129,11 +136,14 @@ struct string_decode static string_decode DecodeUTF8Codepoint(u8 *Data, s64 Count); static u32 EncodeUTF8Codepoint(u8 *Dest, u32 Codepoint); -static string_decode DecodeUTF16Codepoint(u8 *Data, s64 Count); +static string_decode DecodeUTF16Codepoint(u16 *Data, s64 Count); static u32 EncodeUTF16Codepoint(u16 *Dest, u32 Codepoint); static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint); +static string String8FromString16(memory_arena *Arena, string16 String); +static string16 String16FromString8(memory_arena *Arena, string String); + //~ sixten: Text point struct text_point diff --git a/code/generated/vn_platform.meta.h b/code/generated/vn_platform.meta.h index 06999ef..6ef505c 100644 --- a/code/generated/vn_platform.meta.h +++ b/code/generated/vn_platform.meta.h @@ -10,6 +10,9 @@ #define PLATFORM_SET_CURSOR(name) void name(platform_cursor Cursor) #define PLATFORM_TOGGLE_FULLSCREEN(name) void name(void) #define PLATFORM_SHOW_MESSAGE(name) void name(string Message, platform_message_type Type) +#define PLATFORM_BEGIN_FILE_ITER(name) platform_file_iter * name(memory_arena *Arena, string Path) +#define PLATFORM_ADVANCE_FILE_ITER(name) b32 name(memory_arena *Arena, platform_file_iter *Iter, platform_file_info *OutInfo) +#define PLATFORM_END_FILE_ITER(name) void name(platform_file_iter *Iter) typedef PLATFORM_RESERVE(platform_reserve); typedef PLATFORM_RELEASE(platform_release); @@ -23,6 +26,9 @@ typedef PLATFORM_GET_FILE_SIZE(platform_get_file_size); typedef PLATFORM_SET_CURSOR(platform_set_cursor); typedef PLATFORM_TOGGLE_FULLSCREEN(platform_toggle_fullscreen); typedef PLATFORM_SHOW_MESSAGE(platform_show_message); +typedef PLATFORM_BEGIN_FILE_ITER(platform_begin_file_iter); +typedef PLATFORM_ADVANCE_FILE_ITER(platform_advance_file_iter); +typedef PLATFORM_END_FILE_ITER(platform_end_file_iter); struct platform_api { @@ -38,6 +44,9 @@ platform_get_file_size *GetFileSize; platform_set_cursor *SetCursor; platform_toggle_fullscreen *ToggleFullscreen; platform_show_message *ShowMessage; +platform_begin_file_iter *BeginFileIter; +platform_advance_file_iter *AdvanceFileIter; +platform_end_file_iter *EndFileIter; }; #define RegisterPlatformFunctions(PlatformName)\ @@ -53,4 +62,7 @@ Platform.GetFileSize = PlatformName##_GetFileSize;\ Platform.SetCursor = PlatformName##_SetCursor;\ Platform.ToggleFullscreen = PlatformName##_ToggleFullscreen;\ Platform.ShowMessage = PlatformName##_ShowMessage;\ +Platform.BeginFileIter = PlatformName##_BeginFileIter;\ +Platform.AdvanceFileIter = PlatformName##_AdvanceFileIter;\ +Platform.EndFileIter = PlatformName##_EndFileIter;\ diff --git a/code/generated/vn_scene.meta.h b/code/generated/vn_scene.meta.h index 485ebef..b51e3b0 100644 --- a/code/generated/vn_scene.meta.h +++ b/code/generated/vn_scene.meta.h @@ -1,25 +1,37 @@ -enum scene_ast_node_type +enum scene_operator { -S_AstNode_Invalid, -S_AstNode_BlockStatement, -S_AstNode_SceneDecl, -S_AstNode_Count, +S_Operator_Invalid, +S_Operator_Not, +S_Operator_Equal, +S_Operator_Equals, +S_Operator_NotEquals, +S_Operator_GreaterThanOrEquals, +S_Operator_LessThanOrEquals, +S_Operator_Greater, +S_Operator_Less, +S_Operator_Add, +S_Operator_Minus, +S_Operator_Multiply, +S_Operator_Divide, }; -struct scene_ast_node; - -struct scene_ast_node_invalid {}; -struct scene_ast_node_block_statement {scene_ast_node *First; scene_ast_node *Last;}; -struct scene_ast_node_scene_declaration {}; - -struct scene_ast_node +inline scene_operator S_OperatorFromString(string String) { -scene_ast_node_type Type; -union -{ -scene_ast_node_invalid InvalidData; -scene_ast_node_block_statement BlockStatementData; -scene_ast_node_scene_declaration SceneDeclData; -}; -}; +scene_operator Result = S_Operator_Invalid; +if(0) {} +else if(AreEqual(String, StrLit("###"))) { Result = S_Operator_Invalid; } +else if(AreEqual(String, StrLit("!"))) { Result = S_Operator_Not; } +else if(AreEqual(String, StrLit("="))) { Result = S_Operator_Equal; } +else if(AreEqual(String, StrLit("=="))) { Result = S_Operator_Equals; } +else if(AreEqual(String, StrLit("!="))) { Result = S_Operator_NotEquals; } +else if(AreEqual(String, StrLit(">="))) { Result = S_Operator_GreaterThanOrEquals; } +else if(AreEqual(String, StrLit("<="))) { Result = S_Operator_LessThanOrEquals; } +else if(AreEqual(String, StrLit(">"))) { Result = S_Operator_Greater; } +else if(AreEqual(String, StrLit("<"))) { Result = S_Operator_Less; } +else if(AreEqual(String, StrLit("+"))) { Result = S_Operator_Add; } +else if(AreEqual(String, StrLit("-"))) { Result = S_Operator_Minus; } +else if(AreEqual(String, StrLit("*"))) { Result = S_Operator_Multiply; } +else if(AreEqual(String, StrLit("/"))) { Result = S_Operator_Divide; } +return(Result); +} diff --git a/code/vn.cpp b/code/vn.cpp index da9dd6b..b261af1 100644 --- a/code/vn.cpp +++ b/code/vn.cpp @@ -13,7 +13,7 @@ struct debug_settings b32 ShowWelcomeMessage; }; -per_thread debug_settings *DEBUG_DebugSettings = 0; +global debug_settings *DEBUG_DebugSettings = 0; #endif #include "vn_tokenizer.h" @@ -50,6 +50,9 @@ struct vn_state render_handle BackgroundTexture; + memory_arena *SceneArena; + compiled_scene CompiledScene; + #if VN_INTERNAL debug_settings DebugSettings; #endif @@ -107,7 +110,7 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) State->Config = CreateConfig(); //- sixten: load assets - State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/Futon_Room.png")); + //State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/Futon_Room.png")); //- sixten: setup config binds and load current config { @@ -120,9 +123,14 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1); #endif + Config_ReadFile(State->Config, StrLit("config.vn")); } + //- sixten: load startup scene + State->SceneArena = ArenaAllocate(Gigabytes(1)); + State->CompiledScene = S_ScriptFromText(State->SceneArena, Platform_ReadEntireFile(State->SceneArena, StrLit("data/compiler_test.vns"))); + UI_Init(&State->UI); Workspace_Init(&State->Workspace); AnimationCurve_Init(&State->AnimationCurveState); diff --git a/code/vn_config.cpp b/code/vn_config.cpp index a297a8e..7d8a2f4 100644 --- a/code/vn_config.cpp +++ b/code/vn_config.cpp @@ -87,7 +87,7 @@ static void Config_ReadFile(config *Config, string Path) //- sixten: read & tokenize input file string Text = Platform_ReadEntireFile(Scratch.Arena, Path); - tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Path, Text, TokenGroup_Whitespace|TokenGroup_Comment); + tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Text, T_IsIrregular); token_array Tokens = TokenizeResult.Tokens; // sixten: parse context @@ -103,7 +103,7 @@ static void Config_ReadFile(config *Config, string Path) string TokenString = Substring(Text, Token->Range); //- sixten: get next name - if(ParseMode == ConfigParseMode_Main && Token->Flags & TokenFlag_Identifier) + if(ParseMode == ConfigParseMode_Main && Token->Kind & TokenKind_Identifier) { Config_ParseListPush(Scratch.Arena, &FullPath, TokenString); ParseMode = ConfigParseMode_ScanForCurlyOpenOrEquals; @@ -112,7 +112,7 @@ static void Config_ReadFile(config *Config, string Path) } //- sixten: scan for curly close - if(ParseMode == ConfigParseMode_Main && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit("}"))) + if(ParseMode == ConfigParseMode_Main && Token->Kind == TokenKind_CurlyClose) { Config_ParseListPop(&FullPath); Token += 1; @@ -120,7 +120,7 @@ static void Config_ReadFile(config *Config, string Path) } //- sixten: scan for curly open - if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit("{"))) + if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Kind == TokenKind_CurlyOpen) { ParseMode = ConfigParseMode_Main; Token += 1; @@ -128,7 +128,7 @@ static void Config_ReadFile(config *Config, string Path) } //- sixten: scan for equals - if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Flags & TokenFlag_Symbol && AreEqual(TokenString, StrLit("="))) + if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Kind & TokenKind_Equal) { ParseMode = ConfigParseMode_ScanForValue; Token += 1; @@ -136,7 +136,7 @@ static void Config_ReadFile(config *Config, string Path) } //- sixten: scan for semicolon - if(ParseMode == ConfigParseMode_ScanForSemicolon && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit(";"))) + if(ParseMode == ConfigParseMode_ScanForSemicolon && Token->Kind == TokenKind_Semicolon) { ParseMode = ConfigParseMode_Main; Token += 1; @@ -144,7 +144,7 @@ static void Config_ReadFile(config *Config, string Path) } //- sixten: scan for boolean value - if(ParseMode == ConfigParseMode_ScanForValue && Token->Flags & TokenFlag_Identifier && (AreEqual(TokenString, StrLit("true")) || AreEqual(TokenString, StrLit("false")))) + if(ParseMode == ConfigParseMode_ScanForValue && (Token->Kind == TokenKind_True || Token->Kind == TokenKind_False)) { string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); config_entry *Entry = Config_FindEntryByName(Config, FullName); @@ -161,7 +161,7 @@ static void Config_ReadFile(config *Config, string Path) } //- sixten: scan for integer value - if(ParseMode == ConfigParseMode_ScanForValue && Token->Flags & TokenFlag_Numeric) + if(ParseMode == ConfigParseMode_ScanForValue && Token->Kind & TokenKind_Numeric) { string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); config_entry *Entry = Config_FindEntryByName(Config, FullName); diff --git a/code/vn_font.h b/code/vn_font.h index f5d1edb..f69dc3c 100644 --- a/code/vn_font.h +++ b/code/vn_font.h @@ -15,46 +15,51 @@ enum font_id Font_Count, }; -#define FontIcon_None 0x0000 -#define FontIcon_Pencil 0xe800 -#define FontIcon_Forward 0xe801 -#define FontIcon_Book 0xe802 -#define FontIcon_FolderOpen 0xe803 -#define FontIcon_Wrench 0xe804 -#define FontIcon_CW 0xe805 -#define FontIcon_CCW 0xe806 -#define FontIcon_ArrowsCW 0xe807 -#define FontIcon_ResizeVertical 0xe808 -#define FontIcon_ResizeHorizontal 0xe809 -#define FontIcon_Play 0xe80a -#define FontIcon_Stop 0xe80b -#define FontIcon_Floppy 0xe80c -#define FontIcon_Pause 0xe80d -#define FontIcon_Folder 0xe80e -#define FontIcon_Cog 0xe80f -#define FontIcon_Attention 0xe810 -#define FontIcon_Cancel 0xe811 -#define FontIcon_Filter 0xf0b0 -#define FontIcon_Menu 0xf0c9 -#define FontIcon_CircleEmpty 0xf10c -#define FontIcon_Circle 0xf111 -#define FontIcon_Reply 0xf112 -#define FontIcon_Terminal 0xf120 -#define FontIcon_Ellipsis 0xf141 -#define FontIcon_Document 0xf15b -#define FontIcon_DocumentText 0xf15c -#define FontIcon_Eyedropper 0xf1fb -#define FontIcon_WindowMaximize 0xf2d0 -#define FontIcon_WindowMinimize 0xf2d1 -#define FontIcon_WindowRestore 0xf2d2 -#define FontIcon_WindowClose 0xf2d4 -#define FontIcon_DownDir 0xe812 -#define FontIcon_UpDir 0xe813 -#define FontIcon_LeftDir 0xe814 -#define FontIcon_RightDir 0xe815 -#define FontIcon_TextAlignLeft 0xe816 -#define FontIcon_TextAlignCenter 0xe817 -#define FontIcon_TextAlignRight 0xe818 +#define FontIcon_None 0x0000 +#define FontIcon_Pencil 0xe800 +#define FontIcon_Forward 0xe801 +#define FontIcon_Book 0xe802 +#define FontIcon_FolderOpen 0xe803 +#define FontIcon_Wrench 0xe804 +#define FontIcon_CW 0xe805 +#define FontIcon_CCW 0xe806 +#define FontIcon_ArrowsCW 0xe807 +#define FontIcon_ResizeVertical 0xe808 +#define FontIcon_ResizeHorizontal 0xe809 +#define FontIcon_Play 0xe80a +#define FontIcon_Stop 0xe80b +#define FontIcon_Floppy 0xe80c +#define FontIcon_Pause 0xe80d +#define FontIcon_Folder 0xe80e +#define FontIcon_Cog 0xe80f +#define FontIcon_Attention 0xe810 +#define FontIcon_Cancel 0xe811 +#define FontIcon_Filter 0xf0b0 +#define FontIcon_Menu 0xf0c9 +#define FontIcon_CircleEmpty 0xf10c +#define FontIcon_Circle 0xf111 +#define FontIcon_Reply 0xf112 +#define FontIcon_Terminal 0xf120 +#define FontIcon_Ellipsis 0xf141 +#define FontIcon_Document 0xf15b +#define FontIcon_DocumentText 0xf15c +#define FontIcon_Eyedropper 0xf1fb +#define FontIcon_WindowMaximize 0xf2d0 +#define FontIcon_WindowMinimize 0xf2d1 +#define FontIcon_WindowRestore 0xf2d2 +#define FontIcon_WindowClose 0xf2d4 +#define FontIcon_DownDir 0xe812 +#define FontIcon_UpDir 0xe813 +#define FontIcon_LeftDir 0xe814 +#define FontIcon_RightDir 0xe815 +#define FontIcon_TextAlignLeft 0xe816 +#define FontIcon_TextAlignCenter 0xe817 +#define FontIcon_TextAlignRight 0xe818 +#define FontIcon_DocumentInverted 0xf15b +#define FontIcon_DocumentInvertedText 0xf15c +#define FontIcon_DocumentFileImage 0xf1c5 +#define FontIcon_DocumentFileAudio 0xf1c7 +#define FontIcon_DocumentFileCode 0xf1c9 struct glyph { @@ -72,7 +77,7 @@ struct glyph r32 Advance; }; -#define DEFAULT_GLYPH_ATLAS_DIM 1024 +#define DEFAULT_GLYPH_ATLAS_DIM 1024*4 #define MAX_GLYPH_SIZE 64 #define STB_TRUETYPE_IMPLEMENTATION diff --git a/code/vn_platform.h b/code/vn_platform.h index bac4608..8717bc1 100644 --- a/code/vn_platform.h +++ b/code/vn_platform.h @@ -24,6 +24,17 @@ struct platform_file_handle b32 IsValid; }; +struct platform_file_info +{ + string Name; + b32 IsDirectory; +}; + +struct platform_file_iter +{ + u64 U64[1024]; +}; + enum platform_cursor { PlatformCursor_Arrow, @@ -77,7 +88,7 @@ enum platform_key Key_Left, Key_Right, Key_Up, Key_Down, - Key_Space, + Key_Space, Key_Return, Key_PageUp, Key_PageDown, Key_Home, Key_End, diff --git a/code/vn_platform.md b/code/vn_platform.md index 47d743d..86c768b 100644 --- a/code/vn_platform.md +++ b/code/vn_platform.md @@ -1,17 +1,20 @@ @table(Name, NameLower, NameCaps, Type, Arguments) platform_functions: { - { Reserve reserve RESERVE `void *` `u64 Size` } - { Release release RELEASE `void` `void *Pointer` } - { Commit commit COMMIT `void` `void *Pointer, u64 Size` } - { Decommit decommit DECOMMIT `void` `void *Pointer, u64 Size` } - { OpenFile open_file OPEN_FILE `platform_file_handle` `string Path, platform_access_flags FileAccess` } - { CloseFile close_file CLOSE_FILE `void` `platform_file_handle Handle` } - { ReadFile read_file READ_FILE `void` `platform_file_handle Handle, void *Dest, u64 Offset, u64 Size` } - { WriteFile write_file WRITE_FILE `void` `platform_file_handle Handle, void *Source, u64 Offset, u64 Size` } - { GetFileSize get_file_size GET_FILE_SIZE `u64` `platform_file_handle Handle` } - { SetCursor set_cursor SET_CURSOR `void` `platform_cursor Cursor` } - { ToggleFullscreen toggle_fullscreen TOGGLE_FULLSCREEN `void` `void` } - { ShowMessage show_message SHOW_MESSAGE `void` `string Message, platform_message_type Type` } + { Reserve reserve RESERVE `void *` `u64 Size` } + { Release release RELEASE `void` `void *Pointer` } + { Commit commit COMMIT `void` `void *Pointer, u64 Size` } + { Decommit decommit DECOMMIT `void` `void *Pointer, u64 Size` } + { OpenFile open_file OPEN_FILE `platform_file_handle` `string Path, platform_access_flags FileAccess` } + { CloseFile close_file CLOSE_FILE `void` `platform_file_handle Handle` } + { ReadFile read_file READ_FILE `void` `platform_file_handle Handle, void *Dest, u64 Offset, u64 Size` } + { WriteFile write_file WRITE_FILE `void` `platform_file_handle Handle, void *Source, u64 Offset, u64 Size` } + { GetFileSize get_file_size GET_FILE_SIZE `u64` `platform_file_handle Handle` } + { SetCursor set_cursor SET_CURSOR `void` `platform_cursor Cursor` } + { ToggleFullscreen toggle_fullscreen TOGGLE_FULLSCREEN `void` `void` } + { ShowMessage show_message SHOW_MESSAGE `void` `string Message, platform_message_type Type` } + { BeginFileIter begin_file_iter BEGIN_FILE_ITER `platform_file_iter *` `memory_arena *Arena, string Path` } + { AdvanceFileIter advance_file_iter ADVANCE_FILE_ITER `b32` `memory_arena *Arena, platform_file_iter *Iter, platform_file_info *OutInfo` } + { EndFileIter end_file_iter END_FILE_ITER `void` `platform_file_iter *Iter` } } @table_gen diff --git a/code/vn_scene.cpp b/code/vn_scene.cpp index 08611a8..ae3b8a0 100644 --- a/code/vn_scene.cpp +++ b/code/vn_scene.cpp @@ -1,63 +1,592 @@ -//////////////////////////////// -//~ sixten: Scene Message Functions -static void S_PushMessage(memory_arena *Arena, scene_message_list *Messages, scene_node *Node, scene_message_type Type, string String) +#include "generated/vn_scene.meta.h" +#include "generated/vn_scene.meta.c" + +static void S_EmitByte(scene_compiler *Compiler, u8 Byte) { - scene_message *Message = PushStruct(Arena, scene_message); - Message->Type = Type; - Message->Node = Node; - Message->String = String; - QueuePush(Messages->First, Messages->Last, Message); - Messages->Count += 1; + scene_annotated_bytecode_bucket *Bucket = Compiler->CurrentBucket; + scene_annotated_bytecode_chunk *Chunk = Bucket->Last; + if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Data) || !AreEqual(Chunk->Name, Compiler->CurrentName)) + { + Chunk = PushStruct(Compiler->Arena, scene_annotated_bytecode_chunk); + Chunk->Name = Compiler->CurrentName; + + QueuePush(Bucket->First, Bucket->Last, Chunk); + Bucket->Count += 1; + } + Chunk->Data[Chunk->Count] = Byte; + Chunk->Count += 1; } -//////////////////////////////// -//~ sixten: Scene Node Functions -static scene_node *S_SceneFromText(memory_arena *Arena, string Filename, string Text) +static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value) { - tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Filename, Text, TokenGroup_Comment|TokenGroup_Whitespace); - scene_parse_result ParseResult = S_ParseFromTokens(Arena, Filename, Text, TokenizeResult.Tokens); - scene_node *Result = ParseResult.Root; + scene_value_chunk *Chunk = Compiler->LastValueChunk; + if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Values)) + { + Chunk = PushStruct(Compiler->Arena, scene_value_chunk); + QueuePush(Compiler->FirstValueChunk, Compiler->LastValueChunk, Chunk); + } + Chunk->Values[Chunk->Count] = Value; + u64 Result = Compiler->ValueCount; + Compiler->ValueCount += 1; + Chunk->Count += 1; return(Result); } -//////////////////////////////// -//~ sixten: Tokens -> Syntax Tree -static scene_parse_result S_ParseFromTokens(memory_arena *Arena, string Filename, string Text, token_array Tokens) + +static void S_EmitVariableLength(scene_compiler *Compiler, u64 Value) { + u64 Index = Value; + for(;Index > 0x7F; Index >>= 7) + { + S_EmitByte(Compiler, Index|0x80); + InvalidCodepath; + } + S_EmitByte(Compiler, Index); +} + +static u64 S_ReadVariableLength(u8 **BytePtr) +{ + u64 Result = 0; + u8 *Byte = *BytePtr; + for(;*Byte & 0x80; Byte += 1) + { + Result = (Result<<7)|(*Byte & 0x7F); + } + Result = (Result<<7)|(*Byte & 0x7F); + *BytePtr = Byte; + return(Result); +} + +static void S_EmitConstant(scene_compiler *Compiler, scene_value Value) +{ + S_EmitByte(Compiler, S_Op_Constant); + S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, Value)); +} + +static void S_SetEmissionTarget(scene_compiler *Compiler, string Target) +{ + if(AreEqual(Target, StrLit(""))) + { + Compiler->CurrentBucket = &Compiler->GlobalScope; + Compiler->CurrentName = StrLit("Global Scope"); + } + else + { + u64 Hash = HashString(Target); + Compiler->CurrentBucket = &Compiler->ProcBuckets[Hash % ArrayCount(Compiler->ProcBuckets)]; + Compiler->CurrentName = Target; + } +} + +static scene_annotated_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name) +{ + scene_annotated_bytecode_chunk *Result = 0; + u64 Hash = HashString(Name); + scene_annotated_bytecode_bucket *Bucket = &Compiler->ProcBuckets[Hash%ArrayCount(Compiler->ProcBuckets)]; + for(scene_annotated_bytecode_chunk *Chunk = Bucket->First; Chunk != 0; Chunk = Chunk->Next) + { + if(AreEqual(Chunk->Name, Name)) + { + Result = Chunk; + break; + } + } + return(Result); +} + +static void S_AdvanceCompiler(scene_compiler *Compiler) +{ + Compiler->At += 1; +} + +static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token) +{ + scene_parse_rule Result = {}; + switch(Token.Kind) + { + case TokenKind_ParenthesisOpen: { Result = { S_ParseGrouping, 0, S_Precedence_None }; } break; + case TokenKind_Bang: { Result = { S_ParseUnary, 0, S_Precedence_None }; } break; + case TokenKind_Minus: { Result = { S_ParseUnary, S_ParseBinary, S_Precedence_Term }; } break; + case TokenKind_Plus: { Result = { 0, S_ParseBinary, S_Precedence_Term }; } break; + case TokenKind_Star: { Result = { 0, S_ParseBinary, S_Precedence_Factor }; } break; + case TokenKind_Slash: { Result = { 0, S_ParseBinary, S_Precedence_Factor }; } break; + case TokenKind_EqualEqual: { Result = { 0, S_ParseBinary, S_Precedence_Equality }; } break; + case TokenKind_BangEqual: { Result = { 0, S_ParseBinary, S_Precedence_Equality }; } break; + case TokenKind_Greater: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_GreaterEqual: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_Less: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_LessEqual: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_False: { Result = { S_ParseLiteral, 0, S_Precedence_None }; } break; + case TokenKind_True: { Result = { S_ParseLiteral, 0, S_Precedence_None }; } break; + case TokenKind_Numeric: { Result = { S_ParseNumber, 0, S_Precedence_None }; } break; + case TokenKind_Identifier: { Result = { S_ParseVariable, 0, S_Precedence_None }; } break; + default: + { + //InvalidCodepath; + } break; + } + + return(Result); +} + +static b32 S_MatchToken(scene_compiler *Compiler, token Token, token_kind Kind) +{ + b32 Result = false; + string String = T_StringFromToken(Compiler->Text, Token); + if(Token.Kind == Kind) + { + Result = true; + } + return(Result); +} + +static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message) +{ + token Token = Compiler->At[0]; + string String = T_StringFromToken(Compiler->Text, Token); + if(Token.Kind != Kind) + { + S_ParseError(Compiler, Message); + } + + Compiler->At += 1; + return(Token); +} + +static void S_ParseTopLevelDeclaration(scene_compiler *Compiler) +{ + if(Compiler->At[0].Kind == TokenKind_Proc) + { + Compiler->At += 1; + S_ParseProcedure(Compiler); + } + else if(Compiler->At[0].Kind == TokenKind_Var) + { + Compiler->At += 1; + S_ParseVariableDeclaration(Compiler); + } + else + { + S_ParseError(Compiler, "Expected top-level declaration (proc or var).."); + } +} + +static void S_ParseProcedure(scene_compiler *Compiler) +{ + token NameToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected procedure name after 'proc'"); + S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after procedure name."); + + S_SetEmissionTarget(Compiler, T_StringFromToken(Compiler->Text, NameToken)); + + for(;Compiler->At < Compiler->TokensEnd;) + { + if(Compiler->At[0].Kind == TokenKind_CurlyClose) + { + Compiler->At += 1; + break; + } + else + { + S_ParseDeclaration(Compiler); + } + } +} + +static void S_ParseDeclaration(scene_compiler *Compiler) +{ + switch(Compiler->At[0].Kind) + { + case TokenKind_Var: + { + Compiler->At += 1; + S_ParseVariableDeclaration(Compiler); + } break; + case TokenKind_StringLiteral: + { + Compiler->At += 1; + S_ParseLineEntry(Compiler); + } break; + default: + { + S_ParseStatement(Compiler); + } break; + } +} + +static void S_ParseVariableDeclaration(scene_compiler *Compiler) +{ + S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected variable name."); + u64 NameConstant = S_MakeConstant(Compiler, S_MakePointer(&Compiler->At[-1])); + + if(Compiler->At[0].Kind == TokenKind_Equal) + { + Compiler->At += 1; + S_ParseExpression(Compiler); + } + else + { + S_EmitByte(Compiler, S_Op_Nil); + } + + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after variable declaration."); + + S_EmitByte(Compiler, S_Op_DefineGlobal); + u64 Index = NameConstant; + for(;Index > 0x7F; Index >>= 7) + { + S_EmitByte(Compiler, Index|0x80); + InvalidCodepath; + } + S_EmitByte(Compiler, Index); +} + +static void S_ParseVariable(scene_compiler *Compiler, b32 CanAssign) +{ + S_ParseNamedVariable(Compiler, &Compiler->At[-1], CanAssign); +} + +static void S_ParseNamedVariable(scene_compiler *Compiler, token *Token, b32 CanAssign) +{ + u64 NameConstant = S_MakeConstant(Compiler, S_MakePointer(Token)); + if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) + { + Compiler->At += 1; + S_ParseExpression(Compiler); + S_EmitByte(Compiler, S_Op_SetGlobal); + } + else + { + S_EmitByte(Compiler, S_Op_GetGlobal); + } + S_EmitVariableLength(Compiler, NameConstant); +} + +static void S_ParseLineEntry(scene_compiler *Compiler) +{ + token *LineToken = &Compiler->At[-1]; + + b32 EmitAwait = true; + + // sixten: tags -> flags + scene_line_entry_flag Flags = 0; + for(;Compiler->At[0].Kind == TokenKind_PoundSign;) + { + Compiler->At += 1; + token TagToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected tag name after '#'."); + string TagString = T_StringFromToken(Compiler->Text, TagToken); + if(AreEqual(TagString, StrLit("noclear"))) + { + Flags |= S_LineEntryFlag_NoClear; + } + else if(AreEqual(TagString, StrLit("noawait"))) + { + EmitAwait = false; + } + else + { + S_ParseError(Compiler, "Unknown tag."); + } + } + + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after line entry."); + + S_EmitByte(Compiler, S_Op_LineEntry|Flags); + S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakePointer(LineToken))); + if(EmitAwait) + { + S_EmitByte(Compiler, S_Op_AwaitInput); + } +} + +static void S_ParseStatement(scene_compiler *Compiler) +{ + S_ParseExpression(Compiler); + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement."); +} + +static void S_ParseExpression(scene_compiler *Compiler) +{ + S_ParsePrecedence(Compiler, S_Precedence_Assignment); +} + +static void S_ParseLiteral(scene_compiler *Compiler, b32 CanAssign) +{ + string Value = T_StringFromToken(Compiler->Text, Compiler->At[-1]); + switch(Compiler->At[-1].Kind) + { + case TokenKind_False: { S_EmitByte(Compiler, S_Op_False); } break; + case TokenKind_True: { S_EmitByte(Compiler, S_Op_True); } break; + InvalidDefaultCase; + } +} + +static void S_ParseNumber(scene_compiler *Compiler, b32 CanAssign) +{ + r64 Value = DoubleFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1])); + + S_EmitConstant(Compiler, S_MakeNumber(Value)); +} + +static void S_ParseGrouping(scene_compiler *Compiler, b32 CanAssign) +{ + S_ParseExpression(Compiler); + S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after expression."); +} + +static void S_ParseUnary(scene_compiler *Compiler, b32 CanAssign) +{ + scene_operator Operator = S_OperatorFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1])); + S_ParsePrecedence(Compiler, S_Precedence_Unary); + + switch(Operator) + { + case S_Operator_Minus: { S_EmitByte(Compiler, S_Op_Negate); } break; + case S_Operator_Not: { S_EmitByte(Compiler, S_Op_Not); } break; + InvalidDefaultCase; + } +} + +static void S_ParseBinary(scene_compiler *Compiler, b32 CanAssign) +{ + token Token = Compiler->At[-1]; + scene_operator Operator = S_OperatorFromString(T_StringFromToken(Compiler->Text, Token)); + scene_parse_rule Rule = S_ParseRuleFromToken(Compiler, Token); + S_ParsePrecedence(Compiler, (scene_precedence)(Rule.Precedence + 1)); + + switch(Operator) + { + case S_Operator_Add: { S_EmitByte(Compiler, S_Op_Add); } break; + case S_Operator_Minus: { S_EmitByte(Compiler, S_Op_Subtract); } break; + case S_Operator_Multiply: { S_EmitByte(Compiler, S_Op_Multiply); } break; + case S_Operator_Divide: { S_EmitByte(Compiler, S_Op_Divide); } break; + case S_Operator_Equals: { S_EmitByte(Compiler, S_Op_Equal); } break; + case S_Operator_NotEquals: { S_EmitByte(Compiler, S_Op_Equal); S_EmitByte(Compiler, S_Op_Not); } break; + case S_Operator_Greater: { S_EmitByte(Compiler, S_Op_Greater); } break; + case S_Operator_GreaterThanOrEquals: { S_EmitByte(Compiler, S_Op_Less); S_EmitByte(Compiler, S_Op_Not); } break; + case S_Operator_Less: { S_EmitByte(Compiler, S_Op_Less); } break; + case S_Operator_LessThanOrEquals: { S_EmitByte(Compiler, S_Op_Greater); S_EmitByte(Compiler, S_Op_Not); } break; + InvalidDefaultCase; + } +} + +static void S_ParsePrecedence(scene_compiler *Compiler, scene_precedence Precedence) +{ + b32 CanAssign = (Precedence <= S_Precedence_Assignment); + + S_AdvanceCompiler(Compiler); + scene_parse_rule Rule = S_ParseRuleFromToken(Compiler, Compiler->At[-1]); + if(Rule.PrefixRule) + { + Rule.PrefixRule(Compiler, CanAssign); + } + else + { + S_ParseError(Compiler, "Expected expression."); + } + + + while(Precedence <= (Rule = S_ParseRuleFromToken(Compiler, Compiler->At[0])).Precedence) + { + S_AdvanceCompiler(Compiler); + Rule.InfixRule(Compiler, CanAssign); + } + + if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) + { + S_ParseError(Compiler, "Invalid assignment target."); + } +} + +static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_bytecode_chunk *Chunk, memory_arena *Arena) +{ + string_list List = {}; + temporary_memory Scratch = GetScratch(&Arena, 1); - //scene_node *Root = {}; - - //- sixten: setup parse context - scene_parse_context Context = {}; + u8 *ChunkBegin = Chunk->Data; + u8 *ChunkEnd = ChunkBegin + Chunk->Count; + for(u8 *Data = ChunkBegin; Data < ChunkEnd; Data += 1) { - Context.TokensStart = Tokens.Tokens; - Context.TokensEnd = Context.TokensStart + Tokens.Count; - Context.Token = Context.TokensStart; - } - - //- sixten: parse top-level - for(;Context.Token < Context.TokensEnd;) - { - string TokenString = PushString(Scratch.Arena, T_StringFromToken(Text, *Context.Token)); - - if(T_TokenMatches(*Context.Token, TokenFlag_Identifier, Text, StrLit("proc"))) + switch(*Data) { - Context.Token += 1; + case S_Op_Constant: + { + Data += 1; + u64 ValueIndex = S_ReadVariableLength(&Data); + scene_value Value = Compiler->FirstValueChunk->Values[ValueIndex]; + AppendString(&List, StrLit("Constant: "), Scratch.Arena); + switch(Value.Kind) + { + case S_ValueKind_Number: { AppendString(&List, PushFormat(Scratch.Arena, "%f (number)\n", Value.Number), Scratch.Arena); } break; + case S_ValueKind_Boolean: { AppendString(&List, PushFormat(Scratch.Arena, "%b (boolean)\n", Value.Boolean), Scratch.Arena); } break; + case S_ValueKind_Pointer: { AppendString(&List, PushFormat(Scratch.Arena, "%x (pointer)\n", Value.Pointer), Scratch.Arena); } break; + } + } break; + case S_Op_Nil: { AppendString(&List, StrLit("Nil\n"), Scratch.Arena); } break; + case S_Op_True: { AppendString(&List, StrLit("True\n"), Scratch.Arena); } break; + case S_Op_False: { AppendString(&List, StrLit("False\n"), Scratch.Arena); } break; + case S_Op_Negate: { AppendString(&List, StrLit("Negate\n"), Scratch.Arena); } break; + case S_Op_Not: { AppendString(&List, StrLit("Not\n"), Scratch.Arena); } break; + case S_Op_Add: { AppendString(&List, StrLit("Add\n"), Scratch.Arena); } break; + case S_Op_Subtract: { AppendString(&List, StrLit("Subtract\n"), Scratch.Arena); } break; + case S_Op_Multiply: { AppendString(&List, StrLit("Multiply\n"), Scratch.Arena); } break; + case S_Op_Divide: { AppendString(&List, StrLit("Divide\n"), Scratch.Arena); } break; + case S_Op_Equal: { AppendString(&List, StrLit("Equal\n"), Scratch.Arena); } break; + case S_Op_Greater: { AppendString(&List, StrLit("Greater\n"), Scratch.Arena); } break; + case S_Op_Less: { AppendString(&List, StrLit("Less\n"), Scratch.Arena); } break; + case S_Op_DefineGlobal: + { + Data += 1; + + u64 Index = S_ReadVariableLength(&Data); + u64 Pointer = Compiler->FirstValueChunk->Values[Index].Pointer; + token *Token = (token *)Pointer; + string String = T_StringFromToken(Compiler->Text, *Token); + AppendString(&List, StrLit("Define Global: "), Scratch.Arena); + AppendString(&List, String, Scratch.Arena); + AppendString(&List, StrLit("\n"), Scratch.Arena); + } break; - goto TokenConsumed; + case S_Op_GetGlobal: + { + Data += 1; + u64 Pointer = Compiler->FirstValueChunk->Values[S_ReadVariableLength(&Data)].Pointer; + token *Token = (token *)Pointer; + string String = T_StringFromToken(Compiler->Text, *Token); + AppendString(&List, PushFormat(Scratch.Arena, "Get Global: %S\n", String), Scratch.Arena); + } break; + case S_Op_SetGlobal: + { + Data += 1; + u64 Pointer = Compiler->FirstValueChunk->Values[S_ReadVariableLength(&Data)].Pointer; + token *Token = (token *)Pointer; + string String = T_StringFromToken(Compiler->Text, *Token); + AppendString(&List, PushFormat(Scratch.Arena, "Set Global: %S\n", String), Scratch.Arena); + } break; + case S_Op_AwaitInput: { AppendString(&List, StrLit("Await Input\n"), Scratch.Arena); } break; + default: + { + if(*Data & S_Op_LineEntry) + { + Data += 1; + u64 Pointer = Compiler->FirstValueChunk->Values[S_ReadVariableLength(&Data)].Pointer; + token *Token = (token *)Pointer; + string String = Substring(Compiler->Text, Pad(Token->Range, -1)); + AppendString(&List, PushFormat(Scratch.Arena, "Line Entry: %S\n", String), Scratch.Arena); + } + else + { + AppendString(&List, StrLit("Unknown Op\n"), Scratch.Arena); + } + } break; } - //- sixten: token was not consumed, something has gone wrong - { - Context.Token += 1; - } - TokenConsumed:; } + string Result = JoinStringList(&List, Arena); + ReleaseScratch(Scratch); + return(Result); +} + +struct proc_from_chunks_result +{ + scene_proc *Proc; + scene_annotated_bytecode_chunk *NextChunk; +}; + +static proc_from_chunks_result S_ProcFromChunks(memory_arena *Arena, scene_annotated_bytecode_chunk *First) +{ + Assert(First != 0); + string ChunkName = First->Name; - scene_parse_result Result = + //- sixten: find required bytes + s64 RequiredBytes = 0; + scene_annotated_bytecode_chunk *NextChunk = 0; { - 0 + scene_annotated_bytecode_chunk *Chunk = First; + for(; Chunk != 0 && AreEqual(Chunk->Name, ChunkName); Chunk = Chunk->Next) + { + RequiredBytes += Chunk->Count; + } + NextChunk= Chunk; + } + + scene_proc *Proc = PushStruct(Arena, scene_proc); + Proc->Name = ChunkName; + Proc->Data = PushArray(Arena, u8, RequiredBytes); + Proc->Count = RequiredBytes; + + //- sixten: copy over data from chunks + u8 *Dest = Proc->Data; + for(scene_annotated_bytecode_chunk *Chunk = First; Chunk != NextChunk; Chunk = Chunk->Next) + { + Copy(Dest, Chunk->Data, Chunk->Count); + Dest += Chunk->Count; + } + + //- sixten: fill & return + proc_from_chunks_result Result; + { + Result.Proc = Proc; + Result.NextChunk = NextChunk; + } + return(Result); +} + +static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text) +{ + compiled_scene Result = {}; + + temporary_memory Scratch = GetScratch(&Arena, 1); + tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text, T_IsIrregular); + + // sixten(TODO): append token errors + + //- sixten: tokens -> bytecode + scene_compiler Compiler = {}; + { + Compiler.Arena = Scratch.Arena; + Compiler.Text = Text; + Compiler.TokensBegin = TokenizeResult.Tokens.Tokens; + Compiler.TokensEnd = Compiler.TokensBegin+TokenizeResult.Tokens.Count; + Compiler.At = Compiler.TokensBegin; }; + + S_SetEmissionTarget(&Compiler, StrLit("")); + + for(;Compiler.At < Compiler.TokensEnd;) + { + S_ParseTopLevelDeclaration(&Compiler); + } + + //- sixten: bake compiled chunks + for(s64 BucketIndex = 0; BucketIndex < ArrayCount(Compiler.ProcBuckets); BucketIndex += 1) + { + scene_annotated_bytecode_bucket *Bucket = &Compiler.ProcBuckets[BucketIndex]; + for(scene_annotated_bytecode_chunk *Chunk = Bucket->First; Chunk != 0;) + { + proc_from_chunks_result ProcResult = S_ProcFromChunks(Arena, Chunk); + s64 Hash = HashString(Chunk->Name); + scene_proc_bucket *DestBucket = &Result.Buckets[Hash%ArrayCount(Result.Buckets)]; + QueuePush(DestBucket->First, DestBucket->Last, ProcResult.Proc); + Chunk = ProcResult.NextChunk; + } + } + + //- sixten: bake value chunks + { + Result.Values = PushArray(Arena, scene_value, Compiler.ValueCount); + Result.ValueCount = Compiler.ValueCount; + scene_value *Dest = Result.Values; + for(scene_value_chunk *Chunk = Compiler.FirstValueChunk; Chunk != 0; Chunk = Chunk->Next) + { + Copy(Dest, Chunk->Values, Chunk->Count); + Dest += Chunk->Count; + } + } + + // sixten(IMPORTANT): The text is assumed to remain in memory for the duration of the scene. + Result.Source = Text; + + ReleaseScratch(Scratch); return(Result); } \ No newline at end of file diff --git a/code/vn_scene.h b/code/vn_scene.h index 8d6dd6a..b41cf5e 100644 --- a/code/vn_scene.h +++ b/code/vn_scene.h @@ -1,97 +1,240 @@ -/* date = July 9th 2023 11:08 am */ +/* date = July 30th 2023 7:45 pm */ #ifndef VN_SCENE_H #define VN_SCENE_H -//////////////////////////////// -//~ sixten: Scene Node Types -enum scene_node_kind -{ - S_NodeKind_Invalid, - S_NodeKind_File, - S_NodeKind_Proc, - S_NodeKind_Text, - S_NodeKind_Branch, - S_NodeKind_Count, -}; - -struct scene_node -{ - scene_node *Next; - scene_node *Prev; - scene_node *First; - scene_node *Last; - scene_node *Parent; - - scene_node *FirstTag; - scene_node *LastTag; - - scene_node_kind Kind; - string String; - - u64 SourceOffset; -}; +// sixten(IMPORTANT): The arena that the scene is compiled on +// is assumed to be the same it runs on, i.e. the interpreter relies +// on the memory addresses being the same during compilation and +// runtime. +// We also assume that the original text and tokenization remains in +// memory, for the duration of the scene. //////////////////////////////// -//~ sixten: Scene Messages -enum scene_message_type +//~ sixten: Scene Compilation Types +typedef u64 scene_line_entry_flag; +enum { - S_MessageType_Invalid, - S_MessageType_Note, - S_MessageType_Warning, - S_MessageType_Error, + S_LineEntryFlag_NoClear = (1<<0), }; -struct scene_message +enum scene_opcode { - scene_message_type Type; - scene_message *Next; - scene_node *Node; - string String; + S_Op_Invalid = 0, + + S_Op_Constant, + + S_Op_Nil, + S_Op_True, + S_Op_False, + + S_Op_Add, + S_Op_Subtract, + S_Op_Multiply, + S_Op_Divide, + + S_Op_Equal, + S_Op_Greater, + S_Op_Less, + + S_Op_Negate, + S_Op_Not, + + S_Op_DefineGlobal, + S_Op_GetGlobal, + S_Op_SetGlobal, + + S_Op_AwaitInput, + + S_Op_LineEntry = 0x80, // sixten(NOTE): All opcoodes above are reserved. }; -struct scene_message_list +struct scene_bytecode_chunk { - scene_message *First; - scene_message *Last; + scene_bytecode_chunk *Next; + s64 Count; + u8 Data[4096]; +}; + +struct scene_annotated_bytecode_chunk +{ + scene_annotated_bytecode_chunk *Next; + string Name; + s64 Count; + u8 Data[4096]; +}; + +struct scene_annotated_bytecode_bucket +{ + scene_annotated_bytecode_chunk *First; + scene_annotated_bytecode_chunk *Last; s64 Count; }; -//////////////////////////////// -//~ sixten: Text -> Tokens -struct scene_tokenize_result +enum scene_value_kind { - token_array Tokens; - scene_message_list Messages; + S_ValueKind_Number, + S_ValueKind_Boolean, + S_ValueKind_Pointer, }; -struct scene_parse_context +struct scene_value { - token *TokensStart; + scene_value_kind Kind; + union + { + r64 Number; + b32 Boolean; + u64 Pointer; + }; +}; + +struct scene_value_chunk +{ + scene_value_chunk *Next; + s64 Count; + scene_value Values[512]; +}; + +enum scene_precedence +{ + S_Precedence_None, + S_Precedence_Assignment, + S_Precedence_Or, + S_Precedence_And, + S_Precedence_Equality, + S_Precedence_Comparison, + S_Precedence_Term, + S_Precedence_Factor, + S_Precedence_Unary, + S_Precedence_Call, + S_Precedence_Primary, +}; + +typedef void scene_parse_function(struct scene_compiler *Compiler, b32 CanAssign); + +struct scene_parse_rule +{ + scene_parse_function *PrefixRule; + scene_parse_function *InfixRule; + scene_precedence Precedence; +}; + +struct scene_compiler +{ + memory_arena *Arena; + + string Text; + token *TokensBegin; token *TokensEnd; - token *Token; + token *At; + + scene_annotated_bytecode_bucket GlobalScope; + scene_annotated_bytecode_bucket ProcBuckets[32]; + + scene_annotated_bytecode_bucket *CurrentBucket; + string CurrentName; + + scene_value_chunk *FirstValueChunk; + scene_value_chunk *LastValueChunk; + s64 ValueCount; }; //////////////////////////////// -//~ sixten: Tokens -> Syntax Tree -struct scene_parse_result +//~ sixten: Compiled Scene Types +struct scene_proc { - scene_node *Root; - scene_message_list Messages; + // sixten: scene data + string Name; + u8 *Data; + s64 Count; + + // sixten: hash link + scene_proc *Next; +}; + +struct scene_proc_bucket +{ + scene_proc *First; + scene_proc *Last; +}; + +struct compiled_scene +{ + scene_proc_bucket Buckets[16]; + scene_value *Values; + s64 ValueCount; + string Source; }; //////////////////////////////// -//~ sixten: Scene Message Functions -static void S_PushMessage(memory_arena *Arena, scene_message_list *Messages, scene_node *Node, scene_message_type Type, string String);; +//~ sixten: Scene Compiler Functions +//- sixten: value helpers +inline scene_value S_MakeNumber(r64 Value) +{ + scene_value Result; + Result.Kind = S_ValueKind_Number; + Result.Number = Value; + return(Result); +} + +inline scene_value S_MakeBoolean(b32 Value) +{ + scene_value Result; + Result.Kind = S_ValueKind_Boolean; + Result.Boolean = Value; + return(Result); +} + +inline scene_value S_MakePointer(void *Value) +{ + scene_value Result; + Result.Kind = S_ValueKind_Pointer; + Result.Pointer = PointerToU64(Value); + return(Result); +} + +//- sixten: error messaging +static void S_ParseError(scene_compiler *Compiler, char *Message) { InvalidCodepath; } + +//- sixten: bytecode helpers +static void S_EmitByte(scene_compiler *Compiler, u8 Byte); +static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value); +static void S_EmitVariableLength(scene_compiler *Compiler, u64 Value); +static u64 S_ReadVariableLength(u8 **BytePtr); +static void S_EmitConstant(scene_compiler *Compiler, scene_value Value); +static void S_SetEmissionTarget(scene_compiler *Compiler, string Target); +static scene_annotated_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name); + +//- sixten: parsing helpers +static void S_AdvanceCompiler(scene_compiler *Compiler); +static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token); +static b32 S_MatchToken(scene_compiler *Compiler, token Token, token_kind Kind); +static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message); + +//- sixten: parsing +static void S_ParseTopLevelDeclaration(scene_compiler *Compiler); +static void S_ParseProcedure(scene_compiler *Compiler); +static void S_ParseDeclaration(scene_compiler *Compiler); +static void S_ParseVariableDeclaration(scene_compiler *Compiler); +static void S_ParseVariable(scene_compiler *Compiler, b32 CanAssign); +static void S_ParseNamedVariable(scene_compiler *Compiler, token *Token, b32 CanAssign); +static void S_ParseLineEntry(scene_compiler *Compiler); +static void S_ParseStatement(scene_compiler *Compiler); +static void S_ParseExpression(scene_compiler *Compiler); +static void S_ParseLiteral(scene_compiler *Compiler, b32 CanAssign); +static void S_ParseNumber(scene_compiler *Compiler, b32 CanAssign); +static void S_ParseGrouping(scene_compiler *Compiler, b32 CanAssign); +static void S_ParseUnary(scene_compiler *Compiler, b32 CanAssign); +static void S_ParseBinary(scene_compiler *Compiler, b32 CanAssign); +static void S_ParsePrecedence(scene_compiler *Compiler, scene_precedence Precedence); + +//- sixten: debugging +static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_bytecode_chunk *Chunk, memory_arena *Arena); + +//- sixten: api +static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text); -//////////////////////////////// -//~ sixten: Scene Node Functions -static scene_node *S_PushNode(memory_arena *Arena, scene_node_kind Kind, string String); -static scene_node *S_SceneFromText(memory_arena *Arena, string Text); -//////////////////////////////// -//~ sixten: Tokens -> Syntax Tree -static scene_node *S_ParseProc(scene_parse_context *Context, string ProcName); -static scene_parse_result S_ParseFromTokens(memory_arena *Arena, string Filename, string Text, token_array Tokens); #endif //VN_SCENE_H diff --git a/code/vn_scene.md b/code/vn_scene.md index 681c947..59c77de 100644 --- a/code/vn_scene.md +++ b/code/vn_scene.md @@ -1,34 +1,35 @@ -@table(Name, NameLower, Contents) scene_ast_node_types: +@table(Name, Operator) scene_operator_table: { - { Invalid invalid `` } - { BlockStatement block_statement `scene_ast_node *First; scene_ast_node *Last;` } - { SceneDecl scene_declaration `` } -} - -@table_gen_enum scene_ast_node_type: -{ - @expand(scene_ast_node_types s) `S_AstNode_$(s.Name),`; - `S_AstNode_Count,`: + { Invalid, "###" } + { Not, "!" } + { Equal, "=" } + { Equals, "==" } + { NotEquals, "!=" } + { GreaterThanOrEquals, ">=" } + { LessThanOrEquals, "<=" } + { Greater, ">" } + { Less, "<" } + { Add, "+" } + { Minus, "-" } + { Multiply "*" } + { Divide, "/" } } @table_gen { - `struct scene_ast_node;` -} - -@table_gen -{ - @expand(scene_ast_node_types s) `struct scene_ast_node_$(s.NameLower) {$(s.Contents)};` -} - -@table_gen -{ - `struct scene_ast_node`; + `enum scene_operator`; `{`; - `scene_ast_node_type Type;`; - `union`; - `{`; - @expand(scene_ast_node_types s) `scene_ast_node_$(s.NameLower) $(s.Name)Data;`; - `};`; + @expand(scene_operator_table s) `S_Operator_$(s.Name),`; `};`; +} + +@table_gen +{ + `inline scene_operator S_OperatorFromString(string String)`; + `{`; + `scene_operator Result = S_Operator_Invalid;`; + `if(0) {}`; + @expand(scene_operator_table s) `else if(AreEqual(String, StrLit("$(s.Operator)")))$(=>40) { Result = S_Operator_$(s.Name); }`; + `return(Result);`;; + `}`; } \ No newline at end of file diff --git a/code/vn_tokenizer.cpp b/code/vn_tokenizer.cpp index 50920a7..0f91b15 100644 --- a/code/vn_tokenizer.cpp +++ b/code/vn_tokenizer.cpp @@ -7,12 +7,6 @@ static string T_StringFromToken(string Text, token Token) return(Result); } -static b32 T_TokenMatches(token Token, token_flags Flags, string Text, string String) -{ - b32 Result = (Token.Flags & Flags && AreEqual(T_StringFromToken(Text, Token), String)); - return(Result); -} - static void T_TokenChunkListPush(memory_arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode) { token_chunk_node *Node = List->Last; @@ -58,7 +52,7 @@ static void T_MessageListPush(memory_arena *Arena, tokenizer_message_list *List, //////////////////////////////// //~ sixten: Text -> Token Functions -static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, string Text, token_flags ExcludeFilter) +static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Text, tokenizer_filter_function *ExcludeFilter) { temporary_memory Scratch = GetScratch(&Arena, 1); token_chunk_list Tokens = {}; @@ -70,14 +64,14 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, //- sixten: scan string & produce tokens for(;Byte < TextEnd;) { - token_flags TokenFlags = 0; + token_kind TokenKind = TokenKind_None; u8 *TokenStart = 0; u8 *TokenEnd = 0; //- sixten: whitespace - if(TokenFlags == 0 && (*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) + if(TokenKind == TokenKind_None && (*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) { - TokenFlags = TokenFlag_Whitespace; + TokenKind = TokenKind_Whitespace; TokenStart = Byte; TokenEnd = Byte; Byte += 1; @@ -92,18 +86,18 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, } //- sixten: newlines - if(TokenFlags == 0 && *Byte == '\n') + if(TokenKind == TokenKind_None && *Byte == '\n') { - TokenFlags = TokenFlag_Newline; + TokenKind = TokenKind_Newline; TokenStart = Byte; TokenEnd = Byte + 1; Byte += 1; } //- sixten: single-line comments - if(TokenFlags == 0 && (Byte[0] == '/' && Byte[1] == '/')) + if(TokenKind == TokenKind_None && (Byte[0] == '/' && Byte[1] == '/')) { - TokenFlags = TokenFlag_Comment; + TokenKind = TokenKind_Comment;; TokenStart = Byte; TokenEnd = Byte + 2; Byte += 2; @@ -118,9 +112,9 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, } //- sixten: multi-line comments - if(TokenFlags == 0 && (Byte+1 < TextEnd && Byte[0] == '/' && Byte[1] == '*')) + if(TokenKind == TokenKind_None && (Byte+1 < TextEnd && Byte[0] == '/' && Byte[1] == '*')) { - TokenFlags = TokenFlag_Comment; + TokenKind = TokenKind_Comment; TokenStart = Byte; TokenEnd = Byte + 2; Byte += 2; @@ -129,7 +123,7 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, // sixten(NOTE): This could potentially be wrong. The TokenEnd += 1 statement could currently make the token include the EOF. if(Byte == TextEnd) { - TokenFlags |= TokenFlag_BrokenComment; + TokenKind = TokenKind_BrokenComment; break; } TokenEnd += 1; @@ -143,12 +137,12 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, } //- sixten: identifiers - if(TokenFlags == 0 && (('A' <= *Byte && *Byte <= 'Z') || - ('a' <= *Byte && *Byte <= 'z') || - (UTF8Lengths[*Byte>>3] > 1) || - *Byte == '_')) + if(TokenKind == TokenKind_None && (('A' <= *Byte && *Byte <= 'Z') || + ('a' <= *Byte && *Byte <= 'z') || + (UTF8Lengths[*Byte>>3] > 1) || + *Byte == '_')) { - TokenFlags = TokenFlag_Identifier; + TokenKind = TokenKind_Identifier; TokenStart = Byte; TokenEnd = Byte; Byte += UTF8Lengths[*Byte>>3]; @@ -164,14 +158,28 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, break; } } + + string String = MakeString(TokenStart, TokenEnd-TokenStart); + if(0) {} + else if(AreEqual(String, StrLit("and"))) { TokenKind = TokenKind_And; } + else if(AreEqual(String, StrLit("branch"))) { TokenKind = TokenKind_Branch; } + else if(AreEqual(String, StrLit("else"))) { TokenKind = TokenKind_Else; } + else if(AreEqual(String, StrLit("false"))) { TokenKind = TokenKind_False; } + else if(AreEqual(String, StrLit("for"))) { TokenKind = TokenKind_For; } + else if(AreEqual(String, StrLit("if"))) { TokenKind = TokenKind_If; } + else if(AreEqual(String, StrLit("jump"))) { TokenKind = TokenKind_Jump; } + else if(AreEqual(String, StrLit("or"))) { TokenKind = TokenKind_Or; } + else if(AreEqual(String, StrLit("proc"))) { TokenKind = TokenKind_Proc; } + else if(AreEqual(String, StrLit("true"))) { TokenKind = TokenKind_True; } + else if(AreEqual(String, StrLit("var"))) { TokenKind = TokenKind_Var; } + else if(AreEqual(String, StrLit("while"))) { TokenKind = TokenKind_While; } } //- sixten: numerics - if(TokenFlags == 0 && (('0' <= *Byte && *Byte <= '9') || - *Byte == '.' || - ((*Byte == '+' || *Byte == '-') && Byte + 1 < TextEnd && '0' <= Byte[1] && Byte[1] <= '9'))) + if(TokenKind == TokenKind_None && (('0' <= *Byte && *Byte <= '9') || + (*Byte == '-' && Byte + 1 < TextEnd && '0' <= Byte[1] && Byte[1] <= '9'))) { - TokenFlags = TokenFlag_Numeric; + TokenKind = TokenKind_Numeric; TokenStart = Byte; TokenEnd = Byte; Byte += 1; @@ -179,9 +187,7 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, { TokenEnd += 1; if(Byte == TextEnd || - !(('A' <= *Byte && *Byte <= 'Z') || - ('a' <= *Byte && *Byte <= 'z') || - ('0' <= *Byte && *Byte <= '9') || + !(('0' <= *Byte && *Byte <= '9') || *Byte == '_' || *Byte == '.')) { break; @@ -190,9 +196,9 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, } //- sixten: string literals - if(TokenFlags == 0 && *Byte == '"') + if(TokenKind == TokenKind_None && *Byte == '"') { - TokenFlags = TokenFlag_StringLiteral; + TokenKind = TokenKind_StringLiteral; TokenStart = Byte; TokenEnd = Byte; Byte += 1; @@ -201,7 +207,7 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, TokenEnd += 1; if(Byte == TextEnd || *Byte == '\n') { - TokenFlags |= TokenFlag_BrokenStringLiteral; + TokenKind = TokenKind_BrokenStringLiteral; break; } if(*Byte == '"') @@ -213,64 +219,82 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, } } - //- sixten: multi-char symbols - if(TokenFlags == 0 && (*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' || - *Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' || - *Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' || - *Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\'')) + //- sixten: symbols + if(TokenKind == TokenKind_None && (*Byte == '{' || *Byte == '}' || *Byte == '(' || *Byte == ')' || + *Byte == ',' || *Byte == '.' || *Byte == '@' || *Byte == '#' || + *Byte == ';' || *Byte == '+' || *Byte == '-' || *Byte == '*' || + *Byte == '/')) { - TokenFlags = TokenFlag_Symbol; TokenStart = Byte; - TokenEnd = Byte; - Byte += 1; - for(;Byte <= TextEnd; Byte += 1) + TokenEnd = Byte+1; + + switch(*Byte) { + case '{': { TokenKind = TokenKind_CurlyOpen; } break; + case '}': { TokenKind = TokenKind_CurlyClose; } break; + case '(': { TokenKind = TokenKind_ParenthesisOpen; } break; + case ')': { TokenKind = TokenKind_ParenthesisClose; } break; + case ',': { TokenKind = TokenKind_Comma; } break; + case '.': { TokenKind = TokenKind_Dot; } break; + case '@': { TokenKind = TokenKind_At; } break; + case '#': { TokenKind = TokenKind_PoundSign; } break; + case ';': { TokenKind = TokenKind_Semicolon; } break; + case '+': { TokenKind = TokenKind_Plus; } break; + case '-': { TokenKind = TokenKind_Minus; } break; + case '*': { TokenKind = TokenKind_Star; } break; + case '/': { TokenKind = TokenKind_Slash; } break; + InvalidDefaultCase; + } + + Byte += 1; + } + if(TokenKind == TokenKind_None && (*Byte == '!' || *Byte == '=' || *Byte == '>' || *Byte == '<')) + { + TokenStart = Byte; + TokenEnd = Byte+1; + + switch(*Byte) + { + case '!': { TokenKind = TokenKind_Bang; } break; + case '=': { TokenKind = TokenKind_Equal; } break; + case '>': { TokenKind = TokenKind_Greater; } break; + case '<': { TokenKind = TokenKind_Less; } break; + InvalidDefaultCase; + } + + Byte += 1; + + if(Byte < TextEnd && (*Byte == '=')) + { + TokenKind = (token_kind)(TokenKind + 1); TokenEnd += 1; - if(Byte == TextEnd || - !(*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' || - *Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' || - *Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' || - *Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\'')) - { - break; - } + Byte += 1; } } - //- sixten: single-char symbols - if(TokenFlags == 0 && (*Byte == '{' || *Byte == '}' || *Byte == '(' || *Byte == ')' || - *Byte == '[' || *Byte == ']' || *Byte == ',' || *Byte == ';' || - *Byte == ':' || *Byte == '@' || *Byte == '#')) + //- sixten: bad character + if(TokenKind == TokenKind_None) { - TokenFlags = TokenFlag_Reserved; + TokenKind = TokenKind_BadCharacter; TokenStart = Byte; TokenEnd = Byte + 1; Byte += 1; } - //- sixten: bad character - if(TokenFlags == 0) - { - TokenFlags = TokenFlag_BadCharacter; - TokenStart = Byte; - TokenStart = Byte + 1; - Byte += 1; - } - //- sixten: push token - if(TokenFlags != 0 && !(TokenFlags & ExcludeFilter) && TokenStart != 0 && TokenEnd > TokenStart) + if(TokenKind != 0 && (!ExcludeFilter || (ExcludeFilter && !ExcludeFilter(TokenKind))) && TokenStart != 0 && TokenEnd > TokenStart) { - token Token = {TokenFlags, {TokenStart - TextStart, TokenEnd - TextStart}}; + token Token = {TokenKind, {TokenStart - TextStart, TokenEnd - TextStart}}; T_TokenChunkListPush(Scratch.Arena, &Tokens, Token, 4096); } - if(TokenFlags & TokenFlag_BrokenComment) + if(TokenKind == TokenKind_BrokenComment) { string Message = StrLit("broken comment"); T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); } - if(TokenFlags & TokenFlag_BrokenStringLiteral) + if(TokenKind == TokenKind_BrokenStringLiteral) { string Message = StrLit("broken string literal"); T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); diff --git a/code/vn_tokenizer.h b/code/vn_tokenizer.h index 190e254..b985ea2 100644 --- a/code/vn_tokenizer.h +++ b/code/vn_tokenizer.h @@ -5,44 +5,85 @@ //////////////////////////////// //~ sixten: Token Types -typedef u32 token_flags; -enum +enum token_kind { - TokenFlag_Identifier = (1<<0), - TokenFlag_Numeric = (1<<1), - TokenFlag_StringLiteral = (1<<2), - TokenFlag_Symbol = (1<<3), - TokenFlag_Reserved = (1<<4), - TokenFlag_Comment = (1<<5), - TokenFlag_Whitespace = (1<<6), - TokenFlag_Newline = (1<<7), + TokenKind_None, - TokenFlag_BrokenComment = (1<<8), - TokenFlag_BrokenStringLiteral = (1<<9), - TokenFlag_BadCharacter = (1<<10), + // sixten: labels + TokenKind_Identifier, + TokenKind_Numeric, + TokenKind_StringLiteral, + + // sixten: symbols + TokenKind_SymbolsBegin, + // sixten: (single char) + TokenKind_CurlyOpen, + TokenKind_CurlyClose, + TokenKind_ParenthesisOpen, + TokenKind_ParenthesisClose, + TokenKind_Comma, + TokenKind_Dot, + TokenKind_At, + TokenKind_PoundSign, + TokenKind_Semicolon, + TokenKind_Plus, + TokenKind_Minus, + TokenKind_Star, + TokenKind_Slash, + // sixten: (one or two chars) + TokenKind_Bang, + TokenKind_BangEqual, + TokenKind_Equal, + TokenKind_EqualEqual, + TokenKind_Greater, + TokenKind_GreaterEqual, + TokenKind_Less, + TokenKind_LessEqual, + TokenKind_SymbolsEnd, + + // sixten: keywords + TokenKind_KeywordsBegin, + TokenKind_And, + TokenKind_Branch, + TokenKind_Else, + TokenKind_False, + TokenKind_For, + TokenKind_If, + TokenKind_Jump, + TokenKind_Or, + TokenKind_Proc, + TokenKind_True, + TokenKind_Var, + TokenKind_While, + TokenKind_KeywordsEnd, + + // sixten: whitespace + TokenKind_Whitespace, + TokenKind_Newline, + TokenKind_Comment, + + // sixten: invalid kinds + TokenKind_BrokenComment, + TokenKind_BrokenStringLiteral, + TokenKind_BadCharacter, }; -typedef u32 token_group; -enum -{ - TokenGroup_Comment = TokenFlag_Comment, - TokenGroup_Whitespace = (TokenFlag_Whitespace | - TokenFlag_Newline), - TokenGroup_Irregular = (TokenGroup_Comment | - TokenGroup_Whitespace), - TokenGroup_Regular = ~TokenGroup_Irregular, - TokenGroup_Label = (TokenFlag_Identifier | - TokenFlag_Numeric | - TokenFlag_StringLiteral | - TokenFlag_Symbol), - TokenGroup_Error = (TokenFlag_BrokenComment | - TokenFlag_BrokenStringLiteral | - TokenFlag_BadCharacter), -}; +typedef b32 tokenizer_filter_function(token_kind Kind); + +inline b32 T_IsComment(token_kind Kind) { return(Kind == TokenKind_Comment); } +inline b32 T_IsWhitespace(token_kind Kind) { return(Kind == TokenKind_Whitespace || + Kind == TokenKind_Newline); } +inline b32 T_IsIrregular(token_kind Kind) { return(T_IsComment(Kind) || + T_IsWhitespace(Kind)); } +inline b32 T_IsRegular(token_kind Kind) { return(!T_IsIrregular(Kind)); } +inline b32 T_IsInvalid(token_kind Kind) { return(Kind == TokenKind_None || + Kind == TokenKind_BrokenComment || + Kind == TokenKind_BrokenStringLiteral || + Kind == TokenKind_BadCharacter); } struct token { - token_flags Flags; + token_kind Kind; range1_s64 Range; }; @@ -103,7 +144,6 @@ struct tokenize_result //////////////////////////////// //~ sixten: Token Type Functions static string T_StringFromToken(string Text, token Token); -static b32 T_TokenMatches(token Token, token_flags Flags, string Text, string String); static void T_TokenChunkListPush(memory_arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode); static token_array T_TokenArrayFromChunkList(memory_arena *Arena, token_chunk_list *List); @@ -113,6 +153,6 @@ static void T_MessageListPush(memory_arena *Arena, tokenizer_message_list *List, //////////////////////////////// //~ sixten: Text -> Token Functions -static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, string Text, token_flags ExcludeFilter = 0); +static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Text, tokenizer_filter_function *ExcludeFilter = 0); #endif //VN_TOKENIZER_H diff --git a/code/vn_workspace.cpp b/code/vn_workspace.cpp index 14138dd..442c5b0 100644 --- a/code/vn_workspace.cpp +++ b/code/vn_workspace.cpp @@ -16,6 +16,11 @@ static workspace *Workspace_GetState(void) return(ThreadLocal_Workspace); } +static memory_arena *Workspace_FrameArena(void) +{ + return(ThreadLocal_Workspace->FrameArena); +} + //- sixten: Commands static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument = 0) { @@ -225,14 +230,9 @@ static void Workspace_BuildToolbar() Workspace_CreateNewView(Workspace_View_Startup, CurrentPanel); Workspace->Menu = ToolbarMenu_None; } - if(Workspace_BuildMenuItem(FontIcon_None, "Editor", "").Clicked) + if(Workspace_BuildMenuItem(FontIcon_Pencil, "Script Editor", "Ctrl + O").Clicked) { - Workspace_CreateNewView(Workspace_View_Editor, CurrentPanel); - Workspace->Menu = ToolbarMenu_None; - } - if(Workspace_BuildMenuItem(FontIcon_None, "Command Palette", "").Clicked) - { - Workspace_CreateNewView(Workspace_View_CommandPalette, CurrentPanel); + Workspace_CreateNewView(Workspace_View_TextEditor, CurrentPanel); Workspace->Menu = ToolbarMenu_None; } if(Workspace_BuildMenuItem(FontIcon_Wrench, "Settings", "").Clicked) @@ -755,6 +755,7 @@ static void Workspace_Init(workspace *Workspace) { Workspace_SetState(Workspace); + Workspace->FrameArena = ArenaAllocate(Gigabytes(1)); Workspace->CommandArena = ArenaAllocate(Gigabytes(1)); Workspace->PanelArena = ArenaAllocate(Gigabytes(1)); @@ -764,7 +765,6 @@ static void Workspace_Init(workspace *Workspace) { Workspace_CreateNewView(Workspace_View_Startup, Workspace->RootPanel); } - Workspace_CreateNewView(Workspace_View_TextEditor, Workspace->RootPanel); // sixten: Setup keybinds { @@ -772,6 +772,8 @@ static void Workspace_Init(workspace *Workspace) BIND_COMMAND(Key_P, PlatformModifier_Ctrl, Workspace_Command_SplitPanelHorizontal); BIND_COMMAND(Key_L, PlatformModifier_Ctrl, Workspace_Command_SplitPanelVertical); + BIND_COMMAND(Key_O, PlatformModifier_Ctrl, Workspace_Command_OpenView, Workspace_View_TextEditor); + #if VN_INTERNAL BIND_COMMAND(Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, Workspace_Command_ToggleRenderUIDebugRects); #endif @@ -783,6 +785,7 @@ static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCom vn_input *Input, glyph_atlas *GlyphAtlas) { Workspace_SetState(Workspace); + ArenaClear(Workspace->FrameArena); Workspace->Input = Input; Workspace->EventList = Input->EventList; diff --git a/code/vn_workspace.h b/code/vn_workspace.h index 57c8890..c06f5e8 100644 --- a/code/vn_workspace.h +++ b/code/vn_workspace.h @@ -72,6 +72,9 @@ struct workspace vn_input *Input; platform_event_list *EventList; + // sixten: General Purpose Allocation + memory_arena *FrameArena; + // sixten: Command Allocation memory_arena *CommandArena; workspace_command *FirstFreeCommand; @@ -104,6 +107,7 @@ struct workspace //- sixten: State management static void Workspace_SetState(workspace *Workspace); static workspace *Workspace_GetState(void); +static memory_arena *Workspace_FrameArena(void); //- sixten: Commands static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument); diff --git a/code/vn_workspace_commands.cpp b/code/vn_workspace_commands.cpp index b552e85..7374679 100644 --- a/code/vn_workspace_commands.cpp +++ b/code/vn_workspace_commands.cpp @@ -86,6 +86,12 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel) Workspace_DeletePanel(Panel); } +WORKSPACE_COMMAND(Workspace_Command_OpenView) +{ + workspace *Workspace = Workspace_GetState(); + Workspace_CreateNewView((workspace_view_type)Argument, Workspace->CurrentPanel); +} + #if VN_INTERNAL WORKSPACE_COMMAND(Workspace_Command_ToggleRenderUIDebugRects) { diff --git a/code/vn_workspace_text_editor.cpp b/code/vn_workspace_text_editor.cpp index 6cc050d..38661c9 100644 --- a/code/vn_workspace_text_editor.cpp +++ b/code/vn_workspace_text_editor.cpp @@ -30,7 +30,7 @@ static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceS ArenaPopTo(MutString->Arena, sizeof(memory_arena)+NewCount+1); } - Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-DimOfRange(Range)); + Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-Range.Min-DimOfRange(Range)); Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count); MutString->String.Count = NewCount; @@ -70,7 +70,7 @@ static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *A ArenaClear(Arena); //- sixten: tokenize the text - tokenize_result TokenizeResult = T_TokenizeFromText(Arena, StrLit("*scratch*"), Text); + tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text); token_array Tokens = TokenizeResult.Tokens; //- sixten: gather all line ranges @@ -102,6 +102,13 @@ static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *A return(Result); } +static void Workspace_TextEditorApplyChanges(workspace_view_text_editor *Editor) +{ + workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); + Editor->Tokens = TextData.Tokens; + Editor->Lines = TextData.Lines; +} + static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) { temporary_memory Scratch = GetScratch(); @@ -114,7 +121,7 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) v2 Offset = Box->Parent->Offset; //- sixten: rendering properties - r32 FontSize = 16.0f; + r32 FontSize = Editor->FontSize; r32 LineHeight = FontSize + 4.0f; //- sixten: calculate the dimensions of the glyphs @@ -137,7 +144,7 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) v2_r32 LineMarginDim = V2((LineMarginDigitsRequired)*GlyphAdvance, ParentRect.Max.y - ParentRect.Min.y); //- sixten: tokenize text - tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, StrLit("nobody cares"), Editor->Text.String); + tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Editor->Text.String); token_array Tokens = TokenizeResult.Tokens; token *TokensBegin = Tokens.Tokens; token *TokensEnd = TokensBegin + Tokens.Count; @@ -147,7 +154,7 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) s64 TopMostLine = Min((s64)Floor(-Offset.y / LineHeight), LineCount); for(s64 LinesFound = 0; LinesFound < TopMostLine && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1) { - if(VisibleTokensBegin->Flags & TokenFlag_Newline) + if(VisibleTokensBegin->Kind == TokenKind_Newline) { LinesFound += 1; } @@ -158,7 +165,7 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) s64 LinesOnScreen = Min((s64)Floor(ParentDim.y / LineHeight)+1, LineCount-TopMostLine); for(s64 LinesFound = 0; LinesFound < LinesOnScreen && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1) { - if(VisibleTokensEnd->Flags & TokenFlag_Newline) + if(VisibleTokensEnd->Kind == TokenKind_Newline) { LinesFound += 1; } @@ -197,40 +204,59 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) //- sixten: get color & font from token font_id Font = Font_Monospace; v4 Color = Color_Magenta; - if(Token->Flags & TokenGroup_Comment) { Color = Color_Grey; Font = Font_MonospaceOblique; } - else if(Token->Flags & TokenFlag_Reserved) { Color = Color_Grey; } - else if(Token->Flags & TokenFlag_Symbol) { Color = ColorFromHex(0xbd2d2dff); } - else if(Token->Flags & TokenFlag_StringLiteral) { Color = ColorFromHex(0xffa900ff); } - else if(Token->Flags & TokenFlag_Numeric) { Color = ColorFromHex(0xffa900ff); } - else if(Token->Flags & TokenFlag_Identifier) + if(Token->Kind == TokenKind_Comment) { Color = Color_Grey; Font = Font_MonospaceOblique; } + else if(Token->Kind > TokenKind_SymbolsBegin && Token->Kind < TokenKind_SymbolsEnd) { Color = Color_Grey; } + else if(Token->Kind == TokenKind_StringLiteral) { Color = ColorFromHex(0xffa900ff); } + else if(Token->Kind == TokenKind_Numeric) { Color = ColorFromHex(0xffa900ff); } + else if(Token->Kind > TokenKind_KeywordsBegin && Token->Kind < TokenKind_KeywordsEnd) { - //- sixten: check for keywords - if(AreEqual(TokenString, StrLit("true")) || - AreEqual(TokenString, StrLit("false"))) + if(Token->Kind == TokenKind_True || Token->Kind == TokenKind_False) { Color = ColorFromHex(0xffa900ff); } - else if(AreEqual(TokenString, StrLit("var")) || - AreEqual(TokenString, StrLit("proc")) || - AreEqual(TokenString, StrLit("branch")) || - AreEqual(TokenString, StrLit("jump")) || - AreEqual(TokenString, StrLit("if"))) + else { Color = ColorFromHex(0xf0c674ff); } + } + else if(Token->Kind == TokenKind_Identifier) + { + Color = Theme_TextColor; + } + + //- sixten: render & advance by token + if(!(T_IsWhitespace(Token->Kind))) + { + if(Token->Kind == TokenKind_Comment) + { + //- sixten: advance to newline and push text + // sixten(TODO): proper multiline comment rendering. + u8 *TextBegin = TokenString.Data; + u8 *TextEnd = TextBegin+TokenString.Count; + u8 *Marker = TextBegin; + for(u8 *Byte = TextBegin; Byte <= TextEnd; Byte += 1) + { + if(*Byte == '\n' || Byte == TextEnd) + { + PushText(Group, Atlas, Font, TokenP, FontSize, Color, MakeString(Marker, Byte-Marker)); + Marker = Byte+1; + + if(*Byte == '\n' && Byte != TextEnd) + { + TokenP.x = BaseTokenP.x; + TokenP.y += LineHeight; + } + } + } + } else { - Color = Theme_TextColor; + TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString); } } - //- sixten: render & advance by token - if(!(Token->Flags & TokenGroup_Whitespace)) - { - TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString); - } else { - if(Token->Flags & TokenFlag_Newline) + if(Token->Kind == TokenKind_Newline) { TokenP.x = BaseTokenP.x; TokenP.y += LineHeight; @@ -250,8 +276,8 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) } } + //- sixten: render cursor { - //- sixten: render cursor s64 LineIndex = CursorTextP.Line-1; string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); s64 ColumnIndex = CursorTextP.Column-1; @@ -272,7 +298,6 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line); for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1) { - r32 LineY = Box->Rect.Min.y + LineIndex*LineHeight; if(Contains(LineRange, LineIndex + 1)) { range1_s64 ColumnRange = Lines->Ranges[LineIndex]; @@ -294,9 +319,10 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) range1_s64 ColumnOffsetRange = Range1S64(UTF8OffsetFromIndex(Line, NormalizedColumnRange.Min), UTF8OffsetFromIndex(Line, NormalizedColumnRange.Max)); + r32 LineY = LineIndex*LineHeight; v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C); - range2_r32 LineHighlightBox = Range2R32(V2(LineMarginDim.x+ColumnOffsetRange.Min*GlyphAdvance, LineY), - V2(LineMarginDim.x+ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight)); + range2_r32 LineHighlightBox = Range2R32(Box->Rect.Min+V2(LineMarginDim.x+ColumnOffsetRange.Min*GlyphAdvance, LineY), + Box->Rect.Min+V2(LineMarginDim.x+ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight)); PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0); } } @@ -304,6 +330,190 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) ReleaseScratch(Scratch); } +static b32 Workspace_BuildTextEditorListerItem(string Text, u32 Icon) +{ + b32 Result = false; + UI_SetNextLayoutAxis(Axis2_X); + ui_box *Container = UI_MakeBoxF(UI_BoxFlag_DrawBorder | + UI_BoxFlag_DrawBackground | + UI_BoxFlag_Clickable | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation| + UI_BoxFlag_DrawDropShadow, + "File Lister %S", Text); + UI_Parent(Container) + { + UI_Width(UI_Em(2, 1)) UI_Font(Font_Icons) UI_LabelF("%U", Icon); + UI_Width(UI_TextContent(0, 1)) UI_Label(Text); + } + + ui_signal Signal = UI_SignalFromBox(Container); + if(Signal.Clicked) + { + Result = true; + } + return(Result); +} + +static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(workspace_view *View, workspace_view_text_editor *Editor) +{ + workspace_text_editor_lister_action ListerAction = {}; + temporary_memory Scratch = GetScratch(); + UI_Size(UI_Percent(1, 1), UI_Percent(1, 1)) UI_Scroll(0, &Editor->ListerScroll) + { + UI_Height(UI_Em(2, 1)) + { + //- sixten: filename input field + if(Workspace_ViewIsCurrent(View)) + { + for(platform_event *Event = UI_EventList()->First; Event != 0; Event = Event->Next) + { + if((Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) && (Event->Codepoint != '/' && Event->Codepoint != '\\')) + { + text_action Action = SingleLineTextActionFromEvent(Event); + if(IsValid(&Action)) + { + text_op Op = TextOpFromAction(Scratch.Arena, MakeString(Editor->ListerInput, Editor->ListerInputUsed), &Editor->ListerInputEditState, &Action); + + string Left = MakeString(Editor->ListerInput, Op.Range.Min); + string Right = MakeString(Editor->ListerInput + Op.Range.Max, Editor->ListerInputUsed - Op.Range.Max); + + u64 NewStringSize = Left.Count + Right.Count + Op.ReplaceString.Count; + char *NewString = PushArray(Scratch.Arena, char, NewStringSize); + Copy(NewString, Left.Data, Left.Count); + Copy(NewString + Left.Count, Op.ReplaceString.Data, Op.ReplaceString.Count); + Copy(NewString + Left.Count + Op.ReplaceString.Count, Right.Data, Right.Count); + + Editor->ListerInputUsed = Minimum(ArrayCount(Editor->ListerInput), NewStringSize); + Copy(Editor->ListerInput, NewString, Editor->ListerInputUsed); + + Editor->ListerInputEditState.Cursor = Minimum(Op.NewCursor, Editor->ListerInputUsed); + Editor->ListerInputEditState.Mark = Minimum(Op.NewMark, Editor->ListerInputUsed); + } + } + } + } + + //- sixten: build navbar + UI_Row(UI_BoxFlag_DrawBorder) + { + UI_SetNextBackgroundColor(ColorFromHex(0x2D5790FF)); + UI_SetNextWidth(UI_TextContent(20, 1)); + UI_MakeBox(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawText, Editor->Path); + + UI_SetNextWidth(UI_TextContent(15, 1)); + UI_Label(MakeString(Editor->ListerInput, Editor->ListerInputUsed)); + + UI_Padding(UI_Percent(1, 0)); + + UI_SetNextWidth(UI_TextContent(20, 1)); + if(UI_ButtonF("Open/Create").Clicked || Platform_KeyPress(UI_EventList(), Key_Return)) + { + ListerAction.HasRequestedFile = true; + ListerAction.Name = MakeString(Editor->ListerInput, Editor->ListerInputUsed); + ListerAction.Path = Editor->Path; + } + } + + //- sixten: display "parent directory button" + s64 LastSlash = LastIndexOf(Editor->Path, '/'); + if(LastSlash != -1) + { + if(Workspace_BuildTextEditorListerItem(StrLit("Parent Directory"), FontIcon_Reply)) + { + Editor->Path = Prefix(Editor->Path, LastSlash); + Editor->ListerInputUsed = 0; + } + } + platform_file_info FileInfo; + platform_file_iter *FileIter; + + string Name = MakeString(Editor->ListerInput, Editor->ListerInputUsed); + string FullPath = PushFormat(Scratch.Arena, "%S/%S", Editor->Path, Name); + + //- sixten: display directories + { + FileIter = Platform.BeginFileIter(Scratch.Arena, FullPath); + for(;Platform.AdvanceFileIter(Scratch.Arena, FileIter, &FileInfo);) + { + if(FileInfo.IsDirectory) + { + if(Workspace_BuildTextEditorListerItem(FileInfo.Name, FontIcon_Folder)) + { + Editor->Path = PushFormat(View->Arena, "%S/%S", Editor->Path, FileInfo.Name); + Editor->ListerInputUsed = 0; + } + } + } + } + Platform.EndFileIter(FileIter); + + //- sixten: display files + { + FileIter = Platform.BeginFileIter(Scratch.Arena, FullPath); + for(;Platform.AdvanceFileIter(Scratch.Arena, FileIter, &FileInfo);) + { + if(!FileInfo.IsDirectory) + { + if(Workspace_BuildTextEditorListerItem(FileInfo.Name, FontIcon_Document)) + { + ListerAction.HasRequestedFile = true; + ListerAction.Name = PushString(View->Arena, FileInfo.Name); + ListerAction.Path = Editor->Path; + } + } + } + } + Platform.EndFileIter(FileIter); + } + } + ReleaseScratch(Scratch); + return(ListerAction); +} + +static b32 Workspace_ProcessTextEditorEvent(workspace_view_text_editor *Editor, platform_event *Event) +{ + b32 CursorHasBeenModified = false; + temporary_memory Scratch = GetScratch(); + text_action Action = MultiLineTextActionFromEvent(Event); + if(IsValid(&Action)) + { + text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1); + + if(DimOfRange(Op.Range) != 0 || !AreEqual(StrLit(""), Op.ReplaceString)) + { + //- sixten: append to the history + { + history_list *List = &Editor->History; + + //- sixten: remove the pre-existing history if needed + if(List->Sentinel.Prev != List->At) + { + // sixten(TODO): instead of just removing the links to the old memory, find some way to manage it. + List->Sentinel.Prev->Next = List->At; + List->Sentinel.Prev = List->At; + } + + range1_s64 Selection = Range1S64(Editor->EditState.Cursor, Editor->EditState.Mark); + AppendToHistory(Editor->HistoryArena, List, + HistoryEntry(Editor->HistoryArena, Op.ReplaceString, Op.Range), + HistoryEntry(Editor->HistoryArena, Substring(Editor->Text.String, Op.Range), Range1S64(Op.Range.Min, Op.Range.Min+Op.ReplaceString.Count))); + + List->At = List->Sentinel.Prev; + } + //- sixten: apply the text action + MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range); + Workspace_TextEditorApplyChanges(Editor); + } + + CursorHasBeenModified = true; + Editor->EditState.Cursor = Op.NewCursor; + Editor->EditState.Mark = Op.NewMark; + } + ReleaseScratch(Scratch); + return(CursorHasBeenModified); +} + static void Workspace_BuildTextEditor(workspace_view *View) { workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; @@ -311,7 +521,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) temporary_memory Scratch = GetScratch(); //- sixten: rendering properties - r32 FontSize = 16.0f; + r32 FontSize = Editor->FontSize = 14.0f; r32 LineHeight = FontSize + 4.0f; //- sixten: calculate the dimensions of the glyphs @@ -322,129 +532,197 @@ static void Workspace_BuildTextEditor(workspace_view *View) s32 LineMarginDigitsRequired = 6; r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance; - ui_box *EditorBox = 0; - UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); - UI_Scroll(0, &Editor->Offset.y) + + b32 InFileListMode = AreEqual(Editor->FileName, StrLit("")); + if(InFileListMode) { - //- sixten: find the container box for the scrollable region - Editor->ContainerBox = UI_TopParent()->Parent->Parent; - - UI_SetNextSize(UI_Pixels(Editor->TextDim.x, 1), UI_Pixels(Editor->TextDim.y, 1)); - EditorBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, "Workspace Text Editor %p", View); - EditorBox->DrawCallback = Workspace_TextEditorDrawCallback; - EditorBox->DrawCallbackData = Editor; + //- sixten: build & handle file lister + workspace_text_editor_lister_action Action = Workspace_BuildTextEditorLister(View, Editor); + if(Action.HasRequestedFile) + { + //- sixten: try to load file + string FullPath = PushFormat(Scratch.Arena, "%S/%S", Action.Path, Action.Name); + platform_file_handle File = Platform.OpenFile(FullPath, PlatformAccess_Read); + + if(File.IsValid) + { + s64 FileSize = Platform.GetFileSize(File); + string ReplaceString = MakeString(PushArray(Scratch.Arena, u8, FileSize+1), FileSize); + Platform.ReadFile(File, ReplaceString.Data, 0, ReplaceString.Count); + + ReplaceString = RemoveAll(Scratch.Arena, ReplaceString, '\r'); + + MutableStringReplaceRange(&Editor->Text, ReplaceString, Range1S64(0, 0)); + Workspace_TextEditorApplyChanges(Editor); + Editor->FileName = Action.Name; + Editor->FilePath = Action.Path; + + Platform.CloseFile(File); + } + else + { + Editor->FileName = Action.Name; + Editor->FilePath = Action.Path; + } + } } - - b32 CursorHasBeenModified = false; - - if(Workspace_ViewIsCurrent(View)) + else { - //- sixten: handle history + //- sixten: build & handle the text editor + ui_box *EditorBox = 0; + UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); + UI_Scroll(0, &Editor->Offset.y) { - history_list *List = &Editor->History; + //- sixten: find the container box for the scrollable region + Editor->ContainerBox = UI_TopParent()->Parent->Parent; - //- sixten: undo - if(Platform_KeyPress(UI_EventList(), Key_Z, PlatformModifier_Ctrl)) - { - history_node *Node = List->At; - if(Node != &List->Sentinel) - { - //- sixten: get entry & apply - history_entry Entry = Node->Backward; - MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); - workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); - Editor->Tokens = TextData.Tokens; - Editor->Lines = TextData.Lines; - Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; - CursorHasBeenModified = true; - - List->At = Node->Prev; - } - } - - //- sixten: redo - if(Platform_KeyPress(UI_EventList(), Key_Y, PlatformModifier_Ctrl)) - { - history_node *Node = List->At->Next; - if(Node != &List->Sentinel) - { - //- sixten: get entry & apply - history_entry Entry = Node->Forward; - MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); - workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); - Editor->Tokens = TextData.Tokens; - Editor->Lines = TextData.Lines; - Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; - CursorHasBeenModified = true; - - List->At = Node; - } - } + UI_SetNextSize(UI_Pixels(Editor->TextDim.x, 1), UI_Pixels(Editor->TextDim.y, 1)); + EditorBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, "Workspace Text Editor %p", View); + EditorBox->DrawCallback = Workspace_TextEditorDrawCallback; + EditorBox->DrawCallbackData = Editor; } - //- sixten: select all - if(Platform_KeyPress(UI_EventList(), Key_A, PlatformModifier_Ctrl)) - { - Editor->EditState.Mark = 0; - Editor->EditState.Cursor = Editor->Text.String.Count; - } + b32 CursorHasBeenModified = false; - //- sixten: keyboard input -> text op - for(platform_event *Event = UI_EventList()->First; - Event != 0; - Event = Event->Next) + if(Workspace_ViewIsCurrent(View)) { - if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) + //- sixten: handle history { - text_action Action = MultiLineTextActionFromEvent(Event); - if(IsValid(&Action)) + history_list *List = &Editor->History; + + //- sixten: undo + if(Platform_KeyPress(UI_EventList(), Key_Z, PlatformModifier_Ctrl)) { - text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1); - - if(DimOfRange(Op.Range) != 0 || !AreEqual(StrLit(""), Op.ReplaceString)) + history_node *Node = List->At; + if(Node != &List->Sentinel) { - //- sixten: append to the history + //- sixten: get entry & apply + history_entry Entry = Node->Backward; + MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); + Workspace_TextEditorApplyChanges(Editor); + Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; + CursorHasBeenModified = true; + + List->At = Node->Prev; + } + } + + //- sixten: redo + if(Platform_KeyPress(UI_EventList(), Key_Y, PlatformModifier_Ctrl)) + { + history_node *Node = List->At->Next; + if(Node != &List->Sentinel) + { + //- sixten: get entry & apply + history_entry Entry = Node->Forward; + MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); + Workspace_TextEditorApplyChanges(Editor); + Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; + CursorHasBeenModified = true; + + List->At = Node; + } + } + } + + //- sixten: select all + if(Platform_KeyPress(UI_EventList(), Key_A, PlatformModifier_Ctrl)) + { + Editor->EditState.Mark = 0; + Editor->EditState.Cursor = Editor->Text.String.Count; + } + + //- sixten: keyboard input -> text op + for(platform_event *Event = UI_EventList()->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) + { + //- sixten: auto-indent + s64 Indent = 0; + if(Event->Codepoint == '\n') + { + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[Editor->LastTextPoint.Line-1]); + if(Line.Data[Line.Count-1] == '\n') { - history_list *List = &Editor->History; - - //- sixten: remove the pre-existing history if needed - if(List->Sentinel.Prev != List->At) - { - // sixten(TODO): instead of just removing the links to the old memory, find some way to manage it. - List->Sentinel.Prev->Next = List->At; - List->Sentinel.Prev = List->At; - } - - range1_s64 Selection = Range1S64(Editor->EditState.Cursor, Editor->EditState.Mark); - AppendToHistory(Editor->HistoryArena, List, - HistoryEntry(Editor->HistoryArena, Op.ReplaceString, Op.Range), - HistoryEntry(Editor->HistoryArena, Substring(Editor->Text.String, Op.Range), Range1S64(Op.Range.Min, Op.Range.Min+Op.ReplaceString.Count))); - - List->At = List->Sentinel.Prev; + Line.Count -= 1; + } + + for(u8 *Data = Line.Data; *Data == '\t' && Data < Line.Data+Line.Count; Data += 1) + { + Indent += 1; } - //- sixten: apply the text action - MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range); - workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); - Editor->Tokens = TextData.Tokens; - Editor->Lines = TextData.Lines; } - CursorHasBeenModified = true; - Editor->EditState.Cursor = Op.NewCursor; - Editor->EditState.Mark = Op.NewMark; + if(Event->Codepoint == '{') + { + platform_event FakeEvent = {}; + FakeEvent.Codepoint = '}'; + FakeEvent.Type = PlatformEvent_Text; + CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, &FakeEvent); + FakeEvent.Key = Key_Left;; + FakeEvent.Type = PlatformEvent_Press; + CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, &FakeEvent); + } + + CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, Event); + + //- sixten: apply indent + { + platform_event FakeTab = {}; + FakeTab.Codepoint = '\t'; + FakeTab.Type = PlatformEvent_Text; + + for(s64 IndentIndex = 0; IndentIndex < Indent; IndentIndex += 1) + { + CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, &FakeTab); + } + } } } } - } - - ui_signal Signal = UI_SignalFromBox(EditorBox); - if(Signal.Dragging) - { - if(Signal.Pressed) + + //- sixten: right-click dropdown + { + if(Editor->DropdownActive) + { + UI_Tooltip + { + UI_SetNextFixedP(Editor->DropdownP); + UI_SetNextWidth(UI_Em(20, 1)); + UI_SetNextHeight(UI_ChildrenSum(AnimationCurve_AnimateValueDirect(1, 0.2, &Editor->DropdownTransition), 1)); + UI_SetNextCornerRadius(4); + UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawDropShadow | + UI_BoxFlag_Clip | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY, StrLit("Text Editor Dropdown"))) + UI_Width(UI_Percent(1, 1)) UI_Height(UI_Em(1.66, 1)) + UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) + UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) + UI_CornerRadius(2) + { + UI_ButtonF("Hello"); + UI_ButtonF("Line"); + UI_ButtonF("Paint"); + UI_ButtonF("Color"); + UI_ButtonF("Design"); + UI_ButtonF("Address"); + UI_ButtonF("Brightness"); + } + } + } + } + + ui_signal Signal = UI_SignalFromBox(EditorBox); + + if(Signal.Pressed || (Signal.PressedRight && (Editor->EditState.Cursor == Editor->EditState.Mark))) { //- sixten: translate mouse position to text point v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); - s64 LineIndex = (s64)(MouseOffset.y / LineHeight); + s64 LineIndex = Clamp((s64)(MouseOffset.y / LineHeight), 0, Editor->Lines.Count); + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); @@ -453,33 +731,44 @@ static void Workspace_BuildTextEditor(workspace_view *View) Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); } - //- sixten: translate mouse position to text point - v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); - s64 LineIndex = (s64)(MouseOffset.y / LineHeight); - string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); - s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); - s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); - - text_point Point = {LineIndex + 1, ColumnIndex + 1}; - Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); - - CursorHasBeenModified = true; - } - - //- sixten: update eventual text point extents - if(CursorHasBeenModified) - { - text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); - if(Editor->LastTextPoint.Line == Point.Line) + if(Signal.Dragging) { - Editor->LastTextPoint = Point; + //- sixten: translate mouse position to text point + v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); + s64 LineIndex = Clamp((s64)(MouseOffset.y / LineHeight), 0, Editor->Lines.Count); + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); + s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); + s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); + + text_point Point = {LineIndex + 1, ColumnIndex + 1}; + Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); + + CursorHasBeenModified = true; + + Editor->DropdownActive = false; } - else + + if(Signal.PressedRight) { - Editor->LastTextPoint.Line = Point.Line; - Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column); + Editor->DropdownActive = true; + Editor->DropdownP = UI_MouseP(); + Editor->DropdownTransition = 0; + } + + //- sixten: update eventual text point extents + if(CursorHasBeenModified) + { + text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); + if(Editor->LastTextPoint.Line == Point.Line) + { + Editor->LastTextPoint = Point; + } + else + { + Editor->LastTextPoint.Line = Point.Line; + Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column); + } } } - ReleaseScratch(Scratch); } \ No newline at end of file diff --git a/code/vn_workspace_text_editor.h b/code/vn_workspace_text_editor.h index 3f49b9d..0150fed 100644 --- a/code/vn_workspace_text_editor.h +++ b/code/vn_workspace_text_editor.h @@ -44,6 +44,13 @@ struct workspace_text_data range1_s64_array Lines; }; +struct workspace_text_editor_lister_action +{ + b32 HasRequestedFile; + string Path; + string Name; +}; + struct workspace_view_text_editor { // sixten: processed text @@ -52,12 +59,17 @@ struct workspace_view_text_editor range1_s64_array Lines; // sixten: text being edited + string FileName; + string FilePath; mutable_string Text; // sixten: text editing text_edit_state EditState; text_point LastTextPoint; + // sixten: text rendering + r32 FontSize; + // sixten: history memory_arena *HistoryArena; history_list History; @@ -66,6 +78,16 @@ struct workspace_view_text_editor ui_box *ContainerBox; v2 TextDim; v2 Offset; + b32 DropdownActive; + v2 DropdownP; + r32 DropdownTransition; + + // sixten: file lister + string Path; + r32 ListerScroll; + u8 ListerInput[256]; + s32 ListerInputUsed; + text_edit_state ListerInputEditState; }; //////////////////////////////// diff --git a/code/vn_workspace_view.cpp b/code/vn_workspace_view.cpp index a9ff068..dbfcef4 100644 --- a/code/vn_workspace_view.cpp +++ b/code/vn_workspace_view.cpp @@ -85,6 +85,25 @@ inline string Workspace_GetViewName(workspace_view *View) case Workspace_View_Startup: { Result = StrLit("Welcome"); } break; case Workspace_View_Editor: { Result = StrLit("Editor"); } break; case Workspace_View_Settings: { Result = StrLit("Settings"); } break; + case Workspace_View_TextEditor: + { + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + if(AreEqual(Editor->FileName, StrLit(""))) + { + Result = StrLit("Open File"); + } + else + { + if(Editor->History.At == &Editor->History.Sentinel) + { + Result = PushString(Workspace_FrameArena(), Editor->FileName); + } + else + { + Result = PushFormat(Workspace_FrameArena(), "* %S", Editor->FileName); + } + } + } break; } return(Result); diff --git a/code/win32__main.rdbg b/code/win32__main.rdbg index 714c650..0b64cb9 100644 Binary files a/code/win32__main.rdbg and b/code/win32__main.rdbg differ diff --git a/code/win32_main.cpp b/code/win32_main.cpp index 456e955..aefa326 100644 --- a/code/win32_main.cpp +++ b/code/win32_main.cpp @@ -167,6 +167,73 @@ static PLATFORM_SET_CURSOR(Win32_SetCursor) Global_Win32State.Cursor = Cursor; } +static PLATFORM_BEGIN_FILE_ITER(Win32_BeginFileIter) +{ + win32_file_find_data *FileFindData = PushStruct(Arena, win32_file_find_data); + temporary_memory Scratch = GetScratch(&Arena, 1); + Path = PushFormat(Scratch.Arena, "%S/%S*", Global_Win32State.ContentsPath, Path); + string16 Path16 = String16FromString8(Scratch.Arena, Path); + FileFindData->Handle = FindFirstFileW((WCHAR *)Path16.Data, &FileFindData->FindData); + ReleaseScratch(Scratch); + platform_file_iter *Iter = (platform_file_iter *)FileFindData; + return(Iter); +} + +static PLATFORM_ADVANCE_FILE_ITER(Win32_AdvanceFileIter) +{ + b32 Result = false; + win32_file_find_data *FileFindData = (win32_file_find_data *)Iter; + WIN32_FIND_DATAW FindData = {}; + s64 InvalidCount = 0; + for(;;) + { + if(FileFindData->FindFirstReturned) + { + Result = FindNextFileW(FileFindData->Handle, &FindData); + } + else + { + Result = (FileFindData->Handle != 0 && FileFindData->Handle != INVALID_HANDLE_VALUE); + FindData = FileFindData->FindData; + FileFindData->FindFirstReturned = true; + } + + b32 NameIsInvalid = (FindData.cFileName[0] == '.' && (FindData.cFileName[1] == 0 || FindData.cFileName[1] == '.')); + if(NameIsInvalid && InvalidCount < 10) + { + InvalidCount += 1; + continue; + } + + break; + } + + if(Result != 0) + { + string16 Name16 = {}; + Name16.Data = (u16 *)FindData.cFileName; + for(u16 Index = 0; Index < MAX_PATH; Index += 1) + { + if(FindData.cFileName[Index] == 0) + { + break; + } + Name16.Count += 1; + } + + *OutInfo = {}; + OutInfo->Name = String8FromString16(Arena, Name16); + OutInfo->IsDirectory = FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY; + } + return(Result); +} + +static PLATFORM_END_FILE_ITER(Win32_EndFileIter) +{ + win32_file_find_data *FileFindData = (win32_file_find_data *)Iter; + FindClose(FileFindData->Handle); +} + inline u64 Win32_GetWallClock(void) { LARGE_INTEGER Query; @@ -440,6 +507,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP else if(VKCode == VK_UP) { Key = Key_Up; } else if(VKCode == VK_DOWN) { Key = Key_Down; } else if(VKCode == VK_SPACE) { Key = Key_Space; } + else if(VKCode == VK_RETURN) { Key = Key_Return; } else if(VKCode == VK_PRIOR) { Key = Key_PageUp; } else if(VKCode == VK_NEXT) { Key = Key_PageDown; } else if(VKCode == VK_HOME) { Key = Key_Home; } diff --git a/code/win32_main.h b/code/win32_main.h index 4fdd9a2..fb643aa 100644 --- a/code/win32_main.h +++ b/code/win32_main.h @@ -22,6 +22,13 @@ struct win32_state platform_cursor Cursor; }; +struct win32_file_find_data +{ + HANDLE Handle; + b32 FindFirstReturned; + WIN32_FIND_DATAW FindData; +}; + struct win32_loaded_code { HMODULE DLL; diff --git a/data/compiler_test.vns b/data/compiler_test.vns new file mode 100644 index 0000000..2045487 --- /dev/null +++ b/data/compiler_test.vns @@ -0,0 +1,5 @@ +proc main +{ + "Hello!"; + "Scene test 123" #noclear; +} \ No newline at end of file diff --git a/data/first.vns b/data/first.vns new file mode 100644 index 0000000..493795e --- /dev/null +++ b/data/first.vns @@ -0,0 +1,7 @@ +proc main +{ + for(var idx = 0; idx < 10; idx = idx + 1) + { + print("hello, line, paint, color, design, address, brightness"); + } +} \ No newline at end of file diff --git a/data/test.vns b/data/test.vns index 4086dd9..b4e75ef 100644 --- a/data/test.vns +++ b/data/test.vns @@ -1,31 +1,31 @@ // This just experiments with the scripting language -var times = 0 +var times = 0; proc "Start" { - "so, I actually changed my mind." - "the editor will not be node based" - "I realised that it would just be slower to write dialog that way soooo..." - "instead, I present to you the.........." - "vn scene - scripting language" + "so, I actually changed my mind."; + "the editor will not be node based"; + "I realised that it would just be slower to write dialog that way soooo..."; + "instead, I present to you the.........."; + "vn scene - scripting language"; - "btw something happens if you go through this dialog 10 times" + "btw something happens if you go through this dialog 10 times"; - times += 1 + times += 1; branch { "Return to start" { - jump "Start" + jump "Start"; } if(times >= 10) { "SUPER EPIC SECRET" { - jump "Epic Scene" + jump "Epic Scene"; } } } @@ -33,9 +33,9 @@ proc "Start" proc "Epic Scene" { - "woah... so epic" - @s "oh, right. almost forgot to mention that you can talk as different characters." - @s "you know... " - wait - @s #noclear "the usual" + "woah... so epic"; + @s "oh, right. almost forgot to mention that you can talk as different characters."; + @s "you know... "; + wait; + @s #noclear "the usual"; } \ No newline at end of file diff --git a/data/vns.txt b/data/vns.txt new file mode 100644 index 0000000..ec4a14c --- /dev/null +++ b/data/vns.txt @@ -0,0 +1,37 @@ +program -> declaration* EOF; + +declaration -> proc_decl | + var_decl | + statement; + +proc_decl -> "proc" IDENTIFIER block; +var_decl -> "var" IDENTIFIER ("=" expression)? ";"; + +statement -> expression_statement | + for_statement | + if_statement | + block; + +expression_statement -> expression ";"; +for_statement -> "for" "(" (var_decl|expression_statement|";") + expression? ";" expression? ")" statement; +if_statement -> "if" "(" expression ")" statement + ("else" statement)?; +block -> "{" declaration* "}"; + +expression -> assignment; + +assignment -> (call ".")? IDENTIFIER "=" assignment | + logic_or; + +logic_or -> logic_and ("or" logic_and)*; +logic_and -> equality ("and" equality)*; +equality -> comparison (("!=" | "==") comparison)*; +comparison -> term ((">" | ">=" < "<" | "<=") term)*; +term -> factor (("-" | "+") factor)*; +factor -> unary (("/" | "*") unary)*; + +unary -> ("!" | "-") unary | call; +call -> primary "(" arguments? ")"; +primary -> "true" | "false" | NUMBER | STRING | + IDENTIFIER | "(" expression ")"; diff --git a/fonts/icons.ttf b/fonts/icons.ttf index c567a1e..7d17c70 100644 Binary files a/fonts/icons.ttf and b/fonts/icons.ttf differ