mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
WIP: if-then-else, incomplete
This commit is contained in:
10
scripts/cond.toy
Normal file
10
scripts/cond.toy
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
if (true) {
|
||||
print "Success";
|
||||
}
|
||||
|
||||
else {
|
||||
print "Failed";
|
||||
}
|
||||
|
||||
@@ -105,6 +105,17 @@ void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, T
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
tmp->type = TOY_AST_IF_THEN_ELSE;
|
||||
tmp->ifThenElse.condBranch = condBranch;
|
||||
tmp->ifThenElse.thenBranch = thenBranch;
|
||||
tmp->ifThenElse.elseBranch = elseBranch;
|
||||
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_COMPOUND,
|
||||
|
||||
TOY_AST_ASSERT,
|
||||
TOY_AST_IF_THEN_ELSE,
|
||||
TOY_AST_PRINT,
|
||||
|
||||
TOY_AST_VAR_DECLARE,
|
||||
@@ -123,6 +124,13 @@ typedef struct Toy_AstAssert {
|
||||
Toy_Ast* message;
|
||||
} Toy_AstAssert;
|
||||
|
||||
typedef struct Toy_AstIfThenElse {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* condBranch;
|
||||
Toy_Ast* thenBranch;
|
||||
Toy_Ast* elseBranch;
|
||||
} Toy_AstIfThenElse;
|
||||
|
||||
typedef struct Toy_AstPrint {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* child;
|
||||
@@ -168,6 +176,7 @@ union Toy_Ast { //32 | 64 BITNESS
|
||||
Toy_AstGroup group; //8 | 16
|
||||
Toy_AstCompound compound; //16 | 24
|
||||
Toy_AstAssert assert; //16 | 24
|
||||
Toy_AstIfThenElse ifThenElse; //16 | 32
|
||||
Toy_AstPrint print; //8 | 16
|
||||
Toy_AstVarDeclare varDeclare; //16 | 24
|
||||
Toy_AstVarAssign varAssign; //16 | 24
|
||||
@@ -188,6 +197,7 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
|
||||
|
||||
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
|
||||
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr);
|
||||
|
||||
@@ -32,6 +32,7 @@ typedef enum Toy_OpcodeType {
|
||||
|
||||
//control instructions
|
||||
TOY_OPCODE_RETURN,
|
||||
TOY_OPCODE_JUMP,
|
||||
|
||||
TOY_OPCODE_SCOPE_PUSH,
|
||||
TOY_OPCODE_SCOPE_POP,
|
||||
@@ -48,3 +49,16 @@ typedef enum Toy_OpcodeType {
|
||||
TOY_OPCODE_ERROR,
|
||||
TOY_OPCODE_EOF = 255,
|
||||
} Toy_OpcodeType;
|
||||
|
||||
//specific opcode flags
|
||||
typedef enum Toy_OpParamJumpType {
|
||||
TOY_OP_PARAM_JUMP_ABSOLUTE = 0, //from the start of the routine's code section
|
||||
TOY_OP_PARAM_JUMP_RELATIVE = 1,
|
||||
} Toy_OpJumpType;
|
||||
|
||||
typedef enum Toy_OpParamJumpConditional {
|
||||
TOY_OP_PARAM_JUMP_ALWAYS = 0,
|
||||
TOY_OP_PARAM_JUMP_IF_TRUE = 1,
|
||||
TOY_OP_PARAM_JUMP_IF_FALSE = 2,
|
||||
} Toy_OpParamJumpConditional;
|
||||
|
||||
|
||||
@@ -655,6 +655,7 @@ static void makeExpr(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
|
||||
//forward declarations
|
||||
static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||
|
||||
static void makeAssertStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
Toy_Ast* ast = NULL; //assert's emit function is a bit different
|
||||
@@ -677,6 +678,27 @@ static void makeAssertStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of assert statement");
|
||||
}
|
||||
|
||||
static void makeIfThenElseStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
Toy_Ast* condBranch = NULL;
|
||||
Toy_Ast* thenBranch = NULL;
|
||||
Toy_Ast* elseBranch = NULL;
|
||||
|
||||
//if (condBranch)
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_LEFT, "Expected '(' after 'if' keyword");
|
||||
makeExpr(bucketHandle, parser, &condBranch);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'if' condition");
|
||||
|
||||
// { thenBranch }
|
||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch);
|
||||
|
||||
//else { elseBranch }
|
||||
if (match(parser, TOY_TOKEN_KEYWORD_ELSE)) {
|
||||
makeDeclarationStmt(bucketHandle, parser, &elseBranch);
|
||||
}
|
||||
|
||||
Toy_private_emitAstIfThenElse(bucketHandle, rootHandle, condBranch, thenBranch, elseBranch);
|
||||
}
|
||||
|
||||
static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
makeExpr(bucketHandle, parser, rootHandle);
|
||||
Toy_private_emitAstPrint(bucketHandle, rootHandle);
|
||||
@@ -737,12 +759,18 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
return;
|
||||
}
|
||||
|
||||
//assert
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_ASSERT)) {
|
||||
makeAssertStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//if-then-else
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_IF)) {
|
||||
makeIfThenElseStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//while-then
|
||||
//for-pre-clause-post-then
|
||||
//break
|
||||
@@ -750,17 +778,19 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
//return
|
||||
//import
|
||||
|
||||
//check for empty lines
|
||||
else if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
||||
Toy_private_emitAstPass(bucketHandle, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//print
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) {
|
||||
makePrintStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//empty lines
|
||||
else if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
||||
Toy_private_emitAstPass(bucketHandle, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//expressions
|
||||
else {
|
||||
//default
|
||||
makeExprStmt(bucketHandle, parser, rootHandle);
|
||||
|
||||
@@ -47,11 +47,22 @@ static void emitFloat(void** handle, unsigned int* capacity, unsigned int* count
|
||||
|
||||
//write instructions based on the AST types
|
||||
#define EMIT_BYTE(rt, part, byte) \
|
||||
emitByte((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &((*rt)->part##Count), byte);
|
||||
emitByte((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &((*rt)->part##Count), byte)
|
||||
#define EMIT_INT(rt, part, bytes) \
|
||||
emitInt((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &((*rt)->part##Count), bytes);
|
||||
emitInt((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &((*rt)->part##Count), bytes)
|
||||
#define EMIT_FLOAT(rt, part, bytes) \
|
||||
emitFloat((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &((*rt)->part##Count), bytes);
|
||||
emitFloat((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &((*rt)->part##Count), bytes)
|
||||
|
||||
//skip bytes, but return the address
|
||||
#define SKIP_BYTE(rt, part) (EMIT_BYTE(rt, part, 0), ((*rt)->part##Count - 1))
|
||||
#define SKIP_INT(rt, part) (EMIT_INT(rt, part, 0), ((*rt)->part##Count - 4))
|
||||
|
||||
//overwrite a pre-existing position
|
||||
#define OVERWRITE_INT(rt, part, addr, bytes) \
|
||||
emitInt((void**)(&((*rt)->part)), &((*rt)->part##Capacity), &(addr), bytes);
|
||||
|
||||
//simply get the address (always an integer)
|
||||
#define CURRENT_ADDRESS(rt, part) ((*rt)->part##Count)
|
||||
|
||||
static void emitToJumpTable(Toy_Routine** rt, unsigned int startAddr) {
|
||||
EMIT_INT(rt, code, (*rt)->jumpsCount); //mark the jump index in the code
|
||||
@@ -296,6 +307,48 @@ static unsigned int writeInstructionAssert(Toy_Routine** rt, Toy_AstAssert ast)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionIfThenElse(Toy_Routine** rt, Toy_AstIfThenElse ast) {
|
||||
//cond-branch
|
||||
writeRoutineCode(rt, ast.condBranch);
|
||||
|
||||
//emit the jump word (opcode, type, condition, padding)
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_JUMP);
|
||||
EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_RELATIVE);
|
||||
EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_IF_FALSE);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
unsigned int thenEndAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
|
||||
//emit then branch
|
||||
writeRoutineCode(rt, ast.thenBranch);
|
||||
|
||||
if (ast.elseBranch != NULL) {
|
||||
//emit the jump-to-end (opcode, type, condition, padding)
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_JUMP);
|
||||
EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_RELATIVE);
|
||||
EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_ALWAYS);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
unsigned int elseEndAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
|
||||
//specify the starting position for the else branch
|
||||
OVERWRITE_INT(rt, code, thenEndAddr, CURRENT_ADDRESS(rt, code) - thenEndAddr);
|
||||
|
||||
//emit the else branch
|
||||
writeRoutineCode(rt, ast.elseBranch);
|
||||
|
||||
//specify the ending position for the else branch
|
||||
OVERWRITE_INT(rt, code, elseEndAddr, CURRENT_ADDRESS(rt, code) - elseEndAddr);
|
||||
}
|
||||
|
||||
else {
|
||||
//without an else branch, set the jump destination and move on
|
||||
OVERWRITE_INT(rt, code, thenEndAddr, CURRENT_ADDRESS(rt, code) - thenEndAddr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) {
|
||||
//the thing to print
|
||||
writeRoutineCode(rt, ast.child);
|
||||
@@ -494,6 +547,7 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//NOTE: 'result' is used to in 'writeInstructionCompound()'
|
||||
unsigned int result = 0;
|
||||
|
||||
//determine how to write each instruction based on the Ast
|
||||
@@ -545,6 +599,10 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
result += writeInstructionAssert(rt, ast->assert);
|
||||
break;
|
||||
|
||||
case TOY_AST_IF_THEN_ELSE:
|
||||
result += writeInstructionIfThenElse(rt, ast->ifThenElse);
|
||||
break;
|
||||
|
||||
case TOY_AST_PRINT:
|
||||
result += writeInstructionPrint(rt, ast->print);
|
||||
break;
|
||||
@@ -596,7 +654,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
|
||||
//write the header and combine the parts
|
||||
void* buffer = NULL;
|
||||
unsigned int capacity = 0, count = 0;
|
||||
// int paramAddr = 0, codeAddr = 0, subsAddr = 0;
|
||||
// int paramAddr = 0, subsAddr = 0;
|
||||
int codeAddr = 0;
|
||||
int jumpsAddr = 0;
|
||||
int dataAddr = 0;
|
||||
|
||||
@@ -31,7 +31,7 @@ static inline int readPostfixUtil(unsigned int* ptr, int amount) {
|
||||
|
||||
static inline void fixAlignment(Toy_VM* vm) {
|
||||
//NOTE: It's a tilde, not a negative sign
|
||||
vm->programCounter = (vm->programCounter + 3) & ~0b11;
|
||||
vm->programCounter = (vm->programCounter + 3) & ~3;
|
||||
}
|
||||
|
||||
//instruction handlers
|
||||
@@ -361,6 +361,52 @@ static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
static void processJump(Toy_VM* vm) {
|
||||
Toy_OpJumpType type = READ_BYTE(vm);
|
||||
Toy_OpParamJumpConditional cond = READ_BYTE(vm);
|
||||
fixAlignment(vm);
|
||||
|
||||
//assume the param is a signed integer
|
||||
int param = READ_INT(vm);
|
||||
|
||||
//should we jump?
|
||||
switch(cond) {
|
||||
case TOY_OP_PARAM_JUMP_ALWAYS:
|
||||
break;
|
||||
|
||||
case TOY_OP_PARAM_JUMP_IF_TRUE: {
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
if (Toy_checkValueIsTruthy(value) == true) {
|
||||
Toy_freeValue(value);
|
||||
break;
|
||||
}
|
||||
Toy_freeValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOY_OP_PARAM_JUMP_IF_FALSE: {
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
if (Toy_checkValueIsTruthy(value) != true) {
|
||||
Toy_freeValue(value);
|
||||
break;
|
||||
}
|
||||
Toy_freeValue(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//do the jump
|
||||
switch(type) {
|
||||
case TOY_OP_PARAM_JUMP_ABSOLUTE:
|
||||
vm->programCounter = vm->codeAddr + param;
|
||||
return;
|
||||
|
||||
case TOY_OP_PARAM_JUMP_RELATIVE:
|
||||
vm->programCounter += param;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void processAssert(Toy_VM* vm) {
|
||||
unsigned int count = READ_BYTE(vm);
|
||||
|
||||
@@ -551,6 +597,10 @@ static void process(Toy_VM* vm) {
|
||||
//temp terminator
|
||||
return;
|
||||
|
||||
case TOY_OPCODE_JUMP:
|
||||
processJump(vm);
|
||||
break;
|
||||
|
||||
case TOY_OPCODE_SCOPE_PUSH:
|
||||
vm->scope = Toy_pushScope(&vm->scopeBucket, vm->scope);
|
||||
break;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
int test_sizeof_ast_64bit() {
|
||||
//NOTE: This could've covered both bitness sizes as TEST_SIZEOF(type, bit32, bit32)
|
||||
#define TEST_SIZEOF(type, size) \
|
||||
if (sizeof(type) != size) { \
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: sizeof(" #type ") is %d, expected %d\n" TOY_CC_RESET, (int)sizeof(type), size); \
|
||||
@@ -24,6 +25,7 @@ int test_sizeof_ast_64bit() {
|
||||
TEST_SIZEOF(Toy_AstGroup, 16);
|
||||
TEST_SIZEOF(Toy_AstCompound, 24);
|
||||
TEST_SIZEOF(Toy_AstAssert, 24);
|
||||
TEST_SIZEOF(Toy_AstIfThenElse, 32);
|
||||
TEST_SIZEOF(Toy_AstPrint, 16);
|
||||
TEST_SIZEOF(Toy_AstVarDeclare, 24);
|
||||
TEST_SIZEOF(Toy_AstVarAssign, 24);
|
||||
@@ -58,6 +60,7 @@ int test_sizeof_ast_32bit() {
|
||||
TEST_SIZEOF(Toy_AstGroup, 8);
|
||||
TEST_SIZEOF(Toy_AstCompound, 16);
|
||||
TEST_SIZEOF(Toy_AstAssert, 12);
|
||||
TEST_SIZEOF(Toy_AstIfThenElse, 16);
|
||||
TEST_SIZEOF(Toy_AstPrint, 8);
|
||||
TEST_SIZEOF(Toy_AstVarDeclare, 12);
|
||||
TEST_SIZEOF(Toy_AstVarAssign, 16);
|
||||
|
||||
Reference in New Issue
Block a user