2023-08-06 10:35:09 +00:00
|
|
|
#include "generated/vn_scene.meta.h"
|
|
|
|
#include "generated/vn_scene.meta.c"
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
////////////////////////////////
|
|
|
|
//~ sixten: Scene Compiler Functions
|
|
|
|
|
|
|
|
static void S_ParseError(scene_compiler *Compiler, char *Message, s64 TokenOffset)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
if(!Compiler->InPanicMode)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 10:35:09 +00:00
|
|
|
static void S_EmitByte(scene_compiler *Compiler, u8 Byte)
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_emission_target *Target = &Compiler->TargetStack[Compiler->TargetStackIndex];
|
|
|
|
scene_bytecode_bucket *Bucket = Target->Bucket;
|
|
|
|
scene_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_bytecode_chunk);
|
|
|
|
Chunk->Name = Target->Name;
|
|
|
|
|
|
|
|
QueuePush(Bucket->First, Bucket->Last, Chunk);
|
|
|
|
Bucket->Count += 1;
|
|
|
|
}
|
|
|
|
Chunk->Data[Chunk->Count] = Byte;
|
|
|
|
Chunk->Count += 1;
|
2023-07-19 15:09:41 +00:00
|
|
|
}
|
|
|
|
|
2023-09-05 17:50:49 +00:00
|
|
|
static void S_EmitU32(scene_compiler *Compiler, u32 Value)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_EmitByte(Compiler, Value >> 0);
|
|
|
|
S_EmitByte(Compiler, Value >> 8);
|
|
|
|
S_EmitByte(Compiler, Value >> 16);
|
|
|
|
S_EmitByte(Compiler, Value >> 24);
|
2023-09-05 17:50:49 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 10:00:34 +00:00
|
|
|
static void S_EmitBucket(scene_compiler *Compiler, scene_bytecode_bucket *Bucket)
|
2023-08-22 03:19:51 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
for(scene_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]);
|
|
|
|
}
|
|
|
|
}
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 10:35:09 +00:00
|
|
|
static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value)
|
2023-07-19 15:09:41 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
u64 Result = Compiler->ValueCount;
|
|
|
|
Chunk->Values[Chunk->Count] = Value;
|
|
|
|
Chunk->Count += 1;
|
|
|
|
Compiler->ValueCount += 1;
|
|
|
|
return(Result);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_EmitConstant(scene_compiler *Compiler, scene_value Value)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_EmitByte(Compiler, S_Op_Constant);
|
|
|
|
S_EmitU32(Compiler, S_MakeConstant(Compiler, Value));
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target)
|
2023-08-06 10:35:09 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
Compiler->TargetStack[Compiler->TargetStackIndex] = Target;
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
Compiler->TargetStackIndex += 1;
|
|
|
|
Compiler->TargetStack[Compiler->TargetStackIndex] = Target;
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_PopEmissionTarget(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
Compiler->TargetStackIndex -= 1;
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_AdvanceCompiler(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
Compiler->At += 1;
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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_StringLiteral: { Result = { S_ParseString, 0, S_Precedence_None }; } break;
|
|
|
|
case TokenKind_Identifier: { Result = { S_ParseVariable, 0, S_Precedence_None }; } break;
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
//InvalidCodepath;
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(Result);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseTopLevelDeclaration(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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 if(Compiler->At[0].Kind == TokenKind_Nav)
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
S_ParseNavFilePath(Compiler);
|
|
|
|
}
|
|
|
|
else if(Compiler->At[0].Kind == TokenKind_Background)
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
S_ParseBackgroundAsset(Compiler);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_ParseError(Compiler, "Expected top-level declaration (proc, var, background or nav).", 0);
|
|
|
|
Compiler->At += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseNavFilePath(scene_compiler *Compiler)
|
|
|
|
{
|
|
|
|
token NameToken = Compiler->At[0];
|
|
|
|
Compiler->At += 1;
|
|
|
|
|
|
|
|
if(NameToken.Kind == TokenKind_StringLiteral)
|
|
|
|
{
|
|
|
|
Compiler->NavFileName = Substring(Compiler->Text, Pad(NameToken.Range, -1));
|
|
|
|
if(Compiler->At[0].Kind == TokenKind_Semicolon)
|
|
|
|
{
|
|
|
|
// sixten: all good
|
|
|
|
Compiler->At += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_ParseError(Compiler, "Expected ';'.", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_ParseError(Compiler, "Expected nav file name.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseBackgroundAsset(scene_compiler *Compiler)
|
|
|
|
{
|
|
|
|
token AssetNameToken = Compiler->At[0];
|
|
|
|
Compiler->At += 1;
|
|
|
|
if(AssetNameToken.Kind == TokenKind_Identifier)
|
|
|
|
{
|
|
|
|
//- sixten: find asset id by name
|
|
|
|
string AssetName = Substring(Compiler->Text, AssetNameToken.Range);
|
|
|
|
Compiler->BackgroundTexture = AssetID_None;
|
|
|
|
for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1)
|
|
|
|
{
|
|
|
|
if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName))
|
|
|
|
{
|
|
|
|
Compiler->BackgroundTexture = AssetIndex;
|
|
|
|
goto ValidAssetFound;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
S_ParseError(Compiler, "Invalid asset name.");
|
|
|
|
|
|
|
|
ValidAssetFound:;
|
|
|
|
if(Compiler->At[0].Kind == TokenKind_Semicolon)
|
|
|
|
{
|
|
|
|
// sixten: all good
|
|
|
|
Compiler->At += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_ParseError(Compiler, "Expected ';'.", 0);
|
|
|
|
}
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseProcedure(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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_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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
static void S_ParseDeclaration(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
switch(Compiler->At[0].Kind)
|
|
|
|
{
|
|
|
|
case TokenKind_Var:
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
S_ParseVariableDeclaration(Compiler);
|
|
|
|
} break;
|
|
|
|
case TokenKind_Jump:
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
S_ParseJumpStatement(Compiler);
|
|
|
|
} break;
|
|
|
|
case TokenKind_Branch:
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
S_ParseBranchStatement(Compiler);
|
|
|
|
} break;
|
|
|
|
case TokenKind_At:
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
|
|
|
|
token IdentifierToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected identifier after '@'");
|
|
|
|
S_EmitConstant(Compiler, S_MakeSourceRef(IdentifierToken));
|
|
|
|
|
|
|
|
if(Compiler->At->Kind == TokenKind_ParenthesisOpen)
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
token StateToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected character state.");
|
|
|
|
S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after character state.");
|
|
|
|
|
|
|
|
S_EmitConstant(Compiler, S_MakeSourceRef(StateToken));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_EmitConstant(Compiler, S_MakeNil());
|
|
|
|
}
|
|
|
|
|
|
|
|
S_EmitByte(Compiler, S_Op_ShowCharacter);
|
|
|
|
|
|
|
|
if(Compiler->At->Kind == TokenKind_Semicolon)
|
|
|
|
{
|
|
|
|
Compiler->At += 1;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case TokenKind_StringLiteral:
|
|
|
|
{
|
|
|
|
S_ParseExpression(Compiler);
|
|
|
|
|
|
|
|
//- sixten: parse tags
|
|
|
|
{
|
|
|
|
b32 EmitAwait = true;
|
|
|
|
b32 EmitClear = true;
|
|
|
|
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")))
|
|
|
|
{
|
|
|
|
EmitClear = false;
|
|
|
|
}
|
|
|
|
else if(AreEqual(TagString, StrLit("noawait")))
|
|
|
|
{
|
|
|
|
EmitAwait = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_ParseError(Compiler, "Unknown tag.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(EmitClear)
|
|
|
|
{
|
|
|
|
S_EmitByte(Compiler, S_Op_ClearDialog);
|
|
|
|
}
|
|
|
|
|
|
|
|
S_EmitByte(Compiler, S_Op_LineEntry);
|
|
|
|
|
|
|
|
if(EmitAwait)
|
|
|
|
{
|
|
|
|
S_EmitByte(Compiler, S_Op_AwaitInput);
|
|
|
|
}
|
|
|
|
|
|
|
|
S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement.");
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
S_ParseStatement(Compiler);
|
|
|
|
S_EmitByte(Compiler, S_Op_Pop);
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Compiler->InPanicMode)
|
|
|
|
{
|
|
|
|
for(;Compiler->At < Compiler->TokensEnd;)
|
|
|
|
{
|
|
|
|
if(Compiler->At[-1].Kind == TokenKind_Semicolon)
|
|
|
|
{
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(Compiler->At[0].Kind)
|
|
|
|
{
|
|
|
|
case TokenKind_Var: goto End;
|
|
|
|
case TokenKind_StringLiteral: goto End;
|
|
|
|
case TokenKind_Jump: goto End;
|
|
|
|
case TokenKind_Branch: goto End;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Compiler->At += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
End:
|
|
|
|
Compiler->InPanicMode = false;
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseVariableDeclaration(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
Assert(NameConstant < U32_Max);
|
|
|
|
S_EmitU32(Compiler, NameConstant);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseVariable(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_ParseNamedVariable(Compiler, Compiler->At[-1], CanAssign);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static void S_ParseNamedVariable(scene_compiler *Compiler, token Token, b32 CanAssign)
|
2023-08-06 10:35:09 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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_EmitU32(Compiler, NameConstant);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static void S_ParseJumpStatement(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected jump destination.");
|
|
|
|
token DestToken = Compiler->At[-1];
|
|
|
|
|
|
|
|
S_EmitByte(Compiler, S_Op_Jump);
|
|
|
|
S_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(DestToken)));
|
|
|
|
|
|
|
|
S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after jump statement.");
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseBranchStatement(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
temp 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_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(Branch->Name)));
|
|
|
|
S_EmitU32(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;
|
|
|
|
S_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeOffset(BaseOffset)));
|
|
|
|
BaseOffset += 4;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
// sixten(NOTE): This little line here feels rather sketchy, it may one day fail on us.
|
|
|
|
Branch->EndOffsetValue->Offset = BaseOffset-Branch->EndOffsetValue->Offset-4;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseScratch(Scratch);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 10:00:34 +00:00
|
|
|
static scene_branch_case *S_ParseBranchCase(scene_compiler *Compiler, arena *Arena)
|
2023-08-22 03:19:51 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 10:35:09 +00:00
|
|
|
static void S_ParseStatement(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_ParseExpression(Compiler);
|
|
|
|
S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement.");
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseExpression(scene_compiler *Compiler)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_ParsePrecedence(Compiler, S_Precedence_Assignment);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseLiteral(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseNumber(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
r64 Value = DoubleFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1]));
|
|
|
|
|
|
|
|
S_EmitConstant(Compiler, S_MakeNumber(Value));
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
2023-09-05 17:50:49 +00:00
|
|
|
static void S_ParseString(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_EmitConstant(Compiler, S_MakeSourceRef(Pad(Compiler->At[-1].Range, -1)));
|
2023-09-05 17:50:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-06 10:35:09 +00:00
|
|
|
static void S_ParseGrouping(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_ParseExpression(Compiler);
|
|
|
|
S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after expression.");
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseUnary(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParseBinary(scene_compiler *Compiler, b32 CanAssign)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ParsePrecedence(scene_compiler *Compiler, scene_precedence Precedence)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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.");
|
|
|
|
}
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct proc_from_chunks_result
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_proc *Proc;
|
|
|
|
scene_bytecode_chunk *NextChunk;
|
2023-08-06 10:35:09 +00:00
|
|
|
};
|
|
|
|
|
2023-10-29 10:00:34 +00:00
|
|
|
static proc_from_chunks_result S_ProcFromChunks(arena *Arena, scene_bytecode_chunk *First)
|
2023-08-06 10:35:09 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
Assert(First != 0);
|
|
|
|
string ChunkName = First->Name;
|
|
|
|
|
|
|
|
//- sixten: find required bytes
|
|
|
|
s64 RequiredBytes = 0;
|
|
|
|
scene_bytecode_chunk *NextChunk = 0;
|
|
|
|
{
|
|
|
|
scene_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_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);
|
2023-08-06 10:35:09 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 10:00:34 +00:00
|
|
|
static compiled_scene S_ScriptFromText(arena *Arena, string Text)
|
2023-08-06 10:35:09 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
compiled_scene Result = {};
|
|
|
|
|
|
|
|
temp 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 global scope
|
|
|
|
if(Compiler.GlobalScope.First)
|
|
|
|
{
|
|
|
|
proc_from_chunks_result ProcResult = S_ProcFromChunks(Arena, Compiler.GlobalScope.First);
|
|
|
|
Result.GlobalScope = ProcResult.Proc;
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: bake compiled chunks
|
|
|
|
for(s64 BucketIndex = 0; BucketIndex < ArrayCount(Compiler.ProcBuckets); BucketIndex += 1)
|
|
|
|
{
|
|
|
|
scene_bytecode_bucket *Bucket = &Compiler.ProcBuckets[BucketIndex];
|
|
|
|
for(scene_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: copy nav file name
|
|
|
|
Result.NavFileName = Compiler.NavFileName;
|
|
|
|
|
|
|
|
//- sixten: copy background texture handle
|
|
|
|
Result.BackgroundTexture = Compiler.BackgroundTexture;
|
|
|
|
|
|
|
|
// 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);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 10:00:34 +00:00
|
|
|
static compiled_scene S_CopyCompiledScene(arena *Arena, compiled_scene *Compiled)
|
2023-08-22 03:19:51 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
compiled_scene Result = {};
|
|
|
|
|
|
|
|
//- sixten(TODO): copy over the errors
|
|
|
|
//Assert(Compiled->Errors.Count == 0);
|
|
|
|
|
|
|
|
//- sixten: copy the global scope
|
|
|
|
if(Compiled->GlobalScope)
|
|
|
|
{
|
|
|
|
Result.GlobalScope = PushStruct(Arena, scene_proc);
|
|
|
|
Result.GlobalScope->Name = PushString(Arena, Compiled->GlobalScope->Name);
|
|
|
|
Result.GlobalScope->Data = PushArray(Arena, u8, Compiled->GlobalScope->Count);
|
|
|
|
Copy(Result.GlobalScope->Data, Compiled->GlobalScope->Data, Compiled->GlobalScope->Count);
|
|
|
|
Result.GlobalScope->Count = Compiled->GlobalScope->Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
//- 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);
|
|
|
|
|
|
|
|
//- sixten: copy nav file
|
|
|
|
Result.NavFileName = PushString(Arena, Compiled->NavFileName);
|
|
|
|
|
|
|
|
//- sixten: copy background texture handle
|
|
|
|
Result.BackgroundTexture = Compiled->BackgroundTexture;
|
|
|
|
|
|
|
|
Result.IsValid = true;//Compiled->IsValid; // sixten(TODO): I don't know why this is commented out.
|
|
|
|
|
|
|
|
return(Result);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
//~ sixten: Scene Runtime Functions
|
|
|
|
static void S_RuntimeError(scene_runtime *Runtime, string Message)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_runtime_error *Error = PushStruct(Runtime->ErrorArena, scene_runtime_error);
|
|
|
|
Error->Message = PushString(Runtime->ErrorArena, Message);
|
|
|
|
DLLInsertLast(Runtime->Errors.First, Runtime->Errors.Last, Error);
|
|
|
|
Runtime->Errors.Count += 1;
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_RuntimeErrorF(scene_runtime *Runtime, char *Format, ...)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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->Errors.First, Runtime->Errors.Last, Error);
|
|
|
|
Runtime->Errors.Count += 1;
|
|
|
|
|
|
|
|
va_end(Arguments);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 10:00:34 +00:00
|
|
|
static scene_named_value *S_FindGlobalVariableByName(scene_runtime *Runtime, string Name, b32 AlwaysCreate)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_named_value *Result = 0;
|
|
|
|
|
|
|
|
u64 Hash = HashString(Name);
|
|
|
|
scene_named_value_bucket *Bucket = &Runtime->GlobalVariableBuckets[Hash%ArrayCount(Runtime->GlobalVariableBuckets)];
|
|
|
|
|
|
|
|
//- sixten: search for the value in the bucket
|
|
|
|
for(scene_named_value_node *Node = Bucket->First; Node != 0; Node = Node->Next)
|
|
|
|
{
|
|
|
|
scene_named_value *Value = &Node->NamedValue;
|
|
|
|
if(AreEqual(Name, Value->Name))
|
|
|
|
{
|
|
|
|
Result = Value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: create it if it doesn't exist
|
|
|
|
if(Result == 0)
|
|
|
|
{
|
|
|
|
//- sixten: check the free list first
|
|
|
|
scene_named_value_bucket *FreeList = &Runtime->GlobalVariableFreeList;
|
|
|
|
scene_named_value_node *Node = FreeList->First;
|
|
|
|
if(Node)
|
|
|
|
{
|
|
|
|
DLLRemove(FreeList->First, FreeList->Last, Node);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Node = PushStruct(Runtime->RuntimeArena, scene_named_value_node);
|
|
|
|
}
|
|
|
|
DLLInsertLast(Bucket->First, Bucket->Last, Node);
|
|
|
|
|
|
|
|
Result = &Node->NamedValue;
|
|
|
|
Result->Name = Name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(Result);
|
2023-10-29 10:00:34 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2023-09-05 17:50:49 +00:00
|
|
|
static void S_PushStack(scene_runtime *Runtime, scene_value Value)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_runtime_stack *Stack = &Runtime->Stack;
|
|
|
|
if(Stack->Count < ArrayCount(Stack->Stack))
|
|
|
|
{
|
|
|
|
Stack->Stack[Stack->Count] = Value;
|
|
|
|
Stack->Count += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("Stack overflow"));
|
|
|
|
}
|
2023-09-05 17:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static scene_value S_PopStack(scene_runtime *Runtime)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_value Result = {};
|
|
|
|
scene_runtime_stack *Stack = &Runtime->Stack;
|
|
|
|
if(Stack->Count > 0)
|
|
|
|
{
|
|
|
|
Stack->Count -= 1;
|
|
|
|
Result = Stack->Stack[Stack->Count];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("Stack underflow"));
|
|
|
|
}
|
|
|
|
return(Result);
|
2023-09-05 17:50:49 +00:00
|
|
|
}
|
|
|
|
|
2023-10-04 17:21:15 +00:00
|
|
|
static scene_value S_PopStackAndImplicitConvert(scene_runtime *Runtime)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_value Result = S_PopStack(Runtime);
|
|
|
|
if(Result.Kind == S_ValueKind_SourceRef)
|
|
|
|
{
|
|
|
|
Result.Kind = S_ValueKind_String;
|
|
|
|
Result.String = Substring(Runtime->Compiled.Source, Result.SourceRef);
|
|
|
|
}
|
|
|
|
return(Result);
|
2023-10-04 17:21:15 +00:00
|
|
|
}
|
|
|
|
|
2023-12-07 15:50:57 +00:00
|
|
|
static void S_SetCurrentProc(scene_runtime *Runtime, scene_proc *Proc)
|
2023-10-04 17:21:15 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
Runtime->CurrentProc = Proc;
|
|
|
|
Runtime->IP = 0;
|
|
|
|
Runtime->BranchCount = 0;
|
2023-12-07 15:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void S_ResetRuntime(scene_runtime *Runtime)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
S_SetCurrentProc(Runtime, 0);
|
|
|
|
Fill(Runtime->GlobalVariableBuckets, 0, sizeof(Runtime->GlobalVariableBuckets));
|
|
|
|
ZeroStruct(&Runtime->GlobalVariableFreeList);
|
|
|
|
|
|
|
|
ZeroStruct(&Runtime->Errors);
|
|
|
|
|
|
|
|
if(Runtime->RuntimeArena)
|
|
|
|
{
|
|
|
|
ArenaClear(Runtime->RuntimeArena);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Runtime->RuntimeArena = ArenaAlloc(Kilobytes(16));
|
|
|
|
}
|
2023-10-04 17:21:15 +00:00
|
|
|
}
|
|
|
|
|
2023-12-26 08:11:41 +00:00
|
|
|
static scene_runtime_result S_Run(scene_runtime *Runtime, arena *Arena, b32 AdvanceOnAwait)
|
2023-08-22 03:19:51 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
scene_runtime_result Result = {};
|
|
|
|
compiled_scene *Compiled = &Runtime->Compiled;
|
|
|
|
|
|
|
|
Assert(Runtime != 0);
|
|
|
|
Assert(Compiled->IsValid);
|
|
|
|
|
|
|
|
// sixten(NOTE): This will only work on little endian architectures.
|
2023-09-05 17:50:49 +00:00
|
|
|
#define S_ReadU32() (Runtime->IP += 4, *(u32 *)(Data+Runtime->IP-4))
|
|
|
|
#define S_ReadValue() Compiled->Values[S_ReadU32()]
|
2023-12-23 07:27:22 +00:00
|
|
|
|
|
|
|
//- sixten: run the bytecode interpreter
|
|
|
|
if(Runtime->CurrentProc)
|
|
|
|
{
|
|
|
|
if(Runtime->IP < Runtime->CurrentProc->Count)
|
|
|
|
{
|
|
|
|
u8 *Data = Runtime->CurrentProc->Data;
|
|
|
|
|
|
|
|
switch(Data[Runtime->IP])
|
|
|
|
{
|
|
|
|
case S_Op_Constant:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
S_PushStack(Runtime, S_ReadValue());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_Pop:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
S_PopStack(Runtime);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_Nil:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
S_PushStack(Runtime, S_MakeNil());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_True:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
S_PushStack(Runtime, S_MakeBoolean(true));
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_False:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
S_PushStack(Runtime, S_MakeBoolean(false));
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_Add:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
scene_value Value1 = S_PopStackAndImplicitConvert(Runtime);
|
|
|
|
scene_value Value2 = S_PopStackAndImplicitConvert(Runtime);
|
|
|
|
if(Value1.Kind == S_ValueKind_Number && Value2.Kind == S_ValueKind_Number)
|
|
|
|
{
|
|
|
|
S_PushStack(Runtime, S_MakeNumber(Value1.Number + Value2.Number));
|
|
|
|
}
|
|
|
|
else if(Value1.Kind == S_ValueKind_String && Value2.Kind == S_ValueKind_String)
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("String concatination is not yet supported."));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("This operation is not supported."));
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_Subtract:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
scene_value Value1 = S_PopStack(Runtime);
|
|
|
|
scene_value Value2 = S_PopStack(Runtime);
|
|
|
|
if(Value1.Kind == S_ValueKind_Number && Value2.Kind == S_ValueKind_Number)
|
|
|
|
{
|
|
|
|
S_PushStack(Runtime, S_MakeNumber(Value1.Number - Value2.Number));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("This operation is not supported."));
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_Multiply:
|
|
|
|
case S_Op_Divide:
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("This operation is not supported."));
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_DefineGlobal:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
scene_value Value = Runtime->Compiled.Values[S_ReadU32()];
|
|
|
|
if(Value.Kind == S_ValueKind_SourceRef)
|
|
|
|
{
|
|
|
|
string Name = Substring(Runtime->Compiled.Source, Value.SourceRef);
|
|
|
|
scene_named_value *NamedValue = S_FindGlobalVariableByName(Runtime, Name);
|
|
|
|
if(!NamedValue->Initialized)
|
|
|
|
{
|
|
|
|
NamedValue->Initialized = true;
|
|
|
|
scene_value TargetValue = S_PopStackAndImplicitConvert(Runtime);
|
|
|
|
NamedValue->Value = TargetValue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeErrorF(Runtime, "Global %S is already defined.", Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeError(Runtime, StrLit("Incorrect value kind when retrieving global name."));
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_Jump:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
scene_value Value = S_ReadValue();
|
|
|
|
if(Value.Kind == S_ValueKind_SourceRef)
|
|
|
|
{
|
|
|
|
string JumpDest = Substring(Compiled->Source, Value.SourceRef);
|
|
|
|
scene_proc *Dest = S_FindProcByName(Compiled, JumpDest);
|
|
|
|
if(Dest)
|
|
|
|
{
|
|
|
|
S_SetCurrentProc(Runtime, Dest);
|
|
|
|
}
|
|
|
|
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_value OffsetValue = S_ReadValue();
|
|
|
|
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_value BranchName = S_ReadValue();
|
|
|
|
if(BranchName.Kind == S_ValueKind_SourceRef)
|
|
|
|
{
|
|
|
|
scene_value Offset = S_ReadValue();
|
|
|
|
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;
|
|
|
|
|
|
|
|
case S_Op_ClearDialog:
|
|
|
|
{
|
2023-12-26 08:11:41 +00:00
|
|
|
scene_textbox_action *Action = PushStructNoClear(Arena, scene_textbox_action);
|
2023-12-23 07:27:22 +00:00
|
|
|
Action->Kind = S_TextboxActionKind_Set;
|
|
|
|
Action->String = StrLit("");
|
|
|
|
QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action);
|
|
|
|
|
|
|
|
Runtime->IP += 1;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_ShowCharacter:
|
|
|
|
{
|
|
|
|
Runtime->IP += 1;
|
|
|
|
|
|
|
|
scene_value State = S_PopStackAndImplicitConvert(Runtime);
|
|
|
|
scene_value CharacterValue = S_PopStackAndImplicitConvert(Runtime);
|
|
|
|
|
|
|
|
string Character = StrLit("");
|
|
|
|
if(CharacterValue.Kind == S_ValueKind_String)
|
|
|
|
{
|
|
|
|
Character = CharacterValue.String;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving character name.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// sixten: create & apply character action
|
2023-12-26 08:11:41 +00:00
|
|
|
scene_character_action *Action = PushStruct(Arena, scene_character_action);
|
2023-12-23 07:27:22 +00:00
|
|
|
Action->Target = Character;
|
|
|
|
|
|
|
|
if(State.Kind == S_ValueKind_Nil)
|
|
|
|
{
|
|
|
|
Action->State = CR_State_Invalid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Action->State = CR_CharacterStateFromString(State.String);
|
|
|
|
}
|
|
|
|
DLLInsertLast(Runtime->FirstCharacterAction, Runtime->LastCharacterAction, Action);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case S_Op_LineEntry:
|
|
|
|
{
|
2023-12-26 08:11:41 +00:00
|
|
|
scene_textbox_action *Action = PushStructNoClear(Arena, scene_textbox_action);
|
2023-12-23 07:27:22 +00:00
|
|
|
Action->Kind = S_TextboxActionKind_Append;
|
|
|
|
|
|
|
|
Runtime->IP += 1;
|
|
|
|
|
|
|
|
scene_value Value = S_PopStack(Runtime);
|
|
|
|
if(Value.Kind == S_ValueKind_SourceRef)
|
|
|
|
{
|
|
|
|
Action->String = Substring(Compiled->Source, Value.SourceRef);
|
|
|
|
QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving line entry.");
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
S_RuntimeErrorF(Runtime, "Unknown bytecode op: 0x%02x", Data[Runtime->IP]);
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Result.ReachedAwait = true;
|
|
|
|
Runtime->CurrentProc = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_RuntimeErrorF(Runtime, "No main entry was found");
|
|
|
|
}
|
|
|
|
|
|
|
|
Result.HadError = !DLLIsEmpty(Runtime->Errors.First);
|
|
|
|
|
|
|
|
Runtime->LastResult = Result;
|
|
|
|
return(Result);
|
2023-07-19 15:09:41 +00:00
|
|
|
}
|