vn/code/vn_scene2.cpp

1025 lines
38 KiB
C++

#include "generated/vn_scene2.meta.c"
////////////////////////////////
//~ sixten: Scene Message Functions
static void S2_PushMessage(arena *Arena, scene2_message_list *List, token Token, string Message)
{
scene2_message *Node = PushStruct(Arena, scene2_message);
Node->Message = PushString(Arena, Message);
Node->Token = Token;
QueuePush(List->First, List->Last, Node);
List->Count += 1;
}
static void S2_PushMessageFV(arena *Arena, scene2_message_list *List, token Token, char *Format, va_list Arguments)
{
scene2_message *Node = PushStruct(Arena, scene2_message);
Node->Message = PushFormatVariadic(Arena, Format, Arguments);
Node->Token = Token;
QueuePush(List->First, List->Last, Node);
List->Count += 1;
}
static void S2_PushMessageF(arena *Arena, scene2_message_list *List, token Token, char *Format, ...)
{
va_list Arguments;
va_start(Arguments, Format);
S2_PushMessageFV(Arena, List, Token, Format, Arguments);
va_end(Arguments);
}
static scene2_message_list S2_CopyMessageList(arena *Arena, scene2_message_list *Source)
{
scene2_message_list List = {};
for(scene2_message *Message = Source->First; Message != 0; Message = Message->Next)
{
S2_PushMessage(Arena, &List, Message->Token, Message->Message);
}
return(List);
}
static void S2_ConcatMessageList(arena *Arena, scene2_message_list *Dest, scene2_message_list *Source)
{
for(scene2_message *Message = Source->First; Message != 0; Message = Message->Next)
{
S2_PushMessage(Arena, Dest, Message->Token, Message->Message);
}
}
////////////////////////////////
//~ sixten: Scene Action Functions
static void S2_SceneActionPush(scene2_action_list *List, scene2_action *Action)
{
QueuePush(List->First, List->Last, Action);
List->Count += 1;
}
////////////////////////////////
//~ sixten: Scene Parsing Helpers
static token S2_Token(scene2_compiler *Compiler)
{
token Token = {};
if(Compiler->At >= 0 && Compiler->At < Compiler->Tokens.Count)
{
Token = Compiler->Tokens.Tokens[Compiler->At];
}
return(Token);
}
static token S2_NextToken(scene2_compiler *Compiler)
{
token Token = {};
if(Compiler->At+1 >= 0 && Compiler->At+1 < Compiler->Tokens.Count)
{
Token = Compiler->Tokens.Tokens[Compiler->At+1];
}
return(Token);
}
static string S2_StringFromToken(scene2_compiler *Compiler, token Token)
{
string Result = Substring(Compiler->Source, Intersection(Token.Range, Range1S64(0, Compiler->Source.Count)));
return(Result);
}
static string S2_TokenString(scene2_compiler *Compiler)
{
string Result = S2_StringFromToken(Compiler, S2_Token(Compiler));
return(Result);
}
static void S2_ParseError(scene2_compiler *Compiler, token Token, string Message)
{
S2_PushMessage(Compiler->Arena, &Compiler->Messages, Token, Message);
Compiler->InPanicMode = true;
}
static token S2_Consume(scene2_compiler *Compiler, token_kind Expected, string Message)
{
if(S2_Token(Compiler).Kind != Expected)
{
S2_ParseError(Compiler, S2_Token(Compiler), Message);
}
token Result = Compiler->Tokens.Tokens[Compiler->At];
S2_Advance(Compiler);
return(Result);
}
static void S2_Advance(scene2_compiler *Compiler)
{
Compiler->At += 1;
}
static scene2_parse_rule S2_ParseRuleFromToken(scene2_compiler *Compiler, token Token)
{
scene2_parse_rule Result = {};
switch(Token.Kind)
{
case TokenKind_ParenthesisOpen: { Result = { S2_ParseGrouping, 0, S2_Precedence_None }; } break;
case TokenKind_Bang: { Result = { S2_ParseUnary, 0, S2_Precedence_None }; } break;
case TokenKind_Minus: { Result = { S2_ParseUnary, S2_ParseBinary, S2_Precedence_Term }; } break;
case TokenKind_Plus: { Result = { 0, S2_ParseBinary, S2_Precedence_Term }; } break;
case TokenKind_Star: { Result = { 0, S2_ParseBinary, S2_Precedence_Factor }; } break;
case TokenKind_Slash: { Result = { 0, S2_ParseBinary, S2_Precedence_Factor }; } break;
case TokenKind_EqualEqual: { Result = { 0, S2_ParseBinary, S2_Precedence_Equality }; } break;
case TokenKind_BangEqual: { Result = { 0, S2_ParseBinary, S2_Precedence_Equality }; } break;
case TokenKind_Greater: { Result = { 0, S2_ParseBinary, S2_Precedence_Comparison }; } break;
case TokenKind_GreaterEqual: { Result = { 0, S2_ParseBinary, S2_Precedence_Comparison }; } break;
case TokenKind_Less: { Result = { 0, S2_ParseBinary, S2_Precedence_Comparison }; } break;
case TokenKind_LessEqual: { Result = { 0, S2_ParseBinary, S2_Precedence_Comparison }; } break;
case TokenKind_False: { Result = { S2_ParseLiteral, 0, S2_Precedence_None }; } break;
case TokenKind_True: { Result = { S2_ParseLiteral, 0, S2_Precedence_None }; } break;
case TokenKind_Numeric: { Result = { S2_ParseNumber, 0, S2_Precedence_None }; } break;
case TokenKind_StringLiteral: { Result = { S2_ParseString, 0, S2_Precedence_None }; } break;
case TokenKind_Identifier: { Result = { S2_ParseVariable, 0, S2_Precedence_None }; } break;
case TokenKind_Call: { Result = { S2_ParseCall, 0, S2_Precedence_None }; } break;
default: {} break;
}
return(Result);
}
static scene2_instruction S2_Instruction(scene2_opcode Opcode)
{
scene2_instruction Result = {};
Result.Opcode = Opcode;
return(Result);
}
static scene2_instruction S2_Instruction(scene2_opcode Opcode, u64 Arg)
{
scene2_instruction Result = {};
Result.Opcode = Opcode;
Result.Arg = Arg;
return(Result);
}
static scene2_instruction S2_Instruction(scene2_opcode Opcode, r64 Number)
{
scene2_instruction Result = {};
Result.Opcode = Opcode;
Result.Number = Number;
return(Result);
}
static scene2_instruction S2_Instruction(scene2_opcode Opcode, u32 Arg1, u32 Arg2)
{
scene2_instruction Result = {};
Result.Opcode = Opcode;
Result.Arg1 = Arg1;
Result.Arg2 = Arg2;
return(Result);
}
static scene2_instruction S2_Instruction(scene2_opcode Opcode, range1_s64 Range)
{
Assert(0 <= Range.Min && Range.Min <= U32_Max);
Assert(0 <= Range.Max && Range.Max <= U32_Max);
scene2_instruction Result = {};
Result.Opcode = Opcode;
Result.Min = Range.Min;
Result.Max = Range.Max;
return(Result);
}
static scene2_instruction *S2_Emit(scene2_compiler *Compiler, scene2_instruction Instruction)
{
scene2_instruction_chunk *Chunk = Compiler->LastChunk;
if(!Chunk || Chunk->InstructionsUsed >= ArrayCount(Chunk->Instructions))
{
Chunk = PushStruct(Compiler->Arena, scene2_instruction_chunk);
QueuePush(Compiler->FirstChunk, Compiler->LastChunk, Chunk);
}
scene2_instruction *Result = Chunk->Instructions + Chunk->InstructionsUsed;
Chunk->InstructionsUsed += 1;
Compiler->InstructionPointer += 1;
*Result = Instruction;
return(Result);
}
////////////////////////////////
//~ sixten: Scene Parsing Functions
static void S2_ParseTopLevel(scene2_compiler *Compiler)
{
// sixten(NOTE): top-level -> { proc declaration }
for(;Compiler->At < Compiler->Tokens.Count;)
{
switch(S2_Token(Compiler).Kind)
{
case TokenKind_Proc:
{
S2_ParseProc(Compiler);
} break;
default:
{
S2_ParseError(Compiler, S2_Token(Compiler), StrLit("Expected top level delaration (proc)"));
S2_Advance(Compiler);
} break;
}
}
}
static void S2_ParseProc(scene2_compiler *Compiler)
{
// sixten(NOTE): proc -> 'proc' name { statement } 'end'
S2_Advance(Compiler); // sixten: skip 'proc'
token NameToken = S2_Consume(Compiler, TokenKind_Identifier, StrLit("Expected name after 'proc'."));
scene2_proc_node *Node = PushStruct(Compiler->Arena, scene2_proc_node);
QueuePush(Compiler->ProcList.First, Compiler->ProcList.Last, Node);
Compiler->ProcList.Count += 1;
Node->Info.InstructionBegin = Compiler->InstructionPointer;
Node->Info.Name = S2_StringFromToken(Compiler, NameToken);
S2_ParseBlock(Compiler);
Node->Info.InstructionEnd = Compiler->InstructionPointer;
}
static void S2_ParseBlock(scene2_compiler *Compiler)
{
// sixten: block -> { statement } 'end'
S2_Emit(Compiler, S2_Instruction(S2_Op_BeginBlock));
b32 FoundEnd = false;
for(;Compiler->At < Compiler->Tokens.Count;)
{
switch(S2_Token(Compiler).Kind)
{
case TokenKind_End:
{
S2_Advance(Compiler); // sixten: skip 'end'
FoundEnd = true;
goto Done;
} break;
default:
{
S2_ParseStatement(Compiler);
} break;
}
}
Done:;
if(!FoundEnd)
{
S2_ParseError(Compiler, S2_Token(Compiler), StrLit("Expected 'end' after block."));
}
S2_Emit(Compiler, S2_Instruction(S2_Op_EndBlock));
}
static void S2_ParseStatement(scene2_compiler *Compiler)
{
// sixten: statement -> if statement | while statement | let statetment | expression
switch(S2_Token(Compiler).Kind)
{
case TokenKind_Wait: { S2_ParseWaitStatement(Compiler); } break;
case TokenKind_If: { S2_ParseIfStatement(Compiler); } break;
case TokenKind_While: { S2_ParseWhileStatement(Compiler); } break;
case TokenKind_Let: { S2_ParseLetStatement(Compiler); } break;
case TokenKind_DollarSign: { S2_ParseShorthand(Compiler); } break;
default: { S2_ParseExpression(Compiler); } break;
}
if(Compiler->InPanicMode)
{
for(;Compiler->At < Compiler->Tokens.Count;)
{
if(S2_Token(Compiler).Kind == TokenKind_End)
{
break;
}
else
{
S2_Advance(Compiler);
}
}
Compiler->InPanicMode = false;
}
}
static void S2_ParseWaitStatement(scene2_compiler *Compiler)
{
S2_Advance(Compiler); // sixten: skip 'wait'
S2_Emit(Compiler, S2_Instruction(S2_Op_Wait));
}
static void S2_ParseIfStatement(scene2_compiler *Compiler)
{
S2_Advance(Compiler); // sixten: skip 'if'
S2_ParseExpression(Compiler);
S2_Emit(Compiler, S2_Instruction(S2_Op_Not)); // sixten: we only jump if the condition is _NOT_ satisfied.
scene2_instruction *JumpInstruction = S2_Emit(Compiler, S2_Instruction(S2_Op_CondJump));
u64 OffsetBefore = Compiler->InstructionPointer;
S2_Consume(Compiler, TokenKind_Then, StrLit("Expected 'then' after if-statement"));
S2_ParseBlock(Compiler);
u64 JumpOffset = Compiler->InstructionPointer - OffsetBefore;
JumpInstruction->Arg = JumpOffset;
}
static void S2_ParseWhileStatement(scene2_compiler *Compiler)
{
S2_Advance(Compiler); // sixten: skip 'while'
u64 CondBeginOffset = Compiler->InstructionPointer;
S2_ParseExpression(Compiler);
S2_Emit(Compiler, S2_Instruction(S2_Op_Not)); // sixten: we only jump if the condition is _NOT_ satisfied.
scene2_instruction *CondNotSatisfiedJump = S2_Emit(Compiler, S2_Instruction(S2_Op_CondJump));
u64 OffsetJump = Compiler->InstructionPointer;
S2_Consume(Compiler, TokenKind_Then, StrLit("Expected 'then' after while-statement"));
S2_ParseBlock(Compiler);
S2_Emit(Compiler, S2_Instruction(S2_Op_Jump, (u64)(CondBeginOffset - Compiler->InstructionPointer - 1)));
u64 EndOffset = Compiler->InstructionPointer;
CondNotSatisfiedJump->Arg = EndOffset - OffsetJump;
}
static void S2_ParseLetStatement(scene2_compiler *Compiler)
{
S2_Advance(Compiler); // sixten: skip 'let'
token LocalName = S2_Consume(Compiler, TokenKind_Identifier, StrLit("Expected variable name after 'let'"));
if(S2_Token(Compiler).Kind == TokenKind_Equal)
{
S2_Advance(Compiler); // sixten: skip equals
S2_ParseExpression(Compiler);
S2_Emit(Compiler, S2_Instruction(S2_Op_SetLocal, LocalName.Range));
S2_Emit(Compiler, S2_Instruction(S2_Op_SetVariable, LocalName.Range));
}
else
{
S2_Emit(Compiler, S2_Instruction(S2_Op_SetLocal, LocalName.Range));
}
}
static void S2_ParseShorthand(scene2_compiler *Compiler)
{
S2_Advance(Compiler); // sixten: skip '$'
// sixten(NOTE): since we're using the parenthesis to check for a target specifier, we cannot
// use grouping as the text to append to the textbox. Should never really be an issue, but it
// does technically change how the languange should be documented.
S2_Emit(Compiler, S2_Instruction(S2_Op_Call, S2_CallFunctionIndexFromString(StrLit("ctr_unset_all_talking"))));
S2_Emit(Compiler, S2_Instruction(S2_Op_Call, S2_CallFunctionIndexFromString(StrLit("text_clear"))));
if(S2_Token(Compiler).Kind == TokenKind_ParenthesisOpen)
{
S2_Advance(Compiler); // sixten: skip the '('
S2_ParseExpression(Compiler); // sixten: emit the target
S2_Emit(Compiler, S2_Instruction(S2_Op_DuplicateStack, 1llu));
// sixten: emit the action
if(S2_Token(Compiler).Kind == TokenKind_Comma)
{
S2_Advance(Compiler); // sixten: skip the ','
token AssetToken = S2_Consume(Compiler, TokenKind_Identifier, StrLit("Expected asset name after shorthand & target specifier"));
asset_id ID = A_AssetIDFromString(S2_StringFromToken(Compiler, AssetToken));
if(ID == AssetID_None)
{
S2_ParseError(Compiler, AssetToken, StrLit("Unknown asset"));
}
S2_Emit(Compiler, S2_Instruction(S2_Op_PushNumber, (r64)ID));
}
else
{
S2_Emit(Compiler, S2_Instruction(S2_Op_PushNil));
}
S2_Consume(Compiler, TokenKind_ParenthesisClose, StrLit("Expetcted ')' after shorthand specifier"));
S2_Emit(Compiler, S2_Instruction(S2_Op_Call, S2_CallFunctionIndexFromString(StrLit("ctr_set"))));
S2_Emit(Compiler, S2_Instruction(S2_Op_Call, S2_CallFunctionIndexFromString(StrLit("ctr_set_talking"))));
}
S2_ParseExpression(Compiler);
S2_Emit(Compiler, S2_Instruction(S2_Op_Call, S2_CallFunctionIndexFromString(StrLit("text_append"))));
S2_Emit(Compiler, S2_Instruction(S2_Op_Wait));
}
static void S2_ParseExpression(scene2_compiler *Compiler)
{
S2_ParsePrecedence(Compiler, S2_Precedence_Assignment);
}
static void S2_ParsePrecedence(scene2_compiler *Compiler, scene2_precedence Precedence)
{
b32 CanAssign = Precedence <= S2_Precedence_Assignment;
//- sixten: do prefix rule
{
scene2_parse_rule Rule = S2_ParseRuleFromToken(Compiler, S2_Token(Compiler));
if(Rule.PrefixRule)
{
Rule.PrefixRule(Compiler, CanAssign);
}
else
{
S2_ParseError(Compiler, S2_Token(Compiler), StrLit("Expected expression."));
}
}
//- sixten: do infix rule
for(;;)
{
scene2_parse_rule Rule = S2_ParseRuleFromToken(Compiler, S2_Token(Compiler));
if(Precedence <= Rule.Precedence)
{
Rule.InfixRule(Compiler, CanAssign);
}
else
{
break;
}
}
if(CanAssign && S2_Token(Compiler).Kind == TokenKind_Equal)
{
S2_ParseError(Compiler, S2_Token(Compiler), StrLit("Invalid assignment target."));
}
}
static void S2_ParseGrouping(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
S2_Advance(Compiler); // sixten: skip '('
S2_ParseExpression(Compiler);
S2_Consume(Compiler, TokenKind_ParenthesisClose, StrLit("Expected ')' after expression."));
}
static void S2_ParseUnary(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
scene2_operator Operator = S2_OperatorFromString(S2_TokenString(Compiler));
S2_Advance(Compiler);
S2_ParsePrecedence(Compiler, S2_Precedence_Unary);
switch(Operator)
{
case S2_Operator_Minus: { S2_Emit(Compiler, S2_Instruction(S2_Op_Negate)); } break;
case S2_Operator_Not: { S2_Emit(Compiler, S2_Instruction(S2_Op_Not)); } break;
InvalidDefaultCase;
}
}
static void S2_ParseBinary(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
token Token = S2_Token(Compiler);
scene2_operator Operator = S2_OperatorFromString(S2_TokenString(Compiler));
scene2_parse_rule Rule = S2_ParseRuleFromToken(Compiler, Token);
S2_Advance(Compiler); // sixten: skip operator
S2_ParsePrecedence(Compiler, (scene2_precedence)(Rule.Precedence + 1));
switch(Operator)
{
case S2_Operator_Add: { S2_Emit(Compiler, S2_Instruction(S2_Op_Add)); } break;
case S2_Operator_Minus: { S2_Emit(Compiler, S2_Instruction(S2_Op_Subtract)); } break;
case S2_Operator_Multiply: { S2_Emit(Compiler, S2_Instruction(S2_Op_Multiply)); } break;
case S2_Operator_Divide: { S2_Emit(Compiler, S2_Instruction(S2_Op_Divide)); } break;
case S2_Operator_Equals: { S2_Emit(Compiler, S2_Instruction(S2_Op_Equals)); } break;
case S2_Operator_NotEquals: { S2_Emit(Compiler, S2_Instruction(S2_Op_Equals)); S2_Emit(Compiler, S2_Instruction(S2_Op_Not)); } break;
case S2_Operator_Greater: { S2_Emit(Compiler, S2_Instruction(S2_Op_Greater)); } break;
case S2_Operator_GreaterThanOrEquals: { S2_Emit(Compiler, S2_Instruction(S2_Op_Less)); S2_Emit(Compiler, S2_Instruction(S2_Op_Not)); } break;
case S2_Operator_Less: { S2_Emit(Compiler, S2_Instruction(S2_Op_Less)); } break;
case S2_Operator_LessThanOrEquals: { S2_Emit(Compiler, S2_Instruction(S2_Op_Greater)); S2_Emit(Compiler, S2_Instruction(S2_Op_Not)); } break;
InvalidDefaultCase;
}
}
static void S2_ParseVariable(scene2_compiler *Compiler, b32 CanAssign)
{
token VariableToken = S2_Consume(Compiler, TokenKind_Identifier, StrLit("Expected identifier."));
if(CanAssign && S2_Token(Compiler).Kind == TokenKind_Equal)
{
S2_Advance(Compiler);
S2_ParseExpression(Compiler);
S2_Emit(Compiler, S2_Instruction(S2_Op_SetVariable, VariableToken.Range)) ;
}
else
{
S2_Emit(Compiler, S2_Instruction(S2_Op_PushVariable, VariableToken.Range)) ;
}
}
static void S2_ParseCall(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
S2_Advance(Compiler); // sixten: skip 'call'
token NameToken = S2_Consume(Compiler, TokenKind_Identifier, StrLit("Expected function name."));
S2_Consume(Compiler, TokenKind_ParenthesisOpen, StrLit("Expected '(' after function name"));
//- sixten: parse arguments
token Token = S2_Token(Compiler);
if(Token.Kind != TokenKind_ParenthesisClose)
{
for(;Compiler->At < Compiler->Tokens.Count;)
{
S2_ParseExpression(Compiler);
Token = S2_Token(Compiler);
if(Token.Kind == TokenKind_ParenthesisClose)
{
break;
}
else
{
S2_Consume(Compiler, TokenKind_Comma, StrLit("Expected ',' or ')'."));
}
}
}
S2_Advance(Compiler); // sixten: skip ')'
S2_Emit(Compiler, S2_Instruction(S2_Op_Call, S2_CallFunctionIndexFromString(S2_StringFromToken(Compiler, NameToken))));
}
static void S2_ParseLiteral(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
token LiteralToken = S2_Token(Compiler);
switch(LiteralToken.Kind)
{
case TokenKind_False: { S2_Emit(Compiler, S2_Instruction(S2_Op_PushBool, 0ull)); } break;
case TokenKind_True: { S2_Emit(Compiler, S2_Instruction(S2_Op_PushBool, 1ull)); } break;
InvalidDefaultCase;
}
S2_Advance(Compiler); // sixten: skip the literal
}
static void S2_ParseNumber(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
token NumberToken = S2_Token(Compiler);
S2_Advance(Compiler); // sixten: skip the numeric literal
S2_Emit(Compiler, S2_Instruction(S2_Op_PushNumber, DoubleFromString(S2_StringFromToken(Compiler, NumberToken))));
}
static void S2_ParseString(scene2_compiler *Compiler, b32 CanAssign)
{
UnusedVariable(CanAssign);
token StringToken = S2_Token(Compiler);
S2_Advance(Compiler); // sixten: skip the string literal
S2_Emit(Compiler, S2_Instruction(S2_Op_PushString, Pad(StringToken.Range, -1))); // sixten(TODO): push the string to the stack
}
static scene2_proc_list S2_CopyProcList(arena *Arena, scene2_proc_list Source)
{
scene2_proc_list Result = {};
for(scene2_proc_node *Node = Source.First; Node != 0; Node = Node->Next)
{
scene2_proc_node *New = PushStruct(Arena, scene2_proc_node);
New->Info.Name = PushString(Arena, Node->Info.Name);
New->Info.InstructionBegin = Node->Info.InstructionBegin;
New->Info.InstructionEnd = Node->Info.InstructionEnd;
QueuePush(Result.First, Result.Last, New);
Result.Count += 1;
}
Assert(Result.Count == Source.Count);
return(Result);
}
////////////////////////////////
//~ sixten: Scene Runtime Helper Functions
static string S2_StringFromObject(arena *Arena, scene2_object Object)
{
string Result = StrLit("unknown");
switch(Object.Kind)
{
case S2_ObjectKind_Nil: { Result = StrLit("nil"); } break;
case S2_ObjectKind_Bool: { Result = PushFormat(Arena, "%b", Object.As.Bool); } break;
case S2_ObjectKind_Number:
{
if(Object.As.Number == Round(Object.As.Number))
{
Result = PushFormat(Arena, "%i", (s64)Object.As.Number);
}
else
{
Result = PushFormat(Arena, "%f", Object.As.Number);
}
} break;
case S2_ObjectKind_String: { Result = PushString(Arena, Object.As.String); } break;
}
return(Result);
}
static void S2_StackPush(scene2_runtime *Runtime, scene2_object Object)
{
if(Runtime->StackPointer < ArrayCount(Runtime->Stack))
{
Runtime->Stack[Runtime->StackPointer++] = Object;
}
}
static scene2_object S2_StackPop(scene2_runtime *Runtime)
{
scene2_object Result = {};
if(Runtime->StackPointer > 0)
{
Result = Runtime->Stack[--Runtime->StackPointer];
}
return(Result);
}
static scene2_object S2_MakeNil(void)
{
scene2_object Result = {};
Result.Kind = S2_ObjectKind_Nil;
return(Result);
}
static scene2_object S2_MakeBool(b32 Bool)
{
scene2_object Result = {};
Result.Kind = S2_ObjectKind_Bool;
Result.As.Bool = Bool;
return(Result);
}
static scene2_object S2_MakeNumber(r64 Number)
{
scene2_object Result = {};
Result.Kind = S2_ObjectKind_Number;
Result.As.Number = Number;
return(Result);
}
static scene2_object S2_MakeString(string String)
{
scene2_object Result = {};
Result.Kind = S2_ObjectKind_String;
Result.As.String = String;
return(Result);
}
static b32 S2_ObjectIsTrue(scene2_object Object)
{
b32 Result = false;
if(Object.Kind == S2_ObjectKind_Bool)
{
Result = Object.As.Bool;
}
else if(Object.Kind == S2_ObjectKind_Number && Object.As.Number != 0)
{
Result = true;
}
else if(Object.Kind == S2_ObjectKind_String)
{
Result = true;
}
return(Result);
}
static string S2_StringFromInstruction(compiled_scene2 *Compiled, scene2_instruction Instruction)
{
string Result = Substring(Compiled->Source, Range1S64(Instruction.Min, Instruction.Max));
return(Result);
}
static scene2_variable *S2_NewVariable(scene2_runtime *Runtime)
{
scene2_variable *Result = Runtime->FirstFreeVariable;
if(Result)
{
DLLRemove(Runtime->FirstFreeVariable, Runtime->LastFreeVariable, Result);
ZeroStruct(Result);
}
else
{
Result = PushStruct(Runtime->VariableArena, scene2_variable);
}
return(Result);
}
static scene2_variable *S2_VariableFromString(scene2_runtime *Runtime, string Name)
{
scene2_variable *Result = 0;
//- sixten: check if the variable exists
for(scene2_variable *Variable = Runtime->FirstGlobalVariable; Variable != 0; Variable = Variable->Next)
{
if(AreEqual(Name, Variable->Name))
{
Result = Variable;
break;
}
}
if(!Result)
{
Result = S2_NewVariable(Runtime);
Result->Name = PushString(Runtime->VariableArena, Name);
DLLInsertLast(Runtime->FirstGlobalVariable, Runtime->LastGlobalVariable, Result);
}
return(Result);
}
////////////////////////////////
//~ sixten: Scene Interface Functions
static compiled_scene2 S2_CompiledFromString(arena *Arena, string String)
{
temp Scratch = GetScratch(&Arena, 1);
//- sixten: string -> tokens
tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, String, T_IsIrregular);
scene2_compiler Compiler = {};
Compiler.Arena = Scratch.Arena;
Compiler.Tokens = TokenizeResult.Tokens;
Compiler.Source = String;
//- sixten: tokens -> bytecode
S2_ParseTopLevel(&Compiler);
compiled_scene2 Result = {};
{
//- sixten: copy source
Result.Source = PushString(Arena, String);
//- sixten: copy messages
Result.Messages = S2_CopyMessageList(Arena, &Compiler.Messages);
//- sixten: copy instructions
Result.InstructionCount = Compiler.InstructionPointer;
Result.Instructions = PushArray(Arena, scene2_instruction, Result.InstructionCount);
u64 InstructionIndex = 0;
for(scene2_instruction_chunk *Chunk = Compiler.FirstChunk; Chunk != 0; Chunk = Chunk->Next)
{
Copy(Result.Instructions+InstructionIndex, Chunk->Instructions, Chunk->InstructionsUsed*sizeof(scene2_instruction));
InstructionIndex += Chunk->InstructionsUsed;
}
//- sixten: copy procs
Result.ProcList = S2_CopyProcList(Arena, Compiler.ProcList);
}
ReleaseScratch(Scratch);
return(Result);
}
static compiled_scene2 S2_CopyCompiledScene(arena *Arena, compiled_scene2 *Source)
{
compiled_scene2 Result = {};
Result.Source = PushString(Arena, Source->Source);
Result.Messages = S2_CopyMessageList(Arena, &Source->Messages);
Result.InstructionCount = Source->InstructionCount;
Result.Instructions = PushArray(Arena, scene2_instruction, Result.InstructionCount);
Copy(Result.Instructions, Source->Instructions, Result.InstructionCount*sizeof(scene2_instruction));
Result.ProcList = S2_CopyProcList(Arena, Source->ProcList);
return(Result);
}
static void S2_SetCurrentProc(scene2_runtime *Runtime, scene2_proc_info Info)
{
Runtime->CurrentProc = Info;
Runtime->InstructionPointer = Info.InstructionBegin;
}
static scene2_proc_info S2_ProcFromName(compiled_scene2 *Compiled, string Name)
{
scene2_proc_info Result = {};
for(scene2_proc_node *Node = Compiled->ProcList.First; Node != 0; Node = Node->Next)
{
if(AreEqual(Node->Info.Name, Name))
{
Result = Node->Info;
break;
}
}
return(Result);
}
static void S2_Init(scene2_runtime *Runtime)
{
Runtime->VariableArena = ArenaAlloc(4096, true, "Scene Runtime Variable Arena");
}
static scene2_run_result S2_Run(arena *Arena, scene2_runtime *Runtime, compiled_scene2 *Compiled, b32 PlayerAction)
{
scene2_run_result Result = {};
scene2_proc_info *ProcInfo = &Runtime->CurrentProc;
for(;ProcInfo->InstructionBegin <= Runtime->InstructionPointer && Runtime->InstructionPointer < ProcInfo->InstructionEnd && Result.Messages.Count < 100;)
{
scene2_instruction Instruction = Compiled->Instructions[Runtime->InstructionPointer];
switch(Instruction.Opcode)
{
case S2_Op_None:
default:
{
if(S2_Op_None <= Instruction.Opcode && Instruction.Opcode < S2_Op_COUNT)
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Unimplemented opcode: %S (%S)", S2_InstructionNameLookup[Instruction.Opcode], S2_InstructionInfoLookup[Instruction.Opcode]);
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Unknown opcode: %i", Instruction.Opcode);
}
} break;
case S2_Op_Call:
{
// sixten: find the binding
scene2_call_function *Function = S2_CallBindingLookup[Instruction.Arg];
if(Function == S2_Call_Stub)
{
// sixten(TODO): do we error here??
}
Function(Runtime, Compiled);
} break;
case S2_Op_Wait:
{
if(PlayerAction)
{
// sixten: "consume" the action
PlayerAction = false;
}
else
{
goto Done;
}
} break;
case S2_Op_Pop:
{
S2_StackPop(Runtime);
} break;
case S2_Op_PushBool:
{
S2_StackPush(Runtime, S2_MakeBool(Instruction.Arg));
} break;
case S2_Op_PushNumber:
{
S2_StackPush(Runtime, S2_MakeNumber(Instruction.Number));
} break;
case S2_Op_PushString:
{
S2_StackPush(Runtime, S2_MakeString(S2_StringFromInstruction(Compiled, Instruction)));
} break;
case S2_Op_PushNil:
{
S2_StackPush(Runtime, S2_MakeNil());
} break;
case S2_Op_PushVariable:
{
scene2_variable *Variable = S2_VariableFromString(Runtime, S2_StringFromInstruction(Compiled, Instruction));
S2_StackPush(Runtime, Variable->Value);
} break;
case S2_Op_DuplicateStack:
{
temp Scratch = GetScratch();
scene2_object *Stack = PushArrayNoClear(Scratch.Arena, scene2_object, Instruction.Arg);
for(u64 Index = 0; Index < Instruction.Arg; Index += 1)
{
Stack[Index] = S2_StackPop(Runtime);
}
for(u64 Index = 0; Index < 2*Instruction.Arg; Index += 1)
{
S2_StackPush(Runtime, Stack[Index%Instruction.Arg]);
}
ReleaseScratch(Scratch);
} break;
case S2_Op_SetVariable:
{
scene2_object NewValue = S2_StackPop(Runtime);
scene2_variable *Variable = S2_VariableFromString(Runtime, S2_StringFromInstruction(Compiled, Instruction));
Variable->Value = NewValue;
} break;
case S2_Op_SetLocal:
{
InvalidCodepath;
} break;
case S2_Op_BeginBlock:
{
} break;
case S2_Op_EndBlock:
{
} break;
case S2_Op_Jump:
{
Runtime->InstructionPointer += Instruction.Arg;
} break;
case S2_Op_CondJump:
{
if(S2_ObjectIsTrue(S2_StackPop(Runtime)))
{
Runtime->InstructionPointer += Instruction.Arg;
}
} break;
case S2_Op_Add:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeNumber(Object1.As.Number+Object2.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to add two incompatible types: %S & %S. Will return nil.", S2_ObjectKindNameLookup[Object1.Kind], S2_ObjectKindNameLookup[Object2.Kind]);
S2_StackPush(Runtime, S2_MakeNil());
}
} break;
case S2_Op_Subtract:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeNumber(Object2.As.Number-Object1.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to subtract two incompatible types: %S & %S. Will return nil.", S2_ObjectKindNameLookup[Object1.Kind], S2_ObjectKindNameLookup[Object2.Kind]);
S2_StackPush(Runtime, S2_MakeNil());
}
} break;
case S2_Op_Multiply:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeNumber(Object2.As.Number*Object1.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to multiply two incompatible types: %S & %S. Will return nil.", S2_ObjectKindNameLookup[Object1.Kind], S2_ObjectKindNameLookup[Object2.Kind]);
S2_StackPush(Runtime, S2_MakeNil());
}
} break;
case S2_Op_Divide:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeNumber(Object2.As.Number/Object1.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to divide two incompatible types: %S & %S. Will return nil.", S2_ObjectKindNameLookup[Object1.Kind], S2_ObjectKindNameLookup[Object2.Kind]);
S2_StackPush(Runtime, S2_MakeNil());
}
} break;
case S2_Op_Equals:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Bool && Object2.Kind == S2_ObjectKind_Bool)
{
S2_StackPush(Runtime, S2_MakeBool(Object1.As.Bool == Object2.As.Bool));
}
else if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeBool(Object1.As.Number == Object2.As.Number));
}
else if(Object1.Kind == S2_ObjectKind_String && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeBool(DoubleFromString(Object1.As.String) == Object1.As.Number));
}
else if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_String)
{
S2_StackPush(Runtime, S2_MakeBool(Object1.As.Number == DoubleFromString(Object2.As.String)));
}
else
{
S2_StackPush(Runtime, S2_MakeBool(false));
}
} break;
case S2_Op_Greater:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeBool(Object2.As.Number > Object1.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to compare two incompatible types: %S & %S. Will return nil.", S2_ObjectKindNameLookup[Object1.Kind], S2_ObjectKindNameLookup[Object2.Kind]);
S2_StackPush(Runtime, S2_MakeBool(false));
}
} break;
case S2_Op_Less:
{
scene2_object Object1 = S2_StackPop(Runtime);
scene2_object Object2 = S2_StackPop(Runtime);
if(Object1.Kind == S2_ObjectKind_Number && Object2.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeBool(Object2.As.Number < Object1.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to compare two incompatible types: %S & %S. Will return nil.", S2_ObjectKindNameLookup[Object1.Kind], S2_ObjectKindNameLookup[Object2.Kind]);
S2_StackPush(Runtime, S2_MakeBool(false));
}
} break;
case S2_Op_Not:
{
scene2_object Object = S2_StackPop(Runtime);
S2_StackPush(Runtime, S2_MakeBool(!S2_ObjectIsTrue(Object)));
} break;
case S2_Op_Negate:
{
scene2_object Object = S2_StackPop(Runtime);
if(Object.Kind == S2_ObjectKind_Number)
{
S2_StackPush(Runtime, S2_MakeBool(-Object.As.Number));
}
else
{
S2_PushMessageF(Arena, &Result.Messages, T_EmptyToken, "Trying to negate invalid type: %S. Will return nil.", S2_ObjectKindNameLookup[Object.Kind]);
S2_StackPush(Runtime, S2_MakeNil());
}
} break;
}
Runtime->InstructionPointer += 1;
}
Done:;
return(Result);
}