Keywords 'break' and 'continue' are working, untested

See #152
This commit is contained in:
2024-12-30 09:50:51 +11:00
parent 6d25beea03
commit b84a70cc34
7 changed files with 252 additions and 32 deletions

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;