vn/code/vn_scene.cpp

1034 lines
38 KiB
C++

#include "generated/vn_scene.meta.h"
#include "generated/vn_scene.meta.c"
////////////////////////////////
//~ sixten: Scene Compiler Functions
static void S_ParseError(scene_compiler *Compiler, char *Message, s64 TokenOffset)
{
Compiler->EncounteredError = true;
Compiler->InPanicMode = true;
scene_compile_error *Error = PushStruct(Compiler->Arena, scene_compile_error);
Error->Message = PushFormat(Compiler->Arena, Message);
Error->Token = Compiler->At[TokenOffset];
QueuePush(Compiler->Errors.First, Compiler->Errors.Last, Error);
Compiler->Errors.Count += 1;
}
static void S_EmitByte(scene_compiler *Compiler, u8 Byte)
{
scene_emission_target *Target = &Compiler->TargetStack[Compiler->TargetStackIndex];
scene_annotated_bytecode_bucket *Bucket = Target->Bucket;
scene_annotated_bytecode_chunk *Chunk = Bucket->Last;
if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Data) || (Target->Type == S_EmissionTarget_Named && !AreEqual(Chunk->Name, Target->Name)))
{
Chunk = PushStruct(Target->Arena, scene_annotated_bytecode_chunk);
Chunk->Name = Target->Name;
QueuePush(Bucket->First, Bucket->Last, Chunk);
Bucket->Count += 1;
}
Chunk->Data[Chunk->Count] = Byte;
Chunk->Count += 1;
}
static void S_EmitBucket(scene_compiler *Compiler, scene_annotated_bytecode_bucket *Bucket)
{
for(scene_annotated_bytecode_chunk *Chunk = Bucket->First; Chunk != 0; Chunk = Chunk->Next)
{
for(s64 Index = 0; Index < Chunk->Count; Index += 1)
{
S_EmitByte(Compiler, Chunk->Data[Index]);
}
}
}
static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value)
{
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);
}
static s64 S_EmitVariableLength(scene_compiler *Compiler, u64 Value)
{
s64 Length = 1;
u64 Index = Value;
for(;Index > 0x7F; Index >>= 7)
{
S_EmitByte(Compiler, Index|0x80);
Length += 1;
InvalidCodepath;
}
S_EmitByte(Compiler, Index);
return(Length);
}
static scene_variable_read S_ReadVariableLength(u8 *Byte)
{
scene_variable_read Result = {};
u8 *StartByte = Byte;
for(;*Byte & 0x80; Byte += 1)
{
Result.Value = (Result.Value<<7)|(*Byte & 0x7F);
}
Result.Value = (Result.Value<<7)|(*Byte & 0x7F);
Result.Size = Byte-StartByte+1;
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, scene_emission_target Target)
{
Compiler->TargetStack[Compiler->TargetStackIndex] = Target;
}
static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target)
{
Compiler->TargetStackIndex += 1;
Compiler->TargetStack[Compiler->TargetStackIndex] = Target;
}
static void S_PopEmissionTarget(scene_compiler *Compiler)
{
Compiler->TargetStackIndex -= 1;
}
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).", 0);
Compiler->At += 1;
}
}
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.");
if(!Compiler->InPanicMode)
{
string Name = T_StringFromToken(Compiler->Text, NameToken);
scene_annotated_bytecode_bucket *Bucket = &Compiler->ProcBuckets[HashString(Name)%ArrayCount(Compiler->ProcBuckets)];
S_SetEmissionTarget(Compiler, S_NamedEmissionTarget(Compiler->Arena, Bucket, Name));
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;
case TokenKind_Jump:
{
Compiler->At += 1;
S_ParseJumpStatement(Compiler);
} break;
case TokenKind_Branch:
{
Compiler->At += 1;
S_ParseBranchStatement(Compiler);
} break;
default:
{
S_ParseStatement(Compiler);
} break;
}
if(Compiler->InPanicMode)
{
for(;Compiler->At < Compiler->TokensEnd;)
{
if(Compiler->At[0].Kind == TokenKind_Semicolon)
{
goto End;
}
switch(Compiler->At[-1].Kind)
{
case TokenKind_Var: goto End;
case TokenKind_StringLiteral: goto End;
case TokenKind_Jump: goto End;
case TokenKind_Branch: goto End;
}
Compiler->At += 1;
}
End:
Compiler->InPanicMode = false;
}
}
static void S_ParseVariableDeclaration(scene_compiler *Compiler)
{
S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected variable name.");
u64 NameConstant = S_MakeConstant(Compiler, S_MakeSourceRef(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_MakeSourceRef(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_MakeSourceRef(LineToken)));
if(EmitAwait)
{
S_EmitByte(Compiler, S_Op_AwaitInput);
}
}
static void S_ParseJumpStatement(scene_compiler *Compiler)
{
S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected jump destination.");
token DestToken = Compiler->At[-1];
S_EmitByte(Compiler, S_Op_Jump);
S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(DestToken)));
S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after jump statement.");
}
static void S_ParseBranchStatement(scene_compiler *Compiler)
{
temporary_memory Scratch = GetScratch();
scene_branch_case *FirstBranch = 0, *LastBranch = 0;
S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after 'branch'.");
for(;Compiler->At < Compiler->TokensEnd;)
{
if(Compiler->At[0].Kind == TokenKind_CurlyClose)
{
Compiler->At += 1;
break;
}
else
{
//- sixten: parse branch declarations
switch(Compiler->At[0].Kind)
{
case TokenKind_StringLiteral:
{
scene_branch_case *Branch = S_ParseBranchCase(Compiler, Scratch.Arena);
QueuePush(FirstBranch, LastBranch, Branch);
} break;
default:
{
S_ParseError(Compiler, "Expected branch case.");
Compiler->At += 1;
} break;
}
}
}
//- sixten: emit add_branch calls
for(scene_branch_case *Branch = FirstBranch; Branch != 0; Branch = Branch->Next)
{
S_EmitByte(Compiler, S_Op_AddBranch);
S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(Branch->Name)));
S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeOffset(0)));
scene_value_chunk *Chunk = Compiler->LastValueChunk;
Branch->OffsetValue = &Chunk->Values[Chunk->Count-1];
}
S_EmitByte(Compiler, S_Op_Halt);
// sixten: We need to keep track of the amount of bytes that have been emitted between branch cases, such that we can patch our relative jumps
// to point to the correct address.
s64 BaseOffset = 0;
//- sixten: emit branch contents
for(scene_branch_case *Branch = FirstBranch; Branch != 0; Branch = Branch->Next)
{
//- sixten: patch branch jump dest
Branch->OffsetValue->Offset = BaseOffset;
if(Branch->Bucket.Count > 0)
{
S_EmitBucket(Compiler, &Branch->Bucket);
BaseOffset += (Branch->Bucket.Count - 1)*ArrayCount(scene_bytecode_chunk::Data)+Branch->Bucket.Last->Count;
}
S_EmitByte(Compiler, S_Op_JumpClose);
BaseOffset += 1;
BaseOffset += S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeOffset(BaseOffset)));
scene_value_chunk *Chunk = Compiler->LastValueChunk;
Branch->EndOffsetValue = &Chunk->Values[Chunk->Count-1];
}
//- sixten: patch the last jump
for(scene_branch_case *Branch = FirstBranch; Branch != 0; Branch = Branch->Next)
{
Branch->EndOffsetValue->Offset = BaseOffset-Branch->EndOffsetValue->Offset;
}
ReleaseScratch(Scratch);
}
static scene_branch_case *S_ParseBranchCase(scene_compiler *Compiler, memory_arena *Arena)
{
scene_branch_case *Branch = PushStruct(Arena, scene_branch_case);
Branch->Name = S_ConsumeToken(Compiler, TokenKind_StringLiteral, "Expected branch label at start of branch case.");
S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after branch label.");
S_PushEmissionTarget(Compiler, S_RawEmissionTarget(Arena, &Branch->Bucket));
for(;Compiler->At < Compiler->TokensEnd;)
{
if(Compiler->At[0].Kind == TokenKind_CurlyClose)
{
Compiler->At += 1;
break;
}
else
{
S_ParseDeclaration(Compiler);
}
}
S_PopEmissionTarget(Compiler);
return(Branch);
}
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);
u8 *ChunkBegin = Chunk->Data;
u8 *ChunkEnd = ChunkBegin + Chunk->Count;
for(u8 *Data = ChunkBegin; Data < ChunkEnd;)
{
switch(*Data)
{
case S_Op_Constant:
{
Data += 1;
scene_variable_read VariableRead = S_ReadVariableLength(Data);
Data += VariableRead.Size;
scene_value Value = Compiler->FirstValueChunk->Values[VariableRead.Value];
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); Data += 1; } break;
case S_Op_True: { AppendString(&List, StrLit("True\n"), Scratch.Arena); Data += 1; } break;
case S_Op_False: { AppendString(&List, StrLit("False\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Negate: { AppendString(&List, StrLit("Negate\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Not: { AppendString(&List, StrLit("Not\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Add: { AppendString(&List, StrLit("Add\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Subtract: { AppendString(&List, StrLit("Subtract\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Multiply: { AppendString(&List, StrLit("Multiply\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Divide: { AppendString(&List, StrLit("Divide\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Equal: { AppendString(&List, StrLit("Equal\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Greater: { AppendString(&List, StrLit("Greater\n"), Scratch.Arena); Data += 1; } break;
case S_Op_Less: { AppendString(&List, StrLit("Less\n"), Scratch.Arena); Data += 1; } break;
case S_Op_DefineGlobal:
{
Data += 1;
scene_variable_read VariableRead = S_ReadVariableLength(Data);
Data += VariableRead.Size;
u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].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;
scene_variable_read VariableRead = S_ReadVariableLength(Data);
Data += VariableRead.Size;
u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].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;
scene_variable_read VariableRead = S_ReadVariableLength(Data);
Data += VariableRead.Size;
u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].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;
scene_variable_read VariableRead = S_ReadVariableLength(Data);
Data += VariableRead.Size;
u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].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);
Data += 1;
}
} break;
}
}
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);
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, S_RawEmissionTarget(Compiler.Arena, &Compiler.GlobalScope));
//- sixten: append tokenization errors
b32 FoundTokenizationError = false;
for(tokenizer_message *Message = TokenizeResult.Messages.First; Message != 0; Message = Message->Next)
{
if(Message->Kind == T_MessageKind_Error)
{
S_ParseError(&Compiler, (char *)Message->String.Data);
FoundTokenizationError = true;
}
}
//- sixten: tokens -> bytecode
if(!FoundTokenizationError)
{
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*sizeof(scene_value));
Dest += Chunk->Count;
}
}
//- sixten: copy errors
for(scene_compile_error *Error = Compiler.Errors.First; Error != 0; Error = Error->Next)
{
scene_compile_error *New = PushStruct(Arena, scene_compile_error);
New->Message = PushString(Arena, Error->Message);
New->Token = Error->Token;
QueuePush(Result.Errors.First, Result.Errors.Last, New);
}
Result.Errors.Count = Compiler.Errors.Count;
// sixten(IMPORTANT): The text is assumed to remain in memory for the duration of the scene.
Result.Source = Text;
Result.IsValid = !Compiler.EncounteredError;
ReleaseScratch(Scratch);
return(Result);
}
static compiled_scene S_CopyCompiledScene(memory_arena *Arena, compiled_scene *Compiled)
{
compiled_scene Result = {};
Assert(Compiled->Errors.Count == 0);
//- sixten: copy the procs
for(s64 BucketIndex = 0; BucketIndex < ArrayCount(Compiled->Buckets); BucketIndex += 1)
{
scene_proc_bucket *SourceBucket = &Compiled->Buckets[BucketIndex];
scene_proc_bucket *DestBucket = &Result.Buckets[BucketIndex];
for(scene_proc *Proc = SourceBucket->First; Proc != 0; Proc = Proc->Next)
{
scene_proc *NewProc = PushStruct(Arena, scene_proc);
NewProc->Name = PushString(Arena, Proc->Name);
NewProc->Data = PushArray(Arena, u8, Proc->Count);
Copy(NewProc->Data, Proc->Data, Proc->Count);
NewProc->Count = Proc->Count;
QueuePush(DestBucket->First, DestBucket->Last, NewProc);
}
}
//- sixten: copy the values
Result.Values = PushArray(Arena, scene_value, Compiled->ValueCount);
Copy(Result.Values, Compiled->Values, Compiled->ValueCount*sizeof(scene_value));
Result.ValueCount = Compiled->ValueCount;
//- sixten: copy the source
Result.Source = PushString(Arena, Compiled->Source);
Result.IsValid = true;//Compiled->IsValid;
return(Result);
}
////////////////////////////////
//~ sixten: Scene Runtime Functions
static void S_RuntimeError(scene_runtime *Runtime, string Message)
{
scene_runtime_error *Error = PushStruct(Runtime->ErrorArena, scene_runtime_error);
Error->Message = PushString(Runtime->ErrorArena, Message);
DLLInsertLast(Runtime->FirstError, Runtime->LastError, Error);
}
static void S_RuntimeErrorF(scene_runtime *Runtime, char *Format, ...)
{
va_list Arguments;
va_start(Arguments, Format);
scene_runtime_error *Error = PushStruct(Runtime->ErrorArena, scene_runtime_error);
Error->Message = PushFormatVariadic(Runtime->ErrorArena, Format, Arguments);
DLLInsertLast(Runtime->FirstError, Runtime->LastError, Error);
va_end(Arguments);
}
static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name)
{
scene_proc *Result = 0;
u64 Hash = HashString(Name);
scene_proc_bucket *Bucket = &Compiled->Buckets[Hash%ArrayCount(Compiled->Buckets)];
for(scene_proc *Proc = Bucket->First; Proc != 0; Proc = Proc->Next)
{
if(AreEqual(Proc->Name, Name))
{
Result = Proc;
break;
}
}
return(Result);
}
static scene_runtime_result S_Run(scene_runtime *Runtime, memory_arena *FrameArena, b32 AdvanceOnAwait)
{
scene_runtime_result Result = {};
compiled_scene *Compiled = &Runtime->Compiled;
Assert(Runtime != 0);
Assert(Compiled->IsValid);
if(Runtime->CurrentProc == 0)
{
Runtime->CurrentProc = S_FindProcByName(Compiled, StrLit("main"));
}
if(Runtime->CurrentProc)
{
if(Runtime->IP < Runtime->CurrentProc->Count)
{
u8 *Data = Runtime->CurrentProc->Data;
switch(Data[Runtime->IP])
{
case S_Op_Jump:
{
Runtime->IP += 1;
scene_variable_read VariableRead = S_ReadVariableLength(&Data[Runtime->IP]);
Runtime->IP += VariableRead.Size;
scene_value Value = Compiled->Values[VariableRead.Value];
if(Value.Kind == S_ValueKind_SourceRef)
{
string JumpDest = Substring(Compiled->Source, Value.SourceRef);
scene_proc *Dest = S_FindProcByName(Compiled, JumpDest);
if(Dest)
{
Runtime->CurrentProc = Dest;
Runtime->IP = 0;
}
else
{
S_RuntimeErrorF(Runtime, "Trying to jump to unknown proc: %S", JumpDest);
}
}
else
{
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving jump destination.");
}
} break;
case S_Op_JumpClose:
{
Runtime->IP += 1;
scene_variable_read OffsetVariableRead = S_ReadVariableLength(&Data[Runtime->IP]);
scene_value OffsetValue = Compiled->Values[OffsetVariableRead.Value];
if(OffsetValue.Kind == S_ValueKind_Offset)
{
Runtime->IP += OffsetValue.Offset;
}
else
{
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving offset.");
}
} break;
case S_Op_AddBranch:
{
Runtime->IP += 1;
scene_variable_read BranchVariableRead = S_ReadVariableLength(&Data[Runtime->IP]);
scene_value BranchName = Compiled->Values[BranchVariableRead.Value];
Runtime->IP += BranchVariableRead.Size;
if(BranchName.Kind == S_ValueKind_SourceRef)
{
scene_variable_read OffsetVariableRead = S_ReadVariableLength(&Data[Runtime->IP]);
scene_value Offset = Compiled->Values[OffsetVariableRead.Value];
Runtime->IP += OffsetVariableRead.Size;
if(Offset.Kind == S_ValueKind_Offset)
{
branch_case *Branch = &Runtime->Branches[Runtime->BranchCount];
Branch->Name = Substring(Compiled->Source, Pad(BranchName.SourceRef, -1));
Branch->Offset = Offset.Offset;
Runtime->BranchCount += 1;
}
else
{
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving offset.");
}
}
else
{
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving branch name.");
}
} break;
case S_Op_Halt: { Result.ReachedAwait = true; } break;
case S_Op_AwaitInput:
{
if(AdvanceOnAwait)
{
Runtime->IP += 1;
}
Result.ReachedAwait = true;
} break;
default:
{
if(Data[Runtime->IP] & S_Op_LineEntry)
{
textbox_action *Action = PushStructNoClear(FrameArena, textbox_action);
if(Data[Runtime->IP] & S_LineEntryFlag_NoClear)
{
Action->Kind = TextboxActionKind_Append;
}
else
{
Action->Kind = TextboxActionKind_Set;
}
Runtime->IP += 1;
scene_variable_read VariableRead = S_ReadVariableLength(&Data[Runtime->IP]);
Runtime->IP += VariableRead.Size;
scene_value Value = Compiled->Values[VariableRead.Value];
if(Value.Kind == S_ValueKind_SourceRef)
{
Action->String = Substring(Compiled->Source, Pad(Value.SourceRef, -1));
QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action);
}
else
{
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving line entry.");
}
}
else
{
S_RuntimeErrorF(Runtime, "Unknown bytecode op: 0x%02x", Data[Runtime->IP]);
}
} break;
}
}
else
{
S_RuntimeErrorF(Runtime, "Reached end of proc.");
}
}
else
{
S_RuntimeErrorF(Runtime, "No main entry was found");
}
Result.HadError = !DLLIsEmpty(Runtime->FirstError);
Runtime->LastResult = Result;
return(Result);
}