#include "generated/vn_scene.meta.h" #include "generated/vn_scene.meta.c" static void S_EmitByte(scene_compiler *Compiler, u8 Byte) { 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; } 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 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); u8 *ChunkBegin = Chunk->Data; u8 *ChunkEnd = ChunkBegin + Chunk->Count; for(u8 *Data = ChunkBegin; Data < ChunkEnd; Data += 1) { 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; } } 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); return(Result); }