1025 lines
38 KiB
C++
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);
|
|
} |