Reworked generic structures, read more

The following structures are now more independant:

- Toy_Array
- Toy_Stack
- Toy_Bucket
- Toy_String

I reworked a lot of the memory allocation, so now there are more direct
calls to malloc() or realloc(), rather than relying on the macros from
toy_memory.h.

I've also split toy_memory into proper array and bucket files, because
it makes more sense this way, rather than having them both jammed into
one file. This means the eventual hashtable structure can also stand on
its own.

Toy_Array is a new wrapper around raw array pointers, and all of the
structures have their metadata embedded into their allocated memory now,
using variable length array members.

A lot of 'capacity' and 'count' variables were changed to 'size_t'
types, but this doesn't seem to be a problem anywhere.

If the workflow fails, then I'll leave it for tonight - I'm too tired,
and I don't want to overdo myself.
This commit is contained in:
2024-10-01 20:24:52 +10:00
parent 53b0fc158c
commit 7b453bc35f
34 changed files with 566 additions and 576 deletions

View File

@@ -3,17 +3,21 @@
//general utilities
#include "toy_common.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
//building blocks
//basic structures
#include "toy_value.h"
#include "toy_array.h"
#include "toy_stack.h"
#include "toy_bucket.h"
#include "toy_string.h"
//TODO: hashtable
//IR structures and other components
#include "toy_ast.h"
#include "toy_routine.h"
#include "toy_stack.h"
//pipeline
#include "toy_lexer.h"
#include "toy_parser.h"
#include "toy_bytecode.h"
#include "toy_vm.h"

25
source/toy_array.c Normal file
View File

@@ -0,0 +1,25 @@
#include "toy_array.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
Toy_Array* Toy_resizeArray(Toy_Array* paramArray, size_t capacity) {
if (capacity == 0) {
free(paramArray);
return NULL;
}
Toy_Array* array = realloc(paramArray, capacity + sizeof(Toy_Array));
if (array == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Array' of %d capacity\n" TOY_CC_RESET, (int)capacity);
exit(1);
}
array->capacity = capacity;
array->count = paramArray == NULL ? 0 :
(array->count > capacity ? capacity : array->count); //truncate lost data
return array;
}

25
source/toy_array.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include "toy_common.h"
//standard generic array
typedef struct Toy_Array { //32 | 64 BITNESS
size_t capacity; //4 | 4
size_t count; //4 | 4
char data[]; //- | -
} Toy_Array; //8 | 8
TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, size_t capacity);
#define TOY_ALLOCATE_ARRAY(type, count) \
Toy_resizeArray(NULL, sizeof(type)*(count))
#define TOY_FREE_ARRAY(type, array) \
Toy_resizeArray(array, 0)
#define TOY_ADJUST_ARRAY(type, array, newCapacity) \
Toy_resizeArray(array, sizeof(type) * newCapacity)
#define TOY_DOUBLE_ARRAY_CAPACITY(type, array) \
Toy_resizeArray(array, sizeof(type) * array->capacity < 8 ? sizeof(type) * 8 : sizeof(type) * array->capacity * 2)

View File

@@ -1,25 +1,25 @@
#include "toy_ast.h"
void Toy_private_initAstBlock(Toy_Bucket** bucket, Toy_Ast** handle) {
(*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
(*handle)->block.type = TOY_AST_BLOCK;
(*handle)->block.child = NULL;
(*handle)->block.next = NULL;
(*handle)->block.tail = NULL;
tmp->type = TOY_AST_BLOCK;
tmp->block.child = NULL;
tmp->block.next = NULL;
tmp->block.tail = NULL;
(*handle) = tmp;
}
void Toy_private_appendAstBlock(Toy_Bucket** bucket, Toy_Ast** handle, Toy_Ast* child) {
//type check
void Toy_private_appendAstBlock(Toy_Bucket** bucket, Toy_Ast* block, Toy_Ast* child) {
//first, check if we're an empty head
if ((*handle)->block.child == NULL) {
(*handle)->block.child = child;
if (block->block.child == NULL) {
block->block.child = child;
return; //NOTE: first call on an empty head skips any memory allocations
}
//run (or jump) until we hit the current tail
Toy_Ast* iter = (*handle)->block.tail ? (*handle)->block.tail : (*handle);
Toy_Ast* iter = block->block.tail ? block->block.tail : block;
while(iter->block.next != NULL) {
iter = iter->block.next;
@@ -30,21 +30,23 @@ void Toy_private_appendAstBlock(Toy_Bucket** bucket, Toy_Ast** handle, Toy_Ast*
//store the child in the new link, prep the tail pointer
iter->block.next->block.child = child;
(*handle)->block.tail = iter->block.next;
block->block.tail = iter->block.next;
}
void Toy_private_emitAstValue(Toy_Bucket** bucket, Toy_Ast** handle, Toy_Value value) {
(*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
(*handle)->value.type = TOY_AST_VALUE;
(*handle)->value.value = value;
tmp->type = TOY_AST_VALUE;
tmp->value.value = value;
(*handle) = tmp;
}
//TODO: flag range checks
void Toy_private_emitAstUnary(Toy_Bucket** bucket, Toy_Ast** handle, Toy_AstFlag flag) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
tmp->unary.type = TOY_AST_UNARY;
tmp->type = TOY_AST_UNARY;
tmp->unary.flag = flag;
tmp->unary.child = *handle;
@@ -52,9 +54,9 @@ void Toy_private_emitAstUnary(Toy_Bucket** bucket, Toy_Ast** handle, Toy_AstFlag
}
void Toy_private_emitAstBinary(Toy_Bucket** bucket, Toy_Ast** handle, Toy_AstFlag flag, Toy_Ast* right) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
tmp->binary.type = TOY_AST_BINARY;
tmp->type = TOY_AST_BINARY;
tmp->binary.flag = flag;
tmp->binary.left = *handle; //left-recursive
tmp->binary.right = right;
@@ -63,28 +65,34 @@ void Toy_private_emitAstBinary(Toy_Bucket** bucket, Toy_Ast** handle, Toy_AstFla
}
void Toy_private_emitAstGroup(Toy_Bucket** bucket, Toy_Ast** handle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
tmp->group.type = TOY_AST_GROUP;
tmp->type = TOY_AST_GROUP;
tmp->group.child = (*handle);
(*handle) = tmp;
}
void Toy_private_emitAstPass(Toy_Bucket** bucket, Toy_Ast** handle) {
(*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
(*handle)->pass.type = TOY_AST_PASS;
tmp->type = TOY_AST_PASS;
(*handle) = tmp;
}
void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle) {
(*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
(*handle)->error.type = TOY_AST_ERROR;
tmp->type = TOY_AST_ERROR;
(*handle) = tmp;
}
void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle) {
(*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucket, sizeof(Toy_Ast));
(*handle)->error.type = TOY_AST_END;
tmp->type = TOY_AST_END;
(*handle) = tmp;
}

View File

@@ -1,8 +1,8 @@
#pragma once
#include "toy_common.h"
#include "toy_memory.h"
#include "toy_bucket.h"
#include "toy_value.h"
//each major type
@@ -55,18 +55,6 @@ typedef enum Toy_AstFlag {
//the root AST type
typedef union Toy_Ast Toy_Ast;
void Toy_private_initAstBlock(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_appendAstBlock(Toy_Bucket** bucket, Toy_Ast** handle, Toy_Ast* child);
void Toy_private_emitAstValue(Toy_Bucket** bucket, Toy_Ast** handle, Toy_Value value);
void Toy_private_emitAstUnary(Toy_Bucket** bucket, Toy_Ast** handle, Toy_AstFlag flag);
void Toy_private_emitAstBinary(Toy_Bucket** bucket, Toy_Ast** handle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstGroup(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstPass(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle);
typedef struct Toy_AstBlock {
Toy_AstType type;
Toy_Ast* child; //begin encoding the line
@@ -121,3 +109,15 @@ union Toy_Ast { //32 | 64 BITNESS
Toy_AstError error; //4 | 4
Toy_AstEnd end; //4 | 4
}; //16 | 32
void Toy_private_initAstBlock(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_appendAstBlock(Toy_Bucket** bucket, Toy_Ast* block, Toy_Ast* child);
void Toy_private_emitAstValue(Toy_Bucket** bucket, Toy_Ast** handle, Toy_Value value);
void Toy_private_emitAstUnary(Toy_Bucket** bucket, Toy_Ast** handle, Toy_AstFlag flag);
void Toy_private_emitAstBinary(Toy_Bucket** bucket, Toy_Ast** handle,Toy_AstFlag flag, Toy_Ast* right);
void Toy_private_emitAstGroup(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstPass(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle);

68
source/toy_bucket.c Normal file
View File

@@ -0,0 +1,68 @@
#include "toy_bucket.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
//buckets of fun
Toy_Bucket* Toy_allocateBucket(size_t capacity) {
if (capacity == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Cannot allocate a 'Toy_Bucket' with zero capacity\n" TOY_CC_RESET);
exit(1);
}
Toy_Bucket* bucket = malloc(sizeof(Toy_Bucket) + capacity);
if (bucket == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Bucket' of %d capacity\n" TOY_CC_RESET, (int)capacity);
exit(1);
}
//initialize the bucket
bucket->next = NULL;
bucket->capacity = capacity;
bucket->count = 0;
return bucket;
}
void* Toy_partitionBucket(Toy_Bucket** bucketHandle, size_t amount) {
if ((*bucketHandle) == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Expected a 'Toy_Bucket', received NULL\n" TOY_CC_RESET);
exit(1);
}
//if you try to allocate too much space
if ((*bucketHandle)->capacity < amount) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to partition a 'Toy_Bucket': requested %d from a bucket of %d capacity\n" TOY_CC_RESET, (int)amount, (int)((*bucketHandle)->capacity));
exit(1);
}
//if you're out of space in this bucket
if ((*bucketHandle)->capacity < (*bucketHandle)->count + amount) {
//move to the next bucket
Toy_Bucket* tmp = Toy_allocateBucket((*bucketHandle)->capacity);
tmp->next = (*bucketHandle); //it's buckets all the way down
(*bucketHandle) = tmp;
}
//track the new count, and return the specified memory space
(*bucketHandle)->count += amount;
return ((*bucketHandle)->data + (*bucketHandle)->count - amount);
}
void Toy_freeBucket(Toy_Bucket** bucketHandle) {
Toy_Bucket* iter = (*bucketHandle);
while (iter != NULL) {
//run down the chain
Toy_Bucket* last = iter;
iter = iter->next;
//clear the previous bucket from memory
free(last);
}
//for safety
(*bucketHandle) = NULL;
}

22
source/toy_bucket.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "toy_common.h"
//NOTE: this structure has restrictions on it's usage:
// - It can only expand until it is freed
// - It cannot be copied around within RAM
// - It cannot allocate more memory than it has capacity
// If each of these rules are followed, the bucket is actually more efficient than any other option
//a custom allocator
typedef struct Toy_Bucket { //32 | 64 BITNESS
struct Toy_Bucket* next; //4 | 8
size_t capacity; //4 | 4
size_t count; //4 | 4
char data[]; //- | -
} Toy_Bucket; //12 | 16
TOY_API Toy_Bucket* Toy_allocateBucket(size_t capacity);
TOY_API void* Toy_partitionBucket(Toy_Bucket** bucketHandle, size_t amount);
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);

View File

@@ -1,21 +1,26 @@
#include "toy_bytecode.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_routine.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//utils
static void expand(Toy_Bytecode* bc, int amount) {
static void expand(Toy_Bytecode* bc, size_t amount) {
if (bc->count + amount > bc->capacity) {
int oldCapacity = bc->capacity;
while (bc->count + amount > bc->capacity) {
bc->capacity = TOY_GROW_CAPACITY(bc->capacity);
while (bc->count + amount > bc->capacity) { //expand as much as needed
bc->capacity = bc->capacity < 8 ? 8 : bc->capacity * 2;
}
bc->ptr = realloc(bc->ptr, bc->capacity);
if (bc->ptr == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Bytecode' of %d capacity\n" TOY_CC_RESET, (int)(bc->capacity));
exit(1);
}
bc->ptr = TOY_GROW_ARRAY(unsigned char, bc->ptr, oldCapacity, bc->capacity);
}
}
@@ -32,7 +37,7 @@ static void writeBytecodeHeader(Toy_Bytecode* bc) {
//check strlen for the build string
const char* build = Toy_private_version_build();
int len = (int)strlen(build) + 1;
size_t len = strlen(build) + 1;
//BUGFIX: ensure the end of the header has 4-byte alignment
if (len % 4 != 1) { //1 to fill the 4th byte above
@@ -42,6 +47,8 @@ static void writeBytecodeHeader(Toy_Bytecode* bc) {
expand(bc, len);
memcpy(bc->ptr + bc->count, build, len);
bc->count += len;
bc->ptr[bc->count] = '\0';
}
static void writeBytecodeBody(Toy_Bytecode* bc, Toy_Ast* ast) {
@@ -50,7 +57,7 @@ static void writeBytecodeBody(Toy_Bytecode* bc, Toy_Ast* ast) {
//eventually, the bytecode may support multiple modules packed into one file
void* module = Toy_compileRoutine(ast);
int len = ((int*)module)[0];
size_t len = (size_t)(((int*)module)[0]);
expand(bc, len);
memcpy(bc->ptr + bc->count, module, len);
@@ -74,5 +81,5 @@ Toy_Bytecode Toy_compileBytecode(Toy_Ast* ast) {
}
void Toy_freeBytecode(Toy_Bytecode bc) {
TOY_FREE_ARRAY(unsigned char, bc.ptr, bc.capacity);
free(bc.ptr);
}

View File

@@ -5,8 +5,8 @@
typedef struct Toy_Bytecode {
unsigned char* ptr;
int capacity;
int count;
size_t capacity;
size_t count;
} Toy_Bytecode;
TOY_API Toy_Bytecode Toy_compileBytecode(Toy_Ast* ast);

View File

@@ -312,7 +312,7 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
}
}
static void trim(char** s, int* l) { //util
static void trim(char** s, size_t* l) { //util
while( isspace(( (*((unsigned char**)(s)))[(*l) - 1] )) ) (*l)--;
while(**s && isspace( **(unsigned char**)(s)) ) { (*s)++; (*l)--; }
}
@@ -321,22 +321,22 @@ static void trim(char** s, int* l) { //util
void Toy_private_printToken(Toy_Token* token) {
//print errors
if (token->type == TOY_TOKEN_ERROR) {
printf(TOY_CC_ERROR "ERROR: \t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
printf(TOY_CC_ERROR "ERROR: \t%d\t%.*s\n" TOY_CC_RESET, (int)token->line, (int)token->length, token->lexeme);
return;
}
//read pass token, even though it isn't generated
if (token->type == TOY_TOKEN_PASS) {
printf(TOY_CC_NOTICE "PASS: \t%d\t%.*s\n" TOY_CC_RESET, token->line, token->length, token->lexeme);
printf(TOY_CC_NOTICE "PASS: \t%d\t%.*s\n" TOY_CC_RESET, (int)token->line, (int)token->length, token->lexeme);
return;
}
//print the line number
printf("\t%d\t%d\t", token->type, token->line);
printf("\t%d\t%d\t", token->type, (int)token->line);
//print based on type
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
printf("%.*s\t", token->length, token->lexeme);
printf("%.*s\t", (int)token->length, token->lexeme);
} else {
const char* keyword = Toy_private_findKeywordByType(token->type);
@@ -344,9 +344,9 @@ void Toy_private_printToken(Toy_Token* token) {
printf("%s", keyword);
} else {
char* str = (char*)token->lexeme; //strip const-ness for trimming
int length = token->length;
size_t length = token->length;
trim(&str, &length);
printf("%.*s", length, str);
printf("%.*s", (int)length, str);
}
}

View File

@@ -5,17 +5,17 @@
//lexers are bound to a string of code
typedef struct {
int start; //start of the current token
int current; //current position of the lexer
int line; //track this for error handling
size_t start; //start of the current token
size_t current; //current position of the lexer
size_t line; //track this for error handling
const char* source;
} Toy_Lexer;
//tokens are intermediaries between lexers and parsers
typedef struct {
Toy_TokenType type;
int length;
int line;
size_t length;
size_t line;
const char* lexeme;
} Toy_Token;

View File

@@ -1,93 +0,0 @@
#include "toy_memory.h"
#include "toy_console_colors.h"
#include <stdio.h>
#include <stdlib.h>
void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize) {
if (newSize == 0) {
free(pointer);
return NULL;
}
void* result = realloc(pointer, newSize);
if (result == NULL) {
fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Memory allocation error (requested %d, replacing %d)\n" TOY_CC_RESET, (int)newSize, (int)oldSize);
exit(1);
}
return result;
}
//buckets of fun
void Toy_initBucket(Toy_Bucket** bucketHandle, size_t capacity) {
if (capacity == 0) {
fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Cannot init a bucket with zero capacity\n" TOY_CC_RESET);
exit(1);
}
(*bucketHandle) = malloc(sizeof(Toy_Bucket)); //TODO: rework the bucket, so there's only one malloc() call instead of two when partitioning
if ((*bucketHandle) == NULL) {
fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to allocate space for a bucket\n" TOY_CC_RESET);
exit(1);
}
//initialize the bucket
(*bucketHandle)->next = NULL;
(*bucketHandle)->contents = NULL; //leave until the first partition
(*bucketHandle)->capacity = capacity;
(*bucketHandle)->count = 0;
}
void* Toy_partBucket(Toy_Bucket** bucketHandle, size_t space) {
if ((*bucketHandle) == NULL) {
fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Expected bucket, received NULL\n" TOY_CC_RESET);
exit(1);
}
//if you try to allocate too much space
if ((*bucketHandle)->capacity < space) {
fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to partition bucket memory, not enough capacity: needed %d, only %d available\n" TOY_CC_RESET, (int)space, (int)((*bucketHandle)->capacity));
exit(1);
}
//if out of space in the current bucket
if ((*bucketHandle)->capacity < (*bucketHandle)->count + space) {
//move to the next bucket
Toy_Bucket* tmp = NULL;
Toy_initBucket(&tmp, (*bucketHandle)->capacity);
tmp->next = (*bucketHandle);
(*bucketHandle) = tmp;
}
//if no space allocated for the current bucket
if ((*bucketHandle)->contents == NULL) {
//allocate space for the current bucket
(*bucketHandle)->contents = malloc((*bucketHandle)->capacity);
//double check
if ((*bucketHandle)->contents == NULL) {
fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to allocate space for bucket contents\n" TOY_CC_RESET);
exit(1);
}
}
//track the new count, and return the specified memory space
(*bucketHandle)->count += space;
return ((*bucketHandle)->contents + (*bucketHandle)->count - space);
}
void Toy_freeBucket(Toy_Bucket** bucketHandle) {
while ((*bucketHandle) != NULL) {
//run down the chain
Toy_Bucket* ptr = (*bucketHandle);
(*bucketHandle) = (*bucketHandle)->next;
//clear the previous bucket from memory
free(ptr->contents);
free(ptr);
}
}

View File

@@ -1,45 +0,0 @@
#pragma once
#include "toy_common.h"
//standard movable array for general use
#define TOY_GROW_CAPACITY(capacity) \
((capacity) < 8 ? 8 : (capacity) * 2)
#define TOY_ALLOCATE(type, count) \
(type*)Toy_reallocate(NULL, 0, sizeof(type)*(count))
#define TOY_FREE(type, pointer) \
(type*)Toy_reallocate(pointer, sizeof(type), 0)
#define TOY_GROW_ARRAY(type, pointer, oldSize, newSize) \
(type*)Toy_reallocate(pointer, sizeof(type)*oldSize, sizeof(type)*newSize)
#define TOY_SHRINK_ARRAY(type, pointer, oldCount, count) \
(type*)Toy_reallocate((type*)pointer, sizeof(type)*(oldCount), sizeof(type)*(count))
#define TOY_FREE_ARRAY(type, pointer, oldSize) \
(type*)Toy_reallocate(pointer, sizeof(type)*oldSize, 0)
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);
//immobile "bucket" memory structure for custom allocators
#define TOY_BUCKET_INIT(type, bucket, count) \
Toy_initBucket(&(bucket), sizeof(type)*(count))
#define TOY_BUCKET_PART(type, bucket) \
(type*)Toy_partBucket(&(bucket), sizeof(type))
#define TOY_BUCKET_FREE(bucket) \
Toy_freeBucket(&(bucket))
typedef struct Toy_Bucket {
struct Toy_Bucket* next;
void* contents;
size_t capacity;
size_t count;
} Toy_Bucket;
TOY_API void Toy_initBucket(Toy_Bucket** bucketHandle, size_t capacity);
TOY_API void* Toy_partBucket(Toy_Bucket** bucketHandle, size_t space);
TOY_API void Toy_freeBucket(Toy_Bucket** bucketHandle);

View File

@@ -10,14 +10,14 @@ static void printError(Toy_Parser* parser, Toy_Token token, const char* errorMsg
return;
}
fprintf(stderr, TOY_CC_ERROR "[Line %d] Error ", token.line);
fprintf(stderr, TOY_CC_ERROR "[Line %d] Error ", (int)token.line);
//check type
if (token.type == TOY_TOKEN_EOF) {
fprintf(stderr, "at end");
}
else {
fprintf(stderr, "at '%.*s'", token.length, token.lexeme);
fprintf(stderr, "at '%.*s'", (int)token.length, token.lexeme);
}
//finally
@@ -232,7 +232,7 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
//filter the '_' character
char buffer[parser->previous.length];
int i = 0, o = 0;
size_t i = 0, o = 0;
do {
buffer[i] = parser->previous.lexeme[o];
if (buffer[i] != '_') i++;
@@ -249,7 +249,7 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
//filter the '_' character
char buffer[parser->previous.length];
int i = 0, o = 0;
size_t i = 0, o = 0;
do {
buffer[i] = parser->previous.lexeme[o];
if (buffer[i] != '_') i++;
@@ -542,11 +542,11 @@ static void makeBlockStmt(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
Toy_Ast* err = NULL;
Toy_private_emitAstError(bucket, &err);
Toy_private_appendAstBlock(bucket, root, err);
Toy_private_appendAstBlock(bucket, *root, err);
continue;
}
Toy_private_appendAstBlock(bucket, root, stmt);
Toy_private_appendAstBlock(bucket, *root, stmt);
}
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include "toy_common.h"
#include "toy_memory.h"
#include "toy_lexer.h"
#include "toy_ast.h"

View File

@@ -1,7 +1,6 @@
#include "toy_routine.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_opcodes.h"
#include "toy_value.h"
@@ -10,23 +9,26 @@
#include <string.h>
//utils
static void expand(void** handle, int* capacity, int* count, int amount) {
static void expand(void** handle, size_t* capacity, size_t* count, size_t amount) {
if ((*count) + amount > (*capacity)) {
int oldCapacity = (*capacity);
while ((*count) + amount > (*capacity)) {
(*capacity) = TOY_GROW_CAPACITY(*capacity);
(*capacity) = (*capacity) < 8 ? 8 : (*capacity) * 2;
}
(*handle) = realloc((*handle), (*capacity));
if ((*handle) == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Routine' of %d capacity\n" TOY_CC_RESET, (int)(*capacity));
exit(1);
}
(*handle) = TOY_GROW_ARRAY(unsigned char, (*handle), oldCapacity, (*capacity));
}
}
static void emitByte(void** handle, int* capacity, int* count, unsigned char byte) {
static void emitByte(void** handle, size_t* capacity, size_t* count, unsigned char byte) {
expand(handle, capacity, count, 1);
((unsigned char*)(*handle))[(*count)++] = byte;
}
static void emitInt(void** handle, int* capacity, int* count, int bytes) {
static void emitInt(void** handle, size_t* capacity, size_t* count, size_t bytes) {
char* ptr = (char*)&bytes;
emitByte(handle, capacity, count, *(ptr++));
emitByte(handle, capacity, count, *(ptr++));
@@ -34,7 +36,7 @@ static void emitInt(void** handle, int* capacity, int* count, int bytes) {
emitByte(handle, capacity, count, *(ptr++));
}
static void emitFloat(void** handle, int* capacity, int* count, float bytes) {
static void emitFloat(void** handle, size_t* capacity, size_t* count, float bytes) {
char* ptr = (char*)&bytes;
emitByte(handle, capacity, count, *(ptr++));
emitByte(handle, capacity, count, *(ptr++));
@@ -281,8 +283,8 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
//TODO: data
//write the header and combine the parts
void* buffer = TOY_ALLOCATE(unsigned char, 16);
int capacity = 0, count = 0;
void* buffer = NULL;
size_t capacity = 0, count = 0;
// int paramAddr = 0, codeAddr = 0, jumpsAddr = 0, dataAddr = 0, subsAddr = 0;
int codeAddr = 0;
@@ -357,12 +359,13 @@ void* Toy_compileRoutine(Toy_Ast* ast) {
//build
void * buffer = writeRoutine(&rt, ast);
//cleanup the temp object
TOY_FREE_ARRAY(unsigned char, rt.param, rt.paramCapacity);
TOY_FREE_ARRAY(unsigned char, rt.code, rt.codeCapacity);
TOY_FREE_ARRAY(int, rt.jumps, rt.jumpsCapacity);
TOY_FREE_ARRAY(unsigned char, rt.data, rt.dataCapacity);
TOY_FREE_ARRAY(unsigned char, rt.subs, rt.subsCapacity);
free(rt.param);
free(rt.code);
free(rt.jumps);
free(rt.data);
free(rt.subs);
return buffer;
}

View File

@@ -6,24 +6,24 @@
//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?)
int paramCapacity;
int paramCount;
size_t paramCapacity;
size_t paramCount;
unsigned char* code; //the instruction set
int codeCapacity;
int codeCount;
size_t codeCapacity;
size_t codeCount;
int* jumps; //each 'jump' is the starting address of an element within 'data'
int jumpsCapacity;
int jumpsCount;
size_t* jumps; //each 'jump' is the starting address of an element within 'data'
size_t jumpsCapacity;
size_t jumpsCount;
unsigned char* data; //{type,val} tuples of data
int dataCapacity;
int dataCount;
size_t dataCapacity;
size_t dataCount;
unsigned char* subs; //subroutines, recursively
int subsCapacity;
int subsCount;
size_t subsCapacity;
size_t subsCount;
} Toy_Routine;
TOY_API void* Toy_compileRoutine(Toy_Ast* ast);

View File

@@ -1,72 +1,86 @@
#include "toy_stack.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include <stdio.h>
#include <stdlib.h>
//a good chunk of space
#define MIN_SIZE 64
//a good chunk of space - 'count' actually tracks the number of values
#define MIN_CAPACITY 64
TOY_API void Toy_initStack(Toy_Stack* stack) {
stack->ptr = NULL;
stack->capacity = 0;
stack->count = 0;
}
Toy_Stack* Toy_allocateStack() {
Toy_Stack* stack = malloc(MIN_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack));
void Toy_preallocateStack(Toy_Stack* stack) {
stack->capacity = MIN_SIZE;
if (stack == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, MIN_CAPACITY, (int)(MIN_CAPACITY * sizeof(Toy_Value) + sizeof(Toy_Stack)));
exit(1);
}
stack->capacity = MIN_CAPACITY;
stack->count = 0;
stack->ptr = TOY_ALLOCATE(Toy_Value, stack->capacity);
return stack;
}
void Toy_freeStack(Toy_Stack* stack) {
//TODO: slip in a call to free the complex values here
TOY_FREE_ARRAY(Toy_Value, stack->ptr, stack->capacity);
Toy_initStack(stack);
free(stack);
}
void Toy_pushStack(Toy_Stack* stack, Toy_Value value) {
//don't go overboard - limit to 1mb
if (stack->count >= 1024 * 1024 / sizeof(Toy_Value)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack overflow, exiting\n" TOY_CC_RESET);
void Toy_pushStack(Toy_Stack** stack, Toy_Value value) {
//don't go overboard - limit to 1mb of capacity used
if ((*stack)->count >= 1024 * 1024 / sizeof(Toy_Value)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack overflow\n" TOY_CC_RESET);
exit(-1);
}
//expand the capacity if needed
if (stack->count + 1 > stack->capacity) {
int oldCapacity = stack->capacity;
stack->capacity = stack->capacity < MIN_SIZE ? MIN_SIZE : stack->capacity * 2; //similar to TOY_GROW_CAPACITY, with a bigger initial size
stack->ptr = TOY_GROW_ARRAY(Toy_Value, stack->ptr, oldCapacity, stack->capacity);
if ((*stack)->count + 1 > (*stack)->capacity) {
while ((*stack)->count + 1 > (*stack)->capacity) {
(*stack)->capacity = (*stack)->capacity < MIN_CAPACITY ? MIN_CAPACITY : (*stack)->capacity * 2;
}
size_t newCapacity = (*stack)->capacity;
(*stack) = realloc((*stack), newCapacity * sizeof(Toy_Value) + sizeof(Toy_Stack));
if ((*stack) == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reallocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, (int)newCapacity, (int)(newCapacity * sizeof(Toy_Value) + sizeof(Toy_Stack)));
exit(1);
}
}
stack->ptr[stack->count++] = value;
//Note: "pointer arithmetic in C/C++ is type-relative"
((Toy_Value*)((*stack) + 1))[(*stack)->count++] = value;
}
Toy_Value Toy_peekStack(Toy_Stack* stack) {
if (stack->count <= 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack underflow, exiting\n" TOY_CC_RESET);
Toy_Value Toy_peekStack(Toy_Stack** stack) {
if ((*stack)->count == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack underflow\n" TOY_CC_RESET);
exit(-1);
}
return stack->ptr[stack->count - 1];
return ((Toy_Value*)((*stack) + 1))[(*stack)->count - 1];
}
Toy_Value Toy_popStack(Toy_Stack* stack) {
if (stack->count <= 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack underflow, exiting\n" TOY_CC_RESET);
Toy_Value Toy_popStack(Toy_Stack** stack) {
if ((*stack)->count == 0) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Stack underflow\n" TOY_CC_RESET);
exit(-1);
}
//shrink if possible
if (stack->count > MIN_SIZE && stack->count < stack->capacity / 4) {
stack->ptr = TOY_SHRINK_ARRAY(Toy_Value, stack->ptr, stack->capacity, stack->capacity / 2);
stack->capacity /= 2;
if ((*stack)->count > MIN_CAPACITY && (*stack)->count < (*stack)->capacity / 4) {
(*stack)->capacity /= 2;
size_t newCapacity = (*stack)->capacity;
(*stack) = realloc((*stack), (*stack)->capacity * sizeof(Toy_Value) + sizeof(Toy_Stack));
if ((*stack) == NULL) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reallocate a 'Toy_Stack' of %d capacity (%d space in memory)\n" TOY_CC_RESET, (int)newCapacity, (int)(newCapacity * sizeof(Toy_Value) + sizeof(Toy_Stack)));
exit(1);
}
}
return stack->ptr[--stack->count];
return ((Toy_Value*)((*stack) + 1))[--(*stack)->count];
}

View File

@@ -3,16 +3,15 @@
#include "toy_common.h"
#include "toy_value.h"
typedef struct Toy_Stack {
Toy_Value* ptr;
int capacity;
int count;
} Toy_Stack;
typedef struct Toy_Stack { //32 | 64 BITNESS
size_t capacity; //4 | 4
size_t count; //4 | 4
char data[]; //- | -
} Toy_Stack; //8 | 8
TOY_API void Toy_initStack(Toy_Stack* stack); //null memory
TOY_API void Toy_preallocateStack(Toy_Stack* stack); //non-null memory, ready to go
TOY_API Toy_Stack* Toy_allocateStack();
TOY_API void Toy_freeStack(Toy_Stack* stack);
TOY_API void Toy_pushStack(Toy_Stack* stack, Toy_Value value);
TOY_API Toy_Value Toy_peekStack(Toy_Stack* stack);
TOY_API Toy_Value Toy_popStack(Toy_Stack* stack);
TOY_API void Toy_pushStack(Toy_Stack** stack, Toy_Value value);
TOY_API Toy_Value Toy_peekStack(Toy_Stack** stack);
TOY_API Toy_Value Toy_popStack(Toy_Stack** stack);

View File

@@ -42,7 +42,7 @@ Toy_String* Toy_createString(Toy_Bucket** bucket, const char* cstring) {
}
Toy_String* Toy_createStringLength(Toy_Bucket** bucket, const char* cstring, int length) {
Toy_String* ret = (Toy_String*)Toy_partBucket(bucket, sizeof(Toy_String) + length + 1); //TODO: compensate for partitioning more space than bucket capacity
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucket, sizeof(Toy_String) + length + 1); //TODO: compensate for partitioning more space than bucket capacity
ret->type = TOY_STRING_LEAF;
ret->length = length;
@@ -67,7 +67,7 @@ Toy_String* Toy_deepCopyString(Toy_Bucket** bucket, Toy_String* str) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't deep copy a string with refcount below or equal to zero\n" TOY_CC_RESET);
exit(-1);
}
Toy_String* ret = (Toy_String*)Toy_partBucket(bucket, sizeof(Toy_String) + str->length + 1); //TODO: compensate for partitioning more space than bucket capacity
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucket, sizeof(Toy_String) + str->length + 1); //TODO: compensate for partitioning more space than bucket capacity
//TODO
ret->type = TOY_STRING_LEAF;
@@ -85,7 +85,7 @@ Toy_String* Toy_concatString(Toy_Bucket** bucket, Toy_String* left, Toy_String*
exit(-1);
}
Toy_String* ret = (Toy_String*)Toy_partBucket(bucket, sizeof(Toy_String));
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucket, sizeof(Toy_String));
ret->type = TOY_STRING_NODE;
ret->length = left->length + right->length;
@@ -117,7 +117,7 @@ char* Toy_getStringRawBuffer(Toy_String* str) {
exit(-1);
}
char* buffer = TOY_ALLOCATE(char, str->length + 1);
char* buffer = malloc(str->length + 1);
deepCopyUtil(buffer, str);
buffer[str->length] = '\0';

View File

@@ -1,6 +1,6 @@
#include "toy_common.h"
#include "toy_memory.h"
#include "toy_bucket.h"
//rope pattern
typedef struct Toy_String { //32 | 64 BITNESS

View File

@@ -1,5 +1,4 @@
#include "toy_value.h"
#include "toy_console_colors.h"
#include <stdio.h>

View File

@@ -1,7 +1,6 @@
#include "toy_vm.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include "toy_opcodes.h"
#include "toy_value.h"
@@ -278,30 +277,6 @@ static void process(Toy_VM* vm) {
}
//exposed functions
void Toy_initVM(Toy_VM* vm) {
vm->bc = NULL;
vm->bcSize = 0;
vm->routine = NULL;
vm->routineSize = 0;
vm->paramCount = 0;
vm->jumpsCount = 0;
vm->dataCount = 0;
vm->subsCount = 0;
vm->paramAddr = 0;
vm->codeAddr = 0;
vm->jumpsAddr = 0;
vm->dataAddr = 0;
vm->subsAddr = 0;
vm->routineCounter = 0;
//init the scope & stack
Toy_initStack(&vm->stack);
}
void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, int bytecodeSize) {
if (bytecode[0] != TOY_VERSION_MAJOR || bytecode[1] > TOY_VERSION_MINOR) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Wrong bytecode version found: expected %d.%d.%d found %d.%d.%d, exiting\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, bytecode[0], bytecode[1], bytecode[2]);
@@ -331,7 +306,7 @@ void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, int bytecodeSize) {
}
void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) {
Toy_initVM(vm);
Toy_resetVM(vm);
vm->routine = routine;
@@ -362,7 +337,7 @@ void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) {
}
//preallocate the scope & stack
Toy_preallocateStack(&vm->stack);
vm->stack = Toy_allocateStack();
}
void Toy_runVM(Toy_VM* vm) {
@@ -377,11 +352,36 @@ void Toy_runVM(Toy_VM* vm) {
void Toy_freeVM(Toy_VM* vm) {
//clear the stack
Toy_freeStack(&vm->stack);
Toy_freeStack(vm->stack);
//TODO: clear the scope
//free the bytecode
TOY_FREE_ARRAY(unsigned char, vm->bc, vm->bcSize);
Toy_initVM(vm);
free(vm->bc);
Toy_resetVM(vm);
}
void Toy_resetVM(Toy_VM* vm) {
vm->bc = NULL;
vm->bcSize = 0;
vm->routine = NULL;
vm->routineSize = 0;
vm->paramCount = 0;
vm->jumpsCount = 0;
vm->dataCount = 0;
vm->subsCount = 0;
vm->paramAddr = 0;
vm->codeAddr = 0;
vm->jumpsAddr = 0;
vm->dataAddr = 0;
vm->subsAddr = 0;
vm->routineCounter = 0;
//init the scope & stack
vm->stack = NULL;
}

View File

@@ -30,15 +30,14 @@ typedef struct Toy_VM {
//TODO: needs string util for identifiers
//stack - immediate-level values only
Toy_Stack stack;
Toy_Stack* stack;
} Toy_VM;
TOY_API void Toy_initVM(Toy_VM* vm);
TOY_API void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, int bytecodeSize); //process the version data
TOY_API void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine); //process the routine only
TOY_API void Toy_runVM(Toy_VM* vm);
TOY_API void Toy_freeVM(Toy_VM* vm);
TOY_API void Toy_resetVM(Toy_VM* vm);
//TODO: inject extra data