mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
@@ -36,7 +36,8 @@ typedef enum Toy_OpcodeType {
|
||||
|
||||
//control instructions
|
||||
TOY_OPCODE_RETURN,
|
||||
TOY_OPCODE_JUMP,
|
||||
TOY_OPCODE_JUMP, //JUMP, ADDR
|
||||
TOY_OPCODE_ESCAPE, //JUMP, ADDR, UNWIND
|
||||
|
||||
TOY_OPCODE_SCOPE_PUSH,
|
||||
TOY_OPCODE_SCOPE_POP,
|
||||
@@ -57,7 +58,7 @@ typedef enum Toy_OpcodeType {
|
||||
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;
|
||||
} Toy_OpParamJumpType;
|
||||
|
||||
typedef enum Toy_OpParamJumpConditional {
|
||||
TOY_OP_PARAM_JUMP_ALWAYS = 0,
|
||||
|
||||
@@ -9,6 +9,30 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//escapes
|
||||
void* Toy_private_resizeEscapeArray(Toy_private_EscapeArray* ptr, unsigned int capacity) {
|
||||
//if you're freeing everything, just return
|
||||
if (capacity == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int originalCapacity = ptr == NULL ? 0 : ptr->capacity;
|
||||
unsigned int orignalCount = ptr == NULL ? 0 : ptr->count;
|
||||
|
||||
ptr = (Toy_private_EscapeArray*)realloc(ptr, capacity * sizeof(Toy_private_EscapeEntry_t) + sizeof(Toy_private_EscapeArray));
|
||||
|
||||
if (ptr == NULL) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to resize an escape array within 'Toy_Routine' from %d to %d capacity\n" TOY_CC_RESET, (int)originalCapacity, (int)capacity);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
ptr->capacity = capacity;
|
||||
ptr->count = orignalCount;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
//utils
|
||||
static void expand(unsigned char** handle, unsigned int* capacity, unsigned int* count, unsigned int amount) {
|
||||
if ((*count) + amount > (*capacity)) {
|
||||
@@ -240,7 +264,7 @@ static unsigned int writeInstructionBinaryShortCircuit(Toy_Routine** rt, Toy_Ast
|
||||
}
|
||||
|
||||
//parameter address
|
||||
unsigned int endAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
unsigned int paramAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
|
||||
//if the lhs value isn't needed, pop it
|
||||
EMIT_BYTE(rt, code,TOY_OPCODE_ELIMINATE);
|
||||
@@ -252,7 +276,7 @@ static unsigned int writeInstructionBinaryShortCircuit(Toy_Routine** rt, Toy_Ast
|
||||
writeRoutineCode(rt, ast.right);
|
||||
|
||||
//set the parameter
|
||||
OVERWRITE_INT(rt, code, endAddr, CURRENT_ADDRESS(rt, code) - (endAddr + 4));
|
||||
OVERWRITE_INT(rt, code, paramAddr, CURRENT_ADDRESS(rt, code) - (paramAddr + 4));
|
||||
|
||||
return 1; //leaves only 1 value on the stack
|
||||
}
|
||||
@@ -401,7 +425,7 @@ static unsigned int writeInstructionIfThenElse(Toy_Routine** rt, Toy_AstIfThenEl
|
||||
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
|
||||
unsigned int thenParamAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
|
||||
//emit then-branch
|
||||
writeRoutineCode(rt, ast.thenBranch);
|
||||
@@ -413,21 +437,21 @@ static unsigned int writeInstructionIfThenElse(Toy_Routine** rt, Toy_AstIfThenEl
|
||||
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
|
||||
unsigned int elseParamAddr = 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 + 4));
|
||||
OVERWRITE_INT(rt, code, thenParamAddr, CURRENT_ADDRESS(rt, code) - (thenParamAddr + 4));
|
||||
|
||||
//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 + 4));
|
||||
OVERWRITE_INT(rt, code, elseParamAddr, CURRENT_ADDRESS(rt, code) - (elseParamAddr + 4));
|
||||
}
|
||||
|
||||
else {
|
||||
//without an else branch, set the jump destination and move on
|
||||
OVERWRITE_INT(rt, code, thenEndAddr, CURRENT_ADDRESS(rt, code) - (thenEndAddr + 4));
|
||||
OVERWRITE_INT(rt, code, thenParamAddr, CURRENT_ADDRESS(rt, code) - (thenParamAddr + 4));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -446,7 +470,7 @@ static unsigned int writeInstructionWhileThen(Toy_Routine** rt, Toy_AstWhileThen
|
||||
EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_IF_FALSE);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
unsigned int endAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
unsigned int paramAddr = SKIP_INT(rt, code); //parameter to be written later
|
||||
|
||||
//emit then-branch
|
||||
writeRoutineCode(rt, ast.thenBranch);
|
||||
@@ -459,25 +483,85 @@ static unsigned int writeInstructionWhileThen(Toy_Routine** rt, Toy_AstWhileThen
|
||||
|
||||
EMIT_INT(rt, code, beginAddr - (CURRENT_ADDRESS(rt, code) + 4)); //this sets a negative value
|
||||
|
||||
OVERWRITE_INT(rt, code, endAddr, CURRENT_ADDRESS(rt, code) - (endAddr + 4));
|
||||
//set the exit parameter for the cond
|
||||
OVERWRITE_INT(rt, code, paramAddr, CURRENT_ADDRESS(rt, code) - (paramAddr + 4));
|
||||
|
||||
//set the break & continue data
|
||||
while ((*rt)->breakEscapes->count > 0) {
|
||||
//extract
|
||||
unsigned int addr = (*rt)->breakEscapes->data[(*rt)->breakEscapes->count - 1].addr;
|
||||
unsigned int depth = (*rt)->breakEscapes->data[(*rt)->breakEscapes->count - 1].depth;
|
||||
|
||||
unsigned int diff = depth - (*rt)->currentScopeDepth;
|
||||
|
||||
OVERWRITE_INT(rt, code, addr, CURRENT_ADDRESS(rt, code) - (addr + 8)); //tell break to come here AFTER reading the instruction
|
||||
OVERWRITE_INT(rt, code, addr, diff);
|
||||
|
||||
//tick down
|
||||
(*rt)->breakEscapes->count--;
|
||||
}
|
||||
|
||||
while ((*rt)->continueEscapes->count > 0) {
|
||||
//extract
|
||||
unsigned int addr = (*rt)->continueEscapes->data[(*rt)->continueEscapes->count - 1].addr;
|
||||
unsigned int depth = (*rt)->continueEscapes->data[(*rt)->continueEscapes->count - 1].depth;
|
||||
|
||||
unsigned int diff = depth - (*rt)->currentScopeDepth;
|
||||
|
||||
OVERWRITE_INT(rt, code, addr, addr - (CURRENT_ADDRESS(rt, code) + 8)); //tell continue to return to the start AFTER reading the instruction
|
||||
OVERWRITE_INT(rt, code, addr, diff);
|
||||
|
||||
//tick down
|
||||
(*rt)->continueEscapes->count--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionBreak(Toy_Routine** rt, Toy_AstBreak ast) {
|
||||
//TODO: implement break
|
||||
//unused
|
||||
(void)ast;
|
||||
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Keyword 'break' not yet implemented\n" TOY_CC_RESET);
|
||||
(*rt)->panic = true;
|
||||
|
||||
//escapes are always relative
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_ESCAPE);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
unsigned int addr = SKIP_INT(rt, code);
|
||||
(void)SKIP_INT(rt, code); //empty space for depth
|
||||
|
||||
//expand the escape array if needed
|
||||
if ((*rt)->breakEscapes->capacity <= (*rt)->breakEscapes->count) {
|
||||
(*rt)->breakEscapes = Toy_private_resizeEscapeArray((*rt)->breakEscapes, (*rt)->breakEscapes->capacity * TOY_ESCAPE_EXPANSION_RATE);
|
||||
}
|
||||
|
||||
//store for later
|
||||
(*rt)->breakEscapes->data[(*rt)->breakEscapes->count++] = (Toy_private_EscapeEntry_t){ .addr = addr, .depth = (*rt)->currentScopeDepth };
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionContinue(Toy_Routine** rt, Toy_AstContinue ast) {
|
||||
//TODO: implement continue
|
||||
//unused
|
||||
(void)ast;
|
||||
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Keyword 'continue' not yet implemented\n" TOY_CC_RESET);
|
||||
(*rt)->panic = true;
|
||||
|
||||
//escapes are always relative
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_ESCAPE);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
unsigned int addr = SKIP_INT(rt, code);
|
||||
(void)SKIP_INT(rt, code); //empty space for depth
|
||||
|
||||
//expand the escape array if needed
|
||||
if ((*rt)->continueEscapes->capacity <= (*rt)->continueEscapes->count) {
|
||||
(*rt)->continueEscapes = Toy_private_resizeEscapeArray((*rt)->continueEscapes, (*rt)->continueEscapes->capacity * TOY_ESCAPE_EXPANSION_RATE);
|
||||
}
|
||||
|
||||
//store for later
|
||||
(*rt)->continueEscapes->data[(*rt)->continueEscapes->count++] = (Toy_private_EscapeEntry_t){ .addr = addr, .depth = (*rt)->currentScopeDepth };
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -700,6 +784,8 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
(*rt)->currentScopeDepth++;
|
||||
}
|
||||
|
||||
result += writeRoutineCode(rt, ast->block.child);
|
||||
@@ -710,6 +796,8 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
(*rt)->currentScopeDepth--;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -909,12 +997,19 @@ void* Toy_compileRoutine(Toy_Ast* ast) {
|
||||
rt.subsCapacity = 0;
|
||||
rt.subsCount = 0;
|
||||
|
||||
rt.currentScopeDepth = 0;
|
||||
rt.breakEscapes = Toy_private_resizeEscapeArray(NULL, TOY_ESCAPE_INITIAL_CAPACITY);
|
||||
rt.continueEscapes = Toy_private_resizeEscapeArray(NULL, TOY_ESCAPE_INITIAL_CAPACITY);
|
||||
|
||||
rt.panic = false;
|
||||
|
||||
//build
|
||||
void * buffer = writeRoutine(&rt, ast);
|
||||
|
||||
//cleanup the temp object
|
||||
//cleanup
|
||||
Toy_private_resizeEscapeArray(rt.breakEscapes, 0);
|
||||
Toy_private_resizeEscapeArray(rt.continueEscapes, 0);
|
||||
|
||||
free(rt.param);
|
||||
free(rt.code);
|
||||
free(rt.jumps);
|
||||
|
||||
@@ -3,6 +3,29 @@
|
||||
#include "toy_common.h"
|
||||
#include "toy_ast.h"
|
||||
|
||||
//the 'escapes' are lists of data used for processing the 'break' and 'continue' keywords, and can be safely ignored
|
||||
typedef struct Toy_private_EscapeEntry_t {
|
||||
unsigned int addr; //the address to write *to*
|
||||
unsigned int depth; //the current depth
|
||||
} Toy_private_EscapeEntry_t;
|
||||
|
||||
typedef struct Toy_private_EscapeArray {
|
||||
unsigned int capacity;
|
||||
unsigned int count;
|
||||
Toy_private_EscapeEntry_t data[];
|
||||
} Toy_private_EscapeArray;
|
||||
|
||||
//not needed at runtime, so they can be bigger
|
||||
#ifndef TOY_ESCAPE_INITIAL_CAPACITY
|
||||
#define TOY_ESCAPE_INITIAL_CAPACITY 32
|
||||
#endif
|
||||
|
||||
#ifndef TOY_ESCAPE_EXPANSION_RATE
|
||||
#define TOY_ESCAPE_EXPANSION_RATE 4
|
||||
#endif
|
||||
|
||||
TOY_API void* Toy_private_resizeEscapeArray(Toy_private_EscapeArray* ptr, unsigned int capacity);
|
||||
|
||||
//internal structure that holds the individual parts of a compiled routine
|
||||
typedef struct Toy_Routine {
|
||||
unsigned char* param; //c-string params in sequence (could be moved below the jump table?)
|
||||
@@ -25,8 +48,11 @@ typedef struct Toy_Routine {
|
||||
unsigned int subsCapacity;
|
||||
unsigned int subsCount;
|
||||
|
||||
unsigned int currentScopeDepth;
|
||||
Toy_private_EscapeArray* breakEscapes;
|
||||
Toy_private_EscapeArray* continueEscapes;
|
||||
|
||||
bool panic; //any issues found at this point are compilation errors
|
||||
} Toy_Routine;
|
||||
|
||||
TOY_API void* Toy_compileRoutine(Toy_Ast* ast);
|
||||
|
||||
|
||||
@@ -84,22 +84,22 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_OPERATOR_BRACE_RIGHT,
|
||||
|
||||
//other operators
|
||||
TOY_TOKEN_OPERATOR_AND,
|
||||
TOY_TOKEN_OPERATOR_OR,
|
||||
TOY_TOKEN_OPERATOR_NEGATE,
|
||||
TOY_TOKEN_OPERATOR_QUESTION,
|
||||
TOY_TOKEN_OPERATOR_COLON,
|
||||
TOY_TOKEN_OPERATOR_AND, // &&
|
||||
TOY_TOKEN_OPERATOR_OR, // ||
|
||||
TOY_TOKEN_OPERATOR_NEGATE, // !
|
||||
TOY_TOKEN_OPERATOR_QUESTION, // ?
|
||||
TOY_TOKEN_OPERATOR_COLON, // :
|
||||
|
||||
TOY_TOKEN_OPERATOR_SEMICOLON,
|
||||
TOY_TOKEN_OPERATOR_COMMA,
|
||||
TOY_TOKEN_OPERATOR_SEMICOLON, // ;
|
||||
TOY_TOKEN_OPERATOR_COMMA, // ,
|
||||
|
||||
TOY_TOKEN_OPERATOR_DOT, // .
|
||||
TOY_TOKEN_OPERATOR_CONCAT, // ..
|
||||
TOY_TOKEN_OPERATOR_REST, // ...
|
||||
TOY_TOKEN_OPERATOR_DOT, // .
|
||||
TOY_TOKEN_OPERATOR_CONCAT, // ..
|
||||
TOY_TOKEN_OPERATOR_REST, // ...
|
||||
|
||||
//unused operators
|
||||
TOY_TOKEN_OPERATOR_AMPERSAND, // &
|
||||
TOY_TOKEN_OPERATOR_PIPE, // |
|
||||
TOY_TOKEN_OPERATOR_PIPE, // |
|
||||
|
||||
//meta tokens
|
||||
TOY_TOKEN_PASS,
|
||||
|
||||
@@ -63,7 +63,7 @@ static void processRead(Toy_VM* vm) {
|
||||
|
||||
case TOY_VALUE_STRING: {
|
||||
enum Toy_StringType stringType = READ_BYTE(vm);
|
||||
int len = (int)READ_BYTE(vm);
|
||||
int len = (int)READ_BYTE(vm); //only needed for name strings
|
||||
|
||||
//grab the jump as an integer
|
||||
unsigned int jump = *((int*)(vm->module + vm->jumpsAddr + READ_INT(vm)));
|
||||
@@ -524,7 +524,7 @@ static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
}
|
||||
|
||||
static void processJump(Toy_VM* vm) {
|
||||
Toy_OpJumpType type = READ_BYTE(vm);
|
||||
Toy_OpParamJumpType type = READ_BYTE(vm);
|
||||
Toy_OpParamJumpConditional cond = READ_BYTE(vm);
|
||||
fixAlignment(vm);
|
||||
|
||||
@@ -571,6 +571,19 @@ static void processJump(Toy_VM* vm) {
|
||||
}
|
||||
}
|
||||
|
||||
static void processEscape(Toy_VM* vm) {
|
||||
fixAlignment(vm);
|
||||
|
||||
int addr = READ_INT(vm); //where to go
|
||||
int diff = READ_INT(vm); //what to do
|
||||
|
||||
vm->programCounter += addr;
|
||||
|
||||
while (diff > 0 && vm->scope != NULL) {
|
||||
vm->scope = Toy_popScope(vm->scope);
|
||||
}
|
||||
}
|
||||
|
||||
static void processAssert(Toy_VM* vm) {
|
||||
unsigned int count = READ_BYTE(vm);
|
||||
|
||||
@@ -881,6 +894,10 @@ static void process(Toy_VM* vm) {
|
||||
processJump(vm);
|
||||
break;
|
||||
|
||||
case TOY_OPCODE_ESCAPE:
|
||||
processEscape(vm);
|
||||
break;
|
||||
|
||||
case TOY_OPCODE_SCOPE_PUSH:
|
||||
vm->scope = Toy_pushScope(&vm->scopeBucket, vm->scope);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user