Added scene bytecode generation & file navigation.

main
sixtenhugosson 2023-08-06 12:35:09 +02:00
parent 30e384e0a4
commit a3e2314fd0
34 changed files with 1856 additions and 499 deletions

0
brightness.txt 100644
View File

View File

@ -6,6 +6,7 @@
#include <stdint.h>
#include <intrin.h>
#include <stdarg.h>
#include <stdlib.h>
//- 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

View File

@ -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)};

View File

@ -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);

View File

@ -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));

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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;\

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -55,6 +55,11 @@ enum font_id
#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

View File

@ -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,

View File

@ -12,6 +12,9 @@
{ 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

View File

@ -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;
switch(*Data)
{
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;
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: 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")))
{
Context.Token += 1;
goto TokenConsumed;
}
//- 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;
//- sixten: find required bytes
s64 RequiredBytes = 0;
scene_annotated_bytecode_chunk *NextChunk = 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);
scene_parse_result Result =
{
0
};
return(Result);
}

View File

@ -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

View File

@ -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);`;;
`}`;
}

View File

@ -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') ||
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)
{
TokenEnd += 1;
if(Byte == TextEnd ||
!(*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' ||
*Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' ||
*Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' ||
*Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\''))
{
break;
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;
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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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
}
else if(Token->Kind == TokenKind_Identifier)
{
Color = Theme_TextColor;
}
}
//- sixten: render & advance by token
if(!(Token->Flags & TokenGroup_Whitespace))
if(!(T_IsWhitespace(Token->Kind)))
{
TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString);
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
{
if(Token->Flags & TokenFlag_Newline)
TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString);
}
}
else
{
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
{
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,6 +532,43 @@ static void Workspace_BuildTextEditor(workspace_view *View)
s32 LineMarginDigitsRequired = 6;
r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance;
b32 InFileListMode = AreEqual(Editor->FileName, StrLit(""));
if(InFileListMode)
{
//- 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;
}
}
}
else
{
//- 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)
@ -352,9 +599,7 @@ static void Workspace_BuildTextEditor(workspace_view *View)
//- 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;
Workspace_TextEditorApplyChanges(Editor);
Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count;
CursorHasBeenModified = true;
@ -371,9 +616,7 @@ static void Workspace_BuildTextEditor(workspace_view *View)
//- 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;
Workspace_TextEditorApplyChanges(Editor);
Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count;
CursorHasBeenModified = true;
@ -396,55 +639,90 @@ static void Workspace_BuildTextEditor(workspace_view *View)
{
if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text)
{
text_action Action = MultiLineTextActionFromEvent(Event);
if(IsValid(&Action))
//- sixten: auto-indent
s64 Indent = 0;
if(Event->Codepoint == '\n')
{
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))
string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[Editor->LastTextPoint.Line-1]);
if(Line.Data[Line.Count-1] == '\n')
{
//- 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;
Line.Count -= 1;
}
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;
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);
}
}
}
}
}
//- 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.Dragging)
{
if(Signal.Pressed)
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,9 +731,11 @@ static void Workspace_BuildTextEditor(workspace_view *View)
Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
}
if(Signal.Dragging)
{
//- 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);
@ -464,6 +744,15 @@ static void Workspace_BuildTextEditor(workspace_view *View)
Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
CursorHasBeenModified = true;
Editor->DropdownActive = false;
}
if(Signal.PressedRight)
{
Editor->DropdownActive = true;
Editor->DropdownP = UI_MouseP();
Editor->DropdownTransition = 0;
}
//- sixten: update eventual text point extents
@ -480,6 +769,6 @@ static void Workspace_BuildTextEditor(workspace_view *View)
Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column);
}
}
}
ReleaseScratch(Scratch);
}

View File

@ -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;
};
////////////////////////////////

View File

@ -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);

Binary file not shown.

View File

@ -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; }

View File

@ -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;

View File

@ -0,0 +1,5 @@
proc main
{
"Hello!";
"Scene test 123" #noclear;
}

7
data/first.vns 100644
View File

@ -0,0 +1,7 @@
proc main
{
for(var idx = 0; idx < 10; idx = idx + 1)
{
print("hello, line, paint, color, design, address, brightness");
}
}

View File

@ -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";
}

37
data/vns.txt 100644
View File

@ -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 ")";

Binary file not shown.