mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
I added reference values in 62ca7a1fb7,
but forgot to mention it. I'm now using references to assign to the
internals of an array, no matter how many levels deep it is.
860 lines
24 KiB
C
860 lines
24 KiB
C
#include "toy_routine.h"
|
|
#include "toy_console_colors.h"
|
|
|
|
#include "toy_opcodes.h"
|
|
#include "toy_value.h"
|
|
#include "toy_string.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
//utils
|
|
static void expand(void** handle, unsigned int* capacity, unsigned int* count, unsigned int amount) {
|
|
if ((*count) + amount > (*capacity)) {
|
|
while ((*count) + amount > (*capacity)) {
|
|
(*capacity) = (*capacity) < 8 ? 8 : (*capacity) * 2;
|
|
}
|
|
(*handle) = realloc((*handle), (*capacity));
|
|
|
|
if ((*handle) == NULL) {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate %d space for a part of 'Toy_Routine'\n" TOY_CC_RESET, (int)(*capacity));
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void emitByte(void** handle, unsigned int* capacity, unsigned int* count, unsigned char byte) {
|
|
expand(handle, capacity, count, 1);
|
|
((unsigned char*)(*handle))[(*count)++] = byte;
|
|
}
|
|
|
|
static void emitInt(void** handle, unsigned int* capacity, unsigned int* count, unsigned int bytes) {
|
|
char* ptr = (char*)&bytes;
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
}
|
|
|
|
static void emitFloat(void** handle, unsigned int* capacity, unsigned int* count, float bytes) {
|
|
char* ptr = (char*)&bytes;
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
emitByte(handle, capacity, count, *(ptr++));
|
|
}
|
|
|
|
//write instructions based on the AST types
|
|
#define EMIT_BYTE(rt, part, 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)
|
|
#define EMIT_FLOAT(rt, part, 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
|
|
EMIT_INT(rt, jumps, startAddr); //save address at the jump index
|
|
}
|
|
|
|
static unsigned int emitString(Toy_Routine** rt, Toy_String* str) {
|
|
//4-byte alignment
|
|
unsigned int length = str->length + 1;
|
|
if (length % 4 != 0) {
|
|
length += 4 - (length % 4); //ceil
|
|
}
|
|
|
|
//grab the current start address
|
|
unsigned int startAddr = (*rt)->dataCount;
|
|
|
|
//move the string into the data section
|
|
expand((void**)(&((*rt)->data)), &((*rt)->dataCapacity), &((*rt)->dataCount), length);
|
|
|
|
if (str->type == TOY_STRING_NODE) {
|
|
char* buffer = Toy_getStringRawBuffer(str);
|
|
memcpy((*rt)->data + (*rt)->dataCount, buffer, str->length + 1);
|
|
free(buffer);
|
|
}
|
|
else if (str->type == TOY_STRING_LEAF) {
|
|
memcpy((*rt)->data + (*rt)->dataCount, str->as.leaf.data, str->length + 1);
|
|
}
|
|
else if (str->type == TOY_STRING_NAME) {
|
|
memcpy((*rt)->data + (*rt)->dataCount, str->as.name.data, str->length + 1);
|
|
}
|
|
|
|
(*rt)->dataCount += length;
|
|
|
|
//mark the jump position
|
|
emitToJumpTable(rt, startAddr);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast); //forward declare for recursion
|
|
|
|
static unsigned int writeInstructionValue(Toy_Routine** rt, Toy_AstValue ast) {
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
|
|
EMIT_BYTE(rt, code, ast.value.type);
|
|
|
|
//emit the raw value based on the type
|
|
if (TOY_VALUE_IS_NULL(ast.value)) {
|
|
//NOTHING - null's type data is enough
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
}
|
|
else if (TOY_VALUE_IS_BOOLEAN(ast.value)) {
|
|
EMIT_BYTE(rt, code, TOY_VALUE_AS_BOOLEAN(ast.value));
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, 0);
|
|
}
|
|
else if (TOY_VALUE_IS_INTEGER(ast.value)) {
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
|
|
EMIT_INT(rt, code, TOY_VALUE_AS_INTEGER(ast.value));
|
|
}
|
|
else if (TOY_VALUE_IS_FLOAT(ast.value)) {
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
|
|
EMIT_FLOAT(rt, code, TOY_VALUE_AS_FLOAT(ast.value));
|
|
}
|
|
else if (TOY_VALUE_IS_STRING(ast.value)) {
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, TOY_STRING_LEAF); //normal string
|
|
EMIT_BYTE(rt, code, 0); //can't store the length
|
|
|
|
return emitString(rt, TOY_VALUE_AS_STRING(ast.value));
|
|
}
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST type found: Unknown value type\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static unsigned int writeInstructionUnary(Toy_Routine** rt, Toy_AstUnary ast) {
|
|
//working with a stack means the child gets placed first
|
|
unsigned int result = writeRoutineCode(rt, ast.child);
|
|
|
|
if (ast.flag == TOY_AST_FLAG_NEGATE) {
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_NEGATE);
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
}
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST unary flag found\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static unsigned int writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) {
|
|
//left, then right, then the binary's operation
|
|
writeRoutineCode(rt, ast.left);
|
|
writeRoutineCode(rt, ast.right);
|
|
|
|
if (ast.flag == TOY_AST_FLAG_ADD) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ADD);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_SUBTRACT) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_SUBTRACT);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_MULTIPLY) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_MULTIPLY);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_DIVIDE) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DIVIDE);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_MODULO) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_MODULO);
|
|
}
|
|
|
|
//nowhere to really put these for now
|
|
else if (ast.flag == TOY_AST_FLAG_AND) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_AND);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_OR) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_OR);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_CONCAT) {
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_CONCAT);
|
|
}
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST binary flag found\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_PASS); //checked in combined assignments
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 1; //leaves only 1 value on the stack
|
|
}
|
|
|
|
static unsigned int writeInstructionCompare(Toy_Routine** rt, Toy_AstCompare ast) {
|
|
//left, then right, then the compare's operation
|
|
writeRoutineCode(rt, ast.left);
|
|
writeRoutineCode(rt, ast.right);
|
|
|
|
if (ast.flag == TOY_AST_FLAG_COMPARE_EQUAL) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_EQUAL);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_COMPARE_NOT) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_EQUAL);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_NEGATE); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 1;
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_COMPARE_LESS) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_LESS);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_COMPARE_LESS_EQUAL) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_LESS_EQUAL);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_COMPARE_GREATER) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_GREATER);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_COMPARE_GREATER_EQUAL) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_GREATER_EQUAL);
|
|
}
|
|
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compare flag found\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
//4-byte alignment (covers most cases)
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 1; //leaves only 1 value on the stack
|
|
}
|
|
|
|
static unsigned int writeInstructionGroup(Toy_Routine** rt, Toy_AstGroup ast) {
|
|
//not certain what this leaves
|
|
return writeRoutineCode(rt, ast.child);
|
|
}
|
|
|
|
static unsigned int writeInstructionCompound(Toy_Routine** rt, Toy_AstCompound ast) {
|
|
unsigned int result = writeRoutineCode(rt, ast.child);
|
|
|
|
if (ast.flag == TOY_AST_FLAG_COMPOUND_ARRAY) {
|
|
//signal how many values to read in as an array value
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
|
|
EMIT_BYTE(rt, code, TOY_VALUE_ARRAY);
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
//how many elements
|
|
EMIT_INT(rt, code, result);
|
|
|
|
return 1; //leaves only 1 value on the stack
|
|
}
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compound flag found\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static unsigned int writeInstructionAggregate(Toy_Routine** rt, Toy_AstAggregate ast) {
|
|
unsigned int result = 0;
|
|
|
|
//left, then right
|
|
result += writeRoutineCode(rt, ast.left);
|
|
result += writeRoutineCode(rt, ast.right);
|
|
|
|
if (ast.flag == TOY_AST_FLAG_COLLECTION) {
|
|
//collections are handled above
|
|
return result;
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_INDEX) {
|
|
//value[index, length]
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_INDEX);
|
|
EMIT_BYTE(rt, code, result);
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 1; //leaves only 1 value on the stack
|
|
}
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST aggregate flag found\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static unsigned int writeInstructionAssert(Toy_Routine** rt, Toy_AstAssert ast) {
|
|
//the thing to print
|
|
writeRoutineCode(rt, ast.child);
|
|
writeRoutineCode(rt, ast.message);
|
|
|
|
//output the print opcode
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_ASSERT);
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code, ast.message != NULL ? 2 : 1); //arg count
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
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 + 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));
|
|
}
|
|
|
|
else {
|
|
//without an else branch, set the jump destination and move on
|
|
OVERWRITE_INT(rt, code, thenEndAddr, CURRENT_ADDRESS(rt, code) - (thenEndAddr + 4));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int writeInstructionWhileThen(Toy_Routine** rt, Toy_AstWhileThen ast) {
|
|
//TODO: begin
|
|
unsigned int beginAddr = CURRENT_ADDRESS(rt, code);
|
|
|
|
//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 endAddr = SKIP_INT(rt, code); //parameter to be written later
|
|
|
|
//emit then-branch
|
|
writeRoutineCode(rt, ast.thenBranch);
|
|
|
|
//jump to begin to repeat the conditional test
|
|
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);
|
|
|
|
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));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int writeInstructionBreak(Toy_Routine** rt, Toy_AstBreak ast) {
|
|
//TODO: implement break
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Keyword 'break' not yet implemented\n" TOY_CC_RESET);
|
|
(*rt)->panic = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int writeInstructionContinue(Toy_Routine** rt, Toy_AstContinue ast) {
|
|
//TODO: implement continue
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Keyword 'continue' not yet implemented\n" TOY_CC_RESET);
|
|
(*rt)->panic = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) {
|
|
//the thing to print
|
|
writeRoutineCode(rt, ast.child);
|
|
|
|
//output the print opcode
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_PRINT);
|
|
|
|
//4-byte alignment
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int writeInstructionVarDeclare(Toy_Routine** rt, Toy_AstVarDeclare ast) {
|
|
//initial value
|
|
writeRoutineCode(rt, ast.expr);
|
|
|
|
//delcare with the given name string
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_DECLARE);
|
|
EMIT_BYTE(rt, code, Toy_getNameStringType(ast.name));
|
|
EMIT_BYTE(rt, code, ast.name->length); //quick optimisation to skip a 'strlen()' call
|
|
EMIT_BYTE(rt, code, Toy_getNameStringConstant(ast.name) ? 1 : 0); //check for constness
|
|
|
|
emitString(rt, ast.name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign ast) {
|
|
unsigned int result = 0;
|
|
|
|
//don't treat these as valid values
|
|
switch (ast.expr->type) {
|
|
case TOY_AST_BLOCK:
|
|
case TOY_AST_AGGREGATE:
|
|
case TOY_AST_ASSERT:
|
|
case TOY_AST_PRINT:
|
|
case TOY_AST_VAR_DECLARE:
|
|
//emit a compiler error, set the panic flag and skip out
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Malformed assignment value\n" TOY_CC_RESET);
|
|
(*rt)->panic = true;
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//target is a name string
|
|
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value) && TOY_VALUE_AS_STRING(ast.target->value.value)->type == TOY_STRING_NAME) {
|
|
//name string
|
|
Toy_String* target = TOY_VALUE_AS_STRING(ast.target->value.value);
|
|
|
|
//emit the name string
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
|
|
EMIT_BYTE(rt, code, TOY_VALUE_STRING);
|
|
EMIT_BYTE(rt, code, TOY_STRING_NAME);
|
|
EMIT_BYTE(rt, code, target->length); //store the length (max 255)
|
|
|
|
emitString(rt, target);
|
|
}
|
|
|
|
//target is an indexing of some compound value
|
|
else if (ast.target->type == TOY_AST_AGGREGATE && ast.target->aggregate.flag == TOY_AST_FLAG_INDEX) {
|
|
writeRoutineCode(rt, ast.target->aggregate.left); //any deeper indexing will just work, using reference values
|
|
writeRoutineCode(rt, ast.target->aggregate.right); //key
|
|
writeRoutineCode(rt, ast.expr); //value
|
|
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_ASSIGN_COMPOUND); //uses the top three values on the stack
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
else {
|
|
//unknown target
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Malformed assignment target\n" TOY_CC_RESET);
|
|
(*rt)->panic = true;
|
|
return 0;
|
|
}
|
|
|
|
//determine RHS, include duplication if needed
|
|
if (ast.flag == TOY_AST_FLAG_ASSIGN) {
|
|
result += writeRoutineCode(rt, ast.expr);
|
|
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_ASSIGN);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_ADD_ASSIGN) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ACCESS); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
result += writeRoutineCode(rt, ast.expr);
|
|
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ADD);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_SUBTRACT_ASSIGN) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ACCESS); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
result += writeRoutineCode(rt, ast.expr);
|
|
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_SUBTRACT);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_MULTIPLY_ASSIGN) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ACCESS); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
result += writeRoutineCode(rt, ast.expr);
|
|
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_MULTIPLY);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_DIVIDE_ASSIGN) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ACCESS); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
result += writeRoutineCode(rt, ast.expr);
|
|
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DIVIDE);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
}
|
|
else if (ast.flag == TOY_AST_FLAG_MODULO_ASSIGN) {
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ACCESS); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
result += writeRoutineCode(rt, ast.expr);
|
|
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_MODULO);
|
|
EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
}
|
|
|
|
else {
|
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST assign flag found\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static unsigned int writeInstructionAccess(Toy_Routine** rt, Toy_AstVarAccess ast) {
|
|
if (!(ast.child->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.child->value.value) && TOY_VALUE_AS_STRING(ast.child->value.value)->type == TOY_STRING_NAME)) {
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Found a non-name-string in a value node when trying to write access\n" TOY_CC_RESET);
|
|
exit(-1);
|
|
}
|
|
|
|
Toy_String* name = TOY_VALUE_AS_STRING(ast.child->value.value);
|
|
|
|
//push the name
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_READ);
|
|
EMIT_BYTE(rt, code, TOY_VALUE_STRING);
|
|
EMIT_BYTE(rt, code, TOY_STRING_NAME);
|
|
EMIT_BYTE(rt, code, name->length); //store the length (max 255)
|
|
|
|
emitString(rt, name);
|
|
|
|
//convert name to value
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_ACCESS);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
EMIT_BYTE(rt, code,0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//routine structure
|
|
// static void writeRoutineParam(Toy_Routine* rt) {
|
|
// //
|
|
// }
|
|
|
|
static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
|
if (ast == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
//if an error occured, just exit
|
|
if (rt == NULL || (*rt) == NULL || (*rt)->panic) {
|
|
return 0;
|
|
}
|
|
|
|
//NOTE: 'result' is used to in 'writeInstructionAggregate()'
|
|
unsigned int result = 0;
|
|
|
|
//determine how to write each instruction based on the Ast
|
|
switch(ast->type) {
|
|
case TOY_AST_BLOCK:
|
|
if (ast->block.innerScope) {
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_SCOPE_PUSH);
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
}
|
|
|
|
result += writeRoutineCode(rt, ast->block.child);
|
|
result += writeRoutineCode(rt, ast->block.next);
|
|
|
|
if (ast->block.innerScope) {
|
|
EMIT_BYTE(rt, code, TOY_OPCODE_SCOPE_POP);
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
EMIT_BYTE(rt, code, 0);
|
|
}
|
|
break;
|
|
|
|
case TOY_AST_VALUE:
|
|
result += writeInstructionValue(rt, ast->value);
|
|
break;
|
|
|
|
case TOY_AST_UNARY:
|
|
result += writeInstructionUnary(rt, ast->unary);
|
|
break;
|
|
|
|
case TOY_AST_BINARY:
|
|
result += writeInstructionBinary(rt, ast->binary);
|
|
break;
|
|
|
|
case TOY_AST_COMPARE:
|
|
result += writeInstructionCompare(rt, ast->compare);
|
|
break;
|
|
|
|
case TOY_AST_GROUP:
|
|
result += writeInstructionGroup(rt, ast->group);
|
|
break;
|
|
|
|
case TOY_AST_COMPOUND:
|
|
result += writeInstructionCompound(rt, ast->compound);
|
|
break;
|
|
|
|
case TOY_AST_AGGREGATE:
|
|
result += writeInstructionAggregate(rt, ast->aggregate);
|
|
break;
|
|
|
|
case TOY_AST_ASSERT:
|
|
result += writeInstructionAssert(rt, ast->assert);
|
|
break;
|
|
|
|
case TOY_AST_IF_THEN_ELSE:
|
|
result += writeInstructionIfThenElse(rt, ast->ifThenElse);
|
|
break;
|
|
|
|
case TOY_AST_WHILE_THEN:
|
|
result += writeInstructionWhileThen(rt, ast->whileThen);
|
|
break;
|
|
|
|
case TOY_AST_BREAK:
|
|
result += writeInstructionBreak(rt, ast->breakPoint);
|
|
break;
|
|
|
|
case TOY_AST_CONTINUE:
|
|
result += writeInstructionContinue(rt, ast->continuePoint);
|
|
break;
|
|
|
|
case TOY_AST_PRINT:
|
|
result += writeInstructionPrint(rt, ast->print);
|
|
break;
|
|
|
|
case TOY_AST_VAR_DECLARE:
|
|
result += writeInstructionVarDeclare(rt, ast->varDeclare);
|
|
break;
|
|
|
|
case TOY_AST_VAR_ASSIGN:
|
|
result += writeInstructionAssign(rt, ast->varAssign);
|
|
break;
|
|
|
|
case TOY_AST_VAR_ACCESS:
|
|
result += writeInstructionAccess(rt, ast->varAccess);
|
|
break;
|
|
|
|
//meta instructions are disallowed
|
|
case TOY_AST_PASS:
|
|
//NOTE: this *should* be disallowed, but for now it's required for testing
|
|
break;
|
|
|
|
case TOY_AST_ERROR:
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Unknown 'error'\n" TOY_CC_RESET);
|
|
(*rt)->panic = true;
|
|
break;
|
|
|
|
case TOY_AST_END:
|
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid AST type found: Unknown 'end'\n" TOY_CC_RESET);
|
|
(*rt)->panic = true;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
|
|
//code
|
|
writeRoutineCode(&rt, ast);
|
|
EMIT_BYTE(&rt, code, TOY_OPCODE_RETURN); //temp terminator
|
|
EMIT_BYTE(&rt, code, 0); //4-byte alignment
|
|
EMIT_BYTE(&rt, code, 0);
|
|
EMIT_BYTE(&rt, code, 0);
|
|
|
|
//if an error occurred, just exit
|
|
if (rt->panic) {
|
|
return NULL;
|
|
}
|
|
|
|
//write the header and combine the parts
|
|
void* buffer = NULL;
|
|
unsigned int capacity = 0, count = 0;
|
|
// int paramAddr = 0, subsAddr = 0;
|
|
int codeAddr = 0;
|
|
int jumpsAddr = 0;
|
|
int dataAddr = 0;
|
|
|
|
emitInt(&buffer, &capacity, &count, 0); //total size (overwritten later)
|
|
emitInt(&buffer, &capacity, &count, rt->paramCount); //param size
|
|
emitInt(&buffer, &capacity, &count, rt->jumpsCount); //jumps size
|
|
emitInt(&buffer, &capacity, &count, rt->dataCount); //data size
|
|
emitInt(&buffer, &capacity, &count, rt->subsCount); //routine size
|
|
|
|
//generate blank spaces, cache their positions in the *Addr variables (for storing the start positions)
|
|
if (rt->paramCount > 0) {
|
|
// paramAddr = count;
|
|
emitInt((void**)&buffer, &capacity, &count, 0); //params
|
|
}
|
|
if (rt->codeCount > 0) {
|
|
codeAddr = count;
|
|
emitInt((void**)&buffer, &capacity, &count, 0); //code
|
|
}
|
|
if (rt->jumpsCount > 0) {
|
|
jumpsAddr = count;
|
|
emitInt((void**)&buffer, &capacity, &count, 0); //jumps
|
|
}
|
|
if (rt->dataCount > 0) {
|
|
dataAddr = count;
|
|
emitInt((void**)&buffer, &capacity, &count, 0); //data
|
|
}
|
|
if (rt->subsCount > 0) {
|
|
// subsAddr = count;
|
|
emitInt((void**)&buffer, &capacity, &count, 0); //subs
|
|
}
|
|
|
|
//append various parts to the buffer
|
|
//TODO: param region
|
|
|
|
if (rt->codeCount > 0) {
|
|
expand(&buffer, &capacity, &count, rt->codeCount);
|
|
memcpy((buffer + count), rt->code, rt->codeCount);
|
|
|
|
*((int*)(buffer + codeAddr)) = count;
|
|
count += rt->codeCount;
|
|
}
|
|
|
|
if (rt->jumpsCount > 0) {
|
|
expand(&buffer, &capacity, &count, rt->jumpsCount);
|
|
memcpy((buffer + count), rt->jumps, rt->jumpsCount);
|
|
|
|
*((int*)(buffer + jumpsAddr)) = count;
|
|
count += rt->jumpsCount;
|
|
}
|
|
|
|
if (rt->dataCount > 0) {
|
|
expand(&buffer, &capacity, &count, rt->dataCount);
|
|
memcpy((buffer + count), rt->data, rt->dataCount);
|
|
|
|
*((int*)(buffer + dataAddr)) = count;
|
|
count += rt->dataCount;
|
|
}
|
|
|
|
//TODO: subs region
|
|
|
|
//finally, record the total size within the header, and return the result
|
|
*((int*)buffer) = count;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
//exposed functions
|
|
void* Toy_compileRoutine(Toy_Ast* ast) {
|
|
//setup
|
|
Toy_Routine rt;
|
|
|
|
rt.param = NULL;
|
|
rt.paramCapacity = 0;
|
|
rt.paramCount = 0;
|
|
|
|
rt.code = NULL;
|
|
rt.codeCapacity = 0;
|
|
rt.codeCount = 0;
|
|
|
|
rt.jumps = NULL;
|
|
rt.jumpsCapacity = 0;
|
|
rt.jumpsCount = 0;
|
|
|
|
rt.data = NULL;
|
|
rt.dataCapacity = 0;
|
|
rt.dataCount = 0;
|
|
|
|
rt.subs = NULL;
|
|
rt.subsCapacity = 0;
|
|
rt.subsCount = 0;
|
|
|
|
rt.panic = false;
|
|
|
|
//build
|
|
void * buffer = writeRoutine(&rt, ast);
|
|
|
|
//cleanup the temp object
|
|
free(rt.param);
|
|
free(rt.code);
|
|
free(rt.jumps);
|
|
free(rt.data);
|
|
free(rt.subs);
|
|
|
|
return buffer;
|
|
}
|