cleanups and more test cases

This commit is contained in:
cozis
2022-08-12 05:30:41 +02:00
parent f630830b3c
commit 18e936f0ad
145 changed files with 1445 additions and 234 deletions
+524
View File
@@ -0,0 +1,524 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
** | |
** | WHAT IS THIS FILE? |
** | This is the implementation of the "Heap", an object that provides the |
** | rest of the program with memory and manages it by claiming it back |
** | implicitly when it's not in use anymore. To determine which memory is |
** | used or not, the heap system must be aware of the object graph. This is |
** | the reason why the Heap is tightly coupled to the object model. |
** | |
** | HOW DOES IT WORK? |
** | The collection algorithm is move-and-compact. The allocator is a |
** | bump-pointer allocator. When the base pool of memory is filled up, |
** | further allocations are forwarded to the stdlib's malloc, but are kept |
** | track of by putting them in a linked list. When the language's runtime |
** | system decides to free up some memory, a new heap is allocated and the |
** | live objects are moved to it, then the old heap is freed. The references |
** | between live objects are updated when moving them. Some objects implement|
** | destructors that must be called when a new heap is allocated and they're |
** | not moved to it. An auxiliary list of allocated objects with destructors |
** | is stored alongside the heap. When the live objects are moved and the |
** | ones to be destroyed are left in the old one, the list of objects with |
** | destructors is iterated over and the objects in it that weren't moved are|
** | destroied and removed from the list. This approach becomes linearly |
** | slower with the number of allocated objects with destructors, but it's |
** | assumed that not many of them implement them. |
** | If during a collection the new memory pool is filled up, then an error is|
** | thrown to the parent system. |
** | |
** | HOW ARE POINTERS UPDATED? |
** | Basically, when an object is moved from the old to the new heap, the |
** | location of the object in the old heap is overwritten with a placeholder |
** | object that holds the new location. Then all of it's references are |
** | iterated over and if they refer to placeholders they're updated with the |
** | new location of the object. If the references don't refer to placeholder |
** | objects, then the referred objects are moved too. This is a recursive |
** | process that, when applied to the root object of the program, moves all |
** | reachable objects to the new heap and updates the pointers. The |
** | complexity of this algorithm is proportional to the number of live |
** | objects. |
** | |
** | WHAT IS A BUMP-POINTER ALLOCATOR? |
** | A bump-pointer allocator is a minimal memory management system. A |
** | contiguous pool of memory is allocated. On a higher level, allocations |
** | are stacked one after another until the pool is all used up. This is done|
** | by having a pointer that points to the first free buffer of the pool. |
** | Initially, it points to the first byte of the pool. When N bytes are |
** | requested, the value of the pointer is given to the caller and then it's |
** | incremented by the allocated amount. When the pool has less free memory |
** | than what is requested, the allocation fails. |
** +--------------------------------------------------------------------------+
*/
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "objects.h"
#if USING_VALGRIND
#include <valgrind/memcheck.h>
#endif
typedef struct OflowAlloc OflowAlloc;
struct OflowAlloc {
OflowAlloc *prev;
char body[];
};
typedef struct {
Object *object;
_Bool (*destructor)(Object*, Error*);
} PendingDestruct;
struct xHeap {
int objcount;
int size;
int used;
int total;
void *body;
OflowAlloc *oflow;
PendingDestruct *pend;
int pend_size, pend_used;
_Bool collecting;
_Bool collection_failed;
int movedcount;
void *old_body;
int old_used;
int old_total;
OflowAlloc *old_oflow;
Error *error;
};
Heap *Heap_New(int size)
{
if(size < 0)
size = 65536;
Heap *heap = malloc(sizeof(Heap));
if(heap == NULL)
return NULL;
heap->objcount = 0;
heap->total = 0;
heap->size = size;
heap->used = 0;
heap->body = malloc(size);
heap->pend = NULL;
heap->pend_size = 0;
heap->pend_used = 0;
heap->oflow = 0;
heap->collecting = 0;
if(heap->body == NULL)
{
free(heap);
return NULL;
}
#if USING_VALGRIND
VALGRIND_CREATE_MEMPOOL(heap, 0, 0);
#endif
return heap;
}
void Heap_Free(Heap *heap)
{
#if USING_VALGRIND
VALGRIND_DESTROY_MEMPOOL(heap);
#endif
Error error;
Error_Init(&error);
for(int i = 0; i < heap->pend_used; i += 1)
{
heap->pend[i].destructor(heap->pend[i].object, &error);
if(error.occurred)
{
// Errors occurred! We can't do anything about
// it now though.
Error_Free(&error);
Error_Init(&error);
}
}
while(heap->oflow)
{
OflowAlloc *prev = heap->oflow->prev;
free(heap->oflow);
heap->oflow = prev;
}
free(heap->pend);
free(heap->body);
free(heap);
}
void *Heap_GetPointer(Heap *heap)
{
return heap->body;
}
unsigned int Heap_GetSize(Heap *heap)
{
return heap->size;
}
unsigned int Heap_GetObjectCount(Heap *heap)
{
return heap->objcount;
}
float Heap_GetUsagePercentage(Heap *heap)
{
return 100.0 * heap->total / heap->size;
}
void *Heap_Malloc(Heap *heap, TypeObject *type, Error *err)
{
_Bool requires_destruct = type->free != NULL;
if(requires_destruct)
{
// This type of object requires
// a destructor to be called.
if(heap->pend == NULL)
{
int n = 8;
heap->pend = malloc(n * sizeof(PendingDestruct));
if(heap->pend == NULL)
{
Error_Report(err, 1, "No memory");
return NULL;
}
heap->pend_used = 0;
heap->pend_size = n;
}
else if(heap->pend_size == heap->pend_used)
{
int factor = 2;
void *new_pend = realloc(heap->pend, factor * heap->pend_size * sizeof(PendingDestruct));
if(new_pend == NULL)
{
Error_Report(err, 1, "No memory");
return NULL;
}
heap->pend = new_pend;
heap->pend_size *= factor;
}
assert(heap->pend_size > heap->pend_used);
}
int size = type->size;
if(size < (int) sizeof(MovedObject))
size = sizeof(MovedObject);
void *addr = Heap_RawMalloc(heap, type->size, err);
if(addr == NULL)
return NULL;
Object *obj = addr;
obj->type = type;
obj->flags = 0;
if(type->init && !type->init(obj, err))
return NULL;
obj->type = type;
obj->flags = 0;
if(requires_destruct)
heap->pend[heap->pend_used++] = (PendingDestruct) { .object = obj, .destructor = obj->type->free };
heap->objcount += 1;
return (Object*) addr;
}
void *Heap_RawMalloc(Heap *heap, int size, Error *err)
{
assert(err);
assert(heap);
assert(size > -1);
void *addr;
int padding = heap->used;
if(heap->used & 7)
heap->used = (heap->used & ~7) + 8;
padding = heap->used - padding;
if(heap->used + size > heap->size)
{
if(heap->collecting)
{
Error_Report(err, 1, "Out of heap");
return NULL;
}
OflowAlloc *oflow = malloc(sizeof(OflowAlloc) + size);
if(oflow == 0)
return 0;
oflow->prev = heap->oflow;
heap->oflow = oflow;
addr = oflow->body;
}
else
{
assert(heap->used + size <= heap->size);
addr = heap->body + heap->used;
heap->used += size;
}
heap->total += size + padding;
assert(((intptr_t) addr) % 8 == 0);
#if USING_VALGRIND
VALGRIND_MEMPOOL_ALLOC(heap, addr, size);
#endif
return addr;
}
_Bool Heap_StartCollection(Heap *heap, Error *error)
{
assert(heap->collecting == 0);
void *new_body = malloc(heap->size);
if(new_body == NULL)
{
Error_Report(error, 1, "No memory");
return 0;
}
heap->old_body = heap->body;
heap->old_used = heap->used;
heap->old_total = heap->total;
heap->old_oflow = heap->oflow;
heap->total = 0;
heap->body = new_body;
heap->used = 0;
heap->oflow = NULL;
heap->collecting = 1;
heap->collection_failed = 0;
heap->movedcount = 0;
heap->error = error;
return 1;
}
_Bool Heap_StopCollection(Heap *heap)
{
assert(heap->collecting == 1);
if(heap->collection_failed)
{
free(heap->old_body);
return 0;
}
/* Call destructors here */
{
int i = 0;
while(i < heap->pend_used)
{
Object *obj = heap->pend[i].object;
if(obj->flags & Object_MOVED)
{
heap->pend[i].object = ((MovedObject*) heap->pend[i].object)->new_location;
i += 1;
}
else
{
// We need to call the destructor.
heap->pend[i].destructor(obj, heap->error);
if(heap->error->occurred)
return 0; // There will be leaks.
heap->pend[i] = heap->pend[heap->pend_used-1];
heap->pend_used -= 1;
}
}
if(heap->pend_size / 2 > heap->pend_used)
{
// Downsize
void *temp = realloc(heap->pend, heap->pend_size / 2 * sizeof(PendingDestruct));
if(temp != NULL)
{
heap->pend = temp;
heap->pend_size /= 2;
}
}
}
while(heap->old_oflow)
{
OflowAlloc *prev = heap->old_oflow->prev;
free(heap->old_oflow);
heap->old_oflow = prev;
}
free(heap->old_body);
heap->collecting = 0;
heap->objcount = heap->movedcount;
return 1;
}
void Heap_CollectExtension(void **referer, unsigned int size, void *userp)
{
Heap *heap = userp;
assert(referer != NULL);
assert(heap->collecting);
void *old_location = *referer;
if(heap->collection_failed || old_location == NULL)
return;
void *new_location = Heap_RawMalloc(heap, size, heap->error);
if(new_location == NULL)
{
heap->collection_failed = 1;
return;
}
memcpy(new_location, old_location, size);
*referer = new_location;
}
void Heap_CollectReference(Object **referer, void *userp)
{
Heap *heap = userp;
assert(referer != NULL);
assert(heap->collecting);
Object *old_location = *referer;
if(heap->collection_failed || old_location == NULL)
return;
if(old_location->flags & Object_MOVED)
// The object was already moved.
*referer = ((MovedObject*) old_location)->new_location;
else
{
Object *new_location;
// This object wasn't moved to
// the new heap yet.
if(old_location->flags & Object_STATIC)
// The object doesn't need to be moved
// since it was statically allocated.
new_location = old_location;
else
{
// Get some information.
TypeObject *type = old_location->type;
int size = type->size;
// Copy the object to a new location.
{
new_location = Heap_RawMalloc(heap, size, heap->error);
if(new_location == NULL)
{
heap->collection_failed = 1;
return;
}
memcpy(new_location, old_location, size);
}
// Set the old location as moved and
// leave the reference to the new
// location.
{
old_location->flags |= Object_MOVED;
assert((int) sizeof(MovedObject) <= size);
((MovedObject*) old_location)->new_location = new_location;
}
heap->movedcount += 1;
}
// Collect the reference to the type.
if((Object*) new_location->type != new_location)
Heap_CollectReference((Object**) &new_location->type, heap);
// Collect all of the references to
// extensions allocate using the GC'd
// heap.
Object_WalkExtensions(new_location,
Heap_CollectExtension, heap);
// Now collect all of the children.
Object_WalkReferences(new_location,
Heap_CollectReference, heap);
// Update the referer
*referer = new_location;
}
}
+114
View File
@@ -0,0 +1,114 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include <string.h>
#include "objects.h"
static _Bool to_bool(Object *obj, Error *err);
static void print(Object *obj, FILE *fp);
static _Bool op_eql(Object *self, Object *other);
static int hash(Object *self);
static Object *copy(Object *self, Heap *heap, Error *err);
static TypeObject t_bool = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "bool",
.size = sizeof (Object),
.atomic = ATMTP_BOOL,
.hash = hash,
.copy = copy,
.to_bool = to_bool,
.print = print,
.op_eql = op_eql,
};
static Object the_true_object = {
.type = &t_bool,
.flags = Object_STATIC,
};
static Object the_false_object = {
.type = &t_bool,
.flags = Object_STATIC,
};
static int hash(Object *self)
{
assert(self != NULL);
assert(self->type == &t_bool);
if(self == &the_true_object)
return 1;
assert(self == &the_false_object);
return 0;
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
(void) heap;
(void) err;
return self;
}
static _Bool op_eql(Object *self, Object *other)
{
assert(self != NULL);
assert(self->type == &t_bool);
assert(other != NULL);
assert(other->type == &t_bool);
return self == other;
}
static _Bool to_bool(Object *obj, Error *err)
{
assert(obj);
assert(err);
assert(Object_GetType(obj) == &t_bool);
return obj == &the_true_object;
}
Object *Object_FromBool(_Bool val, Heap *heap, Error *error)
{
(void) heap;
(void) error;
return val ? &the_true_object : &the_false_object;
}
static void print(Object *obj, FILE *fp)
{
assert(fp != NULL);
assert(obj != NULL);
assert(obj->type == &t_bool);
fprintf(fp, obj == &the_true_object ? "true" : "false");
}
+450
View File
@@ -0,0 +1,450 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "../utils/defs.h"
#include "objects.h"
typedef struct {
Object base;
int size;
unsigned char *body;
} BufferObject;
typedef struct {
Object base;
BufferObject *sliced;
int offset,
length;
} BufferSliceObject;
static Object *buffer_select(Object *self, Object *key, Heap *heap, Error *err);
static _Bool buffer_insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
static int buffer_count(Object *self);
static void buffer_print(Object *obj, FILE *fp);
static _Bool buffer_free(Object *self, Error *error);
static Object *slice_select(Object *self, Object *key, Heap *heap, Error *err);
static _Bool slice_insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
static int slice_count(Object *self);
static void slice_print(Object *obj, FILE *fp);
static TypeObject t_buffer = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "buffer",
.size = sizeof (BufferObject),
.select = buffer_select,
.insert = buffer_insert,
.count = buffer_count,
.print = buffer_print,
.free = buffer_free,
};
static TypeObject t_buffer_slice = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "buffer slice",
.size = sizeof (BufferSliceObject),
.select = slice_select,
.insert = slice_insert,
.count = slice_count,
.print = slice_print,
};
#define THRESHOLD 128
_Bool Object_IsBuffer(Object *obj)
{
return obj->type == &t_buffer_slice || obj->type == &t_buffer;
}
Object *Object_NewBuffer(int size, Heap *heap, Error *error)
{
assert(size >= 0);
// Make the thing.
BufferObject *obj;
{
obj = (BufferObject*) Heap_Malloc(heap, &t_buffer, error);
if(obj == NULL)
return NULL;
unsigned char *body;
if(size > THRESHOLD)
{
body = malloc(sizeof(unsigned char) * size);
if(body == NULL)
{
Error_Report(error, 1, "No memory");
return NULL;
}
}
else
{
body = Heap_RawMalloc(heap, sizeof(unsigned char) * size, error);
if(body == NULL)
return NULL;
}
obj->size = size;
obj->body = body;
memset(obj->body, 0, size);
}
return (Object*) obj;
}
static _Bool buffer_free(Object *self, Error *error)
{
(void) error;
BufferObject *buffer = (BufferObject*) self;
if(buffer->size > THRESHOLD)
free(buffer->body);
return 1;
}
Object *Object_SliceBuffer(Object *buffer, int offset, int length, Heap *heap, Error *error)
{
if(buffer->type != &t_buffer && buffer->type != &t_buffer_slice)
{
Error_Report(error, 0, "Not a buffer or a buffer slice");
return NULL;
}
BufferSliceObject *slice;
if(buffer->type == &t_buffer)
{
BufferObject *original = (BufferObject*) buffer;
if(offset == 0 && length == original->size)
return buffer;
slice = (BufferSliceObject*) Heap_Malloc(heap, &t_buffer_slice, error);
if(slice == NULL)
return NULL;
if(offset < 0 || offset >= original->size)
{
Error_Report(error, 0, "offset out of range");
return NULL;
}
if(length < 0 || length >= original->size)
{
Error_Report(error, 0, "length out of range");
return NULL;
}
if(offset + length > original->size)
{
Error_Report(error, 0, "slice out of range");
return NULL;
}
slice->sliced = original;
slice->offset = offset;
slice->length = length;
}
else
{
assert(buffer->type == &t_buffer_slice);
slice = (BufferSliceObject*) Heap_Malloc(heap, &t_buffer_slice, error);
if(slice == NULL)
return NULL;
BufferSliceObject *original = (BufferSliceObject*) buffer;
if(offset < 0 || offset >= original->length)
{
Error_Report(error, 0, "offset out of range");
return NULL;
}
if(length < 0 || length >= original->length)
{
Error_Report(error, 0, "length out of range");
return NULL;
}
if(offset + length > original->length)
{
Error_Report(error, 0, "slice out of range");
return NULL;
}
slice->sliced = original->sliced;
slice->offset = original->offset + offset;
slice->length = length;
}
return (Object*) slice;
}
void *Object_GetBufferAddrAndSize(Object *obj, int *size, Error *error)
{
if(obj->type != &t_buffer && obj->type != &t_buffer_slice)
{
Error_Report(error, 0, "Not a buffer or a buffer slice");
return NULL;
}
if(obj->type == &t_buffer)
{
BufferObject *buffer = (BufferObject*) obj;
if(size)
*size = buffer->size;
return buffer->body;
}
else
{
assert(obj->type == &t_buffer_slice);
BufferSliceObject *slice = (BufferSliceObject*) obj;
if(size)
*size = slice->length;
return slice->sliced->body + slice->offset;
}
}
static Object *buffer_select(Object *self, Object *key, Heap *heap, Error *error)
{
assert(self != NULL);
assert(self->type == &t_buffer);
assert(key != NULL);
assert(heap != NULL);
assert(error != NULL);
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
BufferObject *buffer = (BufferObject*) self;
if(idx < 0 || idx >= buffer->size)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
unsigned char byte = buffer->body[idx];
return Object_FromInt(byte, heap, error);
}
static Object *slice_select(Object *self, Object *key, Heap *heap, Error *error)
{
assert(self != NULL);
assert(self->type == &t_buffer_slice);
assert(key != NULL);
assert(heap != NULL);
assert(error != NULL);
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
BufferSliceObject *slice = (BufferSliceObject*) self;
if(idx < 0 || idx >= slice->length)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
unsigned char byte = slice->sliced->body[slice->offset + idx];
return Object_FromInt(byte, heap, error);
}
static _Bool buffer_insert(Object *self, Object *key, Object *val, Heap *heap, Error *error)
{
assert(error != NULL);
assert(key != NULL);
assert(val != NULL);
assert(heap != NULL);
assert(self != NULL);
assert(self->type == &t_buffer);
BufferObject *buffer = (BufferObject*) self;
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
if(idx < 0 || idx >= buffer->size)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
long long int qword = Object_ToInt(val, error);
if(qword > 255 || qword < 0)
{
Error_Report(error, 0, "Not in range [0, 255]");
return NULL;
}
unsigned char byte = qword & 0xff;
if(error->occurred == 1)
return 0;
buffer->body[idx] = byte;
return 1;
}
static _Bool slice_insert(Object *self, Object *key, Object *val, Heap *heap, Error *error)
{
assert(error != NULL);
assert(key != NULL);
assert(val != NULL);
assert(heap != NULL);
assert(self != NULL);
assert(self->type == &t_buffer_slice);
BufferSliceObject *slice = (BufferSliceObject*) self;
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
if(idx < 0 || idx >= slice->length)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
long long int qword = Object_ToInt(val, error);
if(qword > 255 || qword < 0)
{
Error_Report(error, 0, "Not in range [0, 255]");
return NULL;
}
unsigned char byte = qword & 0xff;
if(error->occurred == 1)
return 0;
slice->sliced->body[slice->offset + idx] = byte;
return 1;
}
static int buffer_count(Object *self)
{
BufferObject *buffer = (BufferObject*) self;
return buffer->size;
}
static int slice_count(Object *self)
{
BufferSliceObject *slice = (BufferSliceObject*) self;
return slice->length;
}
static void print_bytes(FILE *fp, unsigned char *addr, int size)
{
fprintf(fp, "[");
for(int i = 0; i < size; i += 1)
{
unsigned char byte, low, high;
byte = addr[i];
low = byte & 0xf;
high = byte >> 4;
assert(low < 16);
assert(high < 16);
char c1, c2;
c1 = low < 10 ? low + '0' : low - 10 + 'A';
c2 = high < 10 ? high + '0' : high - 10 + 'A';
fprintf(fp, "%c%c", c2, c1);
if(i+1 < size)
fprintf(fp, ", ");
}
fprintf(fp, "]");
}
static void buffer_print(Object *self, FILE *fp)
{
BufferObject *buffer = (BufferObject*) self;
print_bytes(fp, buffer->body, buffer->size);
}
static void slice_print(Object *self, FILE *fp)
{
BufferSliceObject *slice = (BufferSliceObject*) self;
print_bytes(fp, slice->sliced->body + slice->offset, slice->length);
}
+97
View File
@@ -0,0 +1,97 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include "../utils/defs.h"
#include "objects.h"
typedef struct ClosureObject ClosureObject;
struct ClosureObject {
Object base;
ClosureObject *prev;
Object *vars;
};
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
static Object *select(Object *self, Object *key, Heap *heap, Error *err);
static TypeObject t_closure = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "closure",
.size = sizeof(ClosureObject),
.select = select,
.walk = walk,
};
Object *Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error)
{
ClosureObject *obj = (ClosureObject*) Heap_Malloc(heap, &t_closure, error);
if(obj == NULL)
return NULL;
if(parent != NULL && parent->type != &t_closure)
{
Error_Report(error, 0, "Object is not a closure");
return NULL;
}
obj->prev = (ClosureObject*) parent;
obj->vars = new_map;
return (Object*) obj;
}
static Object *select(Object *self, Object *key, Heap *heap, Error *err)
{
ClosureObject *closure = (ClosureObject*) self;
Object *selected = NULL;
while(closure != NULL && selected == NULL)
{
selected = Object_Select(closure->vars, key, heap, err);
if(err->occurred)
return NULL;
closure = closure->prev;
}
return selected;
}
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
{
ClosureObject *closure = (ClosureObject*) self;
callback((Object**) &closure->prev, userp);
callback(&closure->vars, userp);
}
+83
View File
@@ -0,0 +1,83 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include "objects.h"
typedef struct {
Object base;
DIR *dir;
} DirObject;
static _Bool dir_free(Object *obj, Error *error);
static TypeObject t_dir = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "Directory",
.size = sizeof(DirObject),
.free = dir_free,
};
_Bool Object_IsDir(Object *obj)
{
return obj->type == &t_dir;
}
Object *Object_FromDIR(DIR *handle, Heap *heap, Error *error)
{
DirObject *dob = (DirObject*) Heap_Malloc(heap, &t_dir, error);
if(dob == NULL)
return NULL;
dob->dir = handle;
return (Object*) dob;
}
DIR *Object_ToDIR(Object *obj, Error *error)
{
if(!Object_IsDir(obj))
{
Error_Report(error, 0, "Object is not a directory");
return NULL;
}
return ((DirObject*) obj)->dir;
}
static _Bool dir_free(Object *obj, Error *error)
{
DirObject *dob = (DirObject*) obj;
if(closedir(dob->dir) == 0)
return 1;
Error_Report(error, 0, "Failed to close directory");
return 0;
}
+83
View File
@@ -0,0 +1,83 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include "objects.h"
typedef struct {
Object base;
FILE *fp;
} FileObject;
static _Bool file_free(Object *self, Error *error);
static TypeObject t_file = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "File",
.size = sizeof(FileObject),
.free = file_free,
};
_Bool Object_IsFile(Object *obj)
{
return obj->type == &t_file;
}
FILE *Object_ToStream(Object *obj, Error *error)
{
if(!Object_IsFile(obj))
{
Error_Report(error, 0, "Object is not a file");
return NULL;
}
return ((FileObject*) obj)->fp;
}
Object *Object_FromStream(FILE *fp, Heap *heap, Error *error)
{
FileObject *fob = Heap_Malloc(heap, &t_file, error);
if(fob == NULL)
return NULL;
fob->fp = fp;
return (Object*) fob;
}
static _Bool file_free(Object *self, Error *error)
{
FileObject *fob = (FileObject*) self;
if(fclose(fob->fp) == 0)
return 1;
Error_Report(error, 0, "Failed to close stream");
return 0;
}
+120
View File
@@ -0,0 +1,120 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include "objects.h"
#include "../utils/hash.h"
static double to_float(Object *obj, Error *err);
static void print(Object *obj, FILE *fp);
static _Bool op_eql(Object *self, Object *other);
static int hash(Object *self);
static Object *copy(Object *self, Heap *heap, Error *err);
typedef struct {
Object base;
double val;
} FloatObject;
static TypeObject t_float = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "float",
.size = sizeof (FloatObject),
.atomic = ATMTP_FLOAT,
.hash = hash,
.copy = copy,
.to_float = to_float,
.print = print,
.op_eql = op_eql
};
static int hash(Object *self)
{
assert(self != NULL);
assert(self->type == &t_float);
FloatObject *iobj = (FloatObject*) self;
return hashbytes((unsigned char*) &iobj->val, sizeof(iobj->val));
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
(void) heap;
(void) err;
return self;
}
static _Bool op_eql(Object *self, Object *other)
{
assert(self != NULL);
assert(self->type == &t_float);
assert(other != NULL);
assert(other->type == &t_float);
FloatObject *i1, *i2;
i1 = (FloatObject*) self;
i2 = (FloatObject*) other;
return i1->val == i2->val;
}
static double to_float(Object *obj, Error *err)
{
assert(obj);
assert(err);
assert(Object_GetType(obj) == &t_float);
(void) err;
return ((FloatObject*) obj)->val;
}
Object *Object_FromFloat(double val, Heap *heap, Error *error)
{
FloatObject *obj = (FloatObject*) Heap_Malloc(heap, &t_float, error);
if(obj == 0)
return 0;
obj->val = val;
return (Object*) obj;
}
static void print(Object *obj, FILE *fp)
{
assert(fp != NULL);
assert(obj != NULL);
assert(obj->type == &t_float);
fprintf(fp, "%2.2f", ((FloatObject*) obj)->val);
}
+124
View File
@@ -0,0 +1,124 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include <string.h>
#include "objects.h"
#include "../utils/hash.h"
static long long int to_int(Object *obj, Error *err);
static void print(Object *obj, FILE *fp);
static _Bool op_eql(Object *self, Object *other);
static int hash(Object *self);
static Object *copy(Object *self, Heap *heap, Error *err);
typedef struct {
Object base;
long long int val;
} IntObject;
static TypeObject t_int = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "int",
.size = sizeof (IntObject),
.atomic = ATMTP_INT,
.hash = hash,
.copy = copy,
.to_int = to_int,
.print = print,
.op_eql = op_eql,
};
static int hash(Object *self)
{
assert(self != NULL);
assert(self->type == &t_int);
IntObject *iobj = (IntObject*) self;
return hashbytes((unsigned char*) &iobj->val, sizeof(iobj->val));
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
(void) heap;
(void) err;
return self;
}
static long long int to_int(Object *obj, Error *err)
{
assert(obj != NULL);
assert(err != NULL);
assert(Object_GetType(obj) == &t_int);
(void) err;
return ((IntObject*) obj)->val;
}
Object *Object_FromInt(long long int val, Heap *heap, Error *error)
{
assert(heap != NULL);
assert(error != NULL);
IntObject *obj = (IntObject*) Heap_Malloc(heap, &t_int, error);
if(obj == 0)
return 0;
obj->val = val;
return (Object*) obj;
}
static void print(Object *obj, FILE *fp)
{
assert(fp != NULL);
assert(obj != NULL);
assert(obj->type == &t_int);
fprintf(fp, "%lld", ((IntObject*) obj)->val);
}
static _Bool op_eql(Object *self, Object *other)
{
assert(self != NULL);
assert(self->type == &t_int);
assert(other != NULL);
assert(other->type == &t_int);
IntObject *i1, *i2;
i1 = (IntObject*) self;
i2 = (IntObject*) other;
return i1->val == i2->val;
}
+272
View File
@@ -0,0 +1,272 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include <string.h>
#include "../utils/defs.h"
#include "objects.h"
typedef struct {
Object base;
int capacity, count;
Object **vals;
} ListObject;
static Object *select(Object *self, Object *key, Heap *heap, Error *err);
static _Bool insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
static int count(Object *self);
static void print(Object *obj, FILE *fp);
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
static Object *copy(Object *self, Heap *heap, Error *err);
static int hash(Object *self);
static TypeObject t_list = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "list",
.size = sizeof (ListObject),
.copy = copy,
.hash = hash,
.select = select,
.insert = insert,
.count = count,
.print = print,
.walk = walk,
.walkexts = walkexts,
};
static int hash(Object *self)
{
assert(self != NULL);
assert(self->type == &t_list);
ListObject *ls = (ListObject*) self;
int h = 0;
// The hash is the sum of the nested
// hashes. It's not a smart solution
// but it works for now.
for(int i = 0; i < ls->count; i += 1)
h += Object_Hash(ls->vals[i]);
return h;
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
(void) heap;
(void) err;
ListObject *ls = (ListObject*) self;
ListObject *ls2 = (ListObject*) Object_NewList(ls->count, heap, err);
if(ls2 == NULL) return NULL;
for(int i = 0; i < ls->count; i += 1)
{
ls2->vals[i] = Object_Copy(ls->vals[i], heap, err);
if(err->occurred) return NULL;
}
ls2->count = ls->count;
return (Object*) ls2;
}
Object *Object_NewList(int capacity, Heap *heap, Error *error)
{
// Handle default args.
if(capacity < 8)
capacity = 8;
// Make the thing.
ListObject *obj;
{
obj = (ListObject*) Heap_Malloc(heap, &t_list, error);
if(obj == NULL)
return NULL;
obj->count = 0;
obj->capacity = capacity;
obj->vals = Heap_RawMalloc(heap, sizeof(Object*) * capacity, error);
if(obj->vals == NULL)
return NULL;
}
return (Object*) obj;
}
Object *Object_NewList2(int num, Object **items, Heap *heap, Error *error)
{
assert(num > -1);
ListObject *list = (ListObject*) Object_NewList(num, heap, error);
if(list == NULL)
return NULL;
memcpy(list->vals, items, num * sizeof(Object*));
list->count = num;
return (Object*) list;
}
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
{
ListObject *list = (ListObject*) self;
for(int i = 0; i < list->count; i += 1)
callback(&list->vals[i], userp);
}
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
{
ListObject *list = (ListObject*) self;
callback((void**) &list->vals, sizeof(Object) * list->capacity, userp);
}
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
{
assert(self != NULL);
assert(self->type == &t_list);
assert(key != NULL);
assert(heap != NULL);
assert(error != NULL);
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
ListObject *list = (ListObject*) self;
if(idx < 0 || idx >= list->count)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
return list->vals[idx];
}
static unsigned int calc_new_capacity(unsigned int old_capacity)
{
return old_capacity * 2;
}
static _Bool grow(ListObject *list, Heap *heap, Error *error)
{
assert(list != NULL);
int new_capacity = calc_new_capacity(list->capacity);
Object **vals = Heap_RawMalloc(heap, sizeof(Object*) * new_capacity, error);
if(vals == NULL)
return 0;
for(int i = 0; i < list->count; i += 1)
vals[i] = list->vals[i];
list->vals = vals;
list->capacity = new_capacity;
return 1;
}
static _Bool insert(Object *self, Object *key, Object *val, Heap *heap, Error *error)
{
assert(error != NULL);
assert(key != NULL);
assert(val != NULL);
assert(heap != NULL);
assert(self != NULL);
assert(self->type == &t_list);
ListObject *list = (ListObject*) self;
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
if(idx < 0 || idx > list->count)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
if(idx == list->count)
{
if(list->count == list->capacity)
if(!grow(list, heap, error))
return 0;
list->vals[list->count] = val;
list->count += 1;
}
else
{
list->vals[idx] = val;
}
return 1;
}
static int count(Object *self)
{
ListObject *list = (ListObject*) self;
return list->count;
}
static void print(Object *self, FILE *fp)
{
ListObject *list = (ListObject*) self;
fprintf(fp, "[");
for(int i = 0; i < list->count; i += 1)
{
Object_Print(list->vals[i], fp);
if(i+1 < list->count)
fprintf(fp, ", ");
}
fprintf(fp, "]");
}
+373
View File
@@ -0,0 +1,373 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include "../utils/defs.h"
#include "objects.h"
typedef struct {
Object base;
int mapper_size, count;
int *mapper;
Object **keys;
Object **vals;
} MapObject;
static Object *select(Object *self, Object *key, Heap *heap, Error *err);
static _Bool insert(Object *self, Object *key, Object *val, Heap *heap, Error *err);
static int count(Object *self);
static void print(Object *self, FILE *fp);
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp);
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
static Object *copy(Object *self, Heap *heap, Error *err);
static int hash(Object *self);
static TypeObject t_map = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "map",
.size = sizeof (MapObject),
.copy = copy,
.hash = hash,
.select = select,
.insert = insert,
.count = count,
.print = print,
.walk = walk,
.walkexts = walkexts,
};
static inline int calc_capacity(int mapper_size)
{
return mapper_size * 2.0 / 3.0;
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
MapObject *m1 = (MapObject*) self;
Object *m2 = Object_NewMap(m1->count, heap, err);
if(m2 == NULL) return NULL;
for(int i = 0; i < m1->count; i += 1)
{
Object *key, *key_cpy;
Object *val, *val_cpy;
key = m1->keys[i];
val = m1->vals[i];
key_cpy = Object_Copy(key, heap, err);
if(key_cpy == NULL) return NULL;
val_cpy = Object_Copy(val, heap, err);
if(val_cpy == NULL) return NULL;
if(!Object_Insert(m2, key_cpy, val_cpy, heap, err))
return NULL;
}
return (Object*) m2;
}
static int hash(Object *self)
{
MapObject *m = (MapObject*) self;
int h = 0;
// The hash of the map is the sum of the
// hashes of each key and each item.
for(int i = 0; i < m->count; i += 1)
h += Object_Hash(m->keys[i])
+ Object_Hash(m->vals[i]);
return h;
}
Object *Object_NewMap(int num, Heap *heap, Error *error)
{
// Handle default args.
if(num < 0)
num = 0;
// Calculate initial mapper size.
int mapper_size, capacity;
{
mapper_size = 8;
while(calc_capacity(mapper_size) < num)
mapper_size <<= 1;
capacity = calc_capacity(mapper_size);
}
// Make the thing.
MapObject *obj = (MapObject*) Heap_Malloc(heap, &t_map, error);
{
if(obj == 0)
return 0;
obj->mapper_size = mapper_size;
obj->count = 0;
obj->mapper = Heap_RawMalloc(heap, sizeof(int) * mapper_size, error);
obj->keys = Heap_RawMalloc(heap, sizeof(Object*) * capacity, error);
obj->vals = Heap_RawMalloc(heap, sizeof(Object*) * capacity, error);
if(obj->mapper == NULL || obj->keys == NULL || obj->vals == NULL)
return NULL;
}
for(int i = 0; i < mapper_size; i += 1)
obj->mapper[i] = -1;
return (Object*) obj;
}
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
{
MapObject *map = (MapObject*) self;
for(int i = 0; i < map->count; i += 1)
{
callback(&map->keys[i], userp);
callback(&map->vals[i], userp);
}
}
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
{
assert(self->type == &t_map);
MapObject *map = (MapObject*) self;
int capacity = calc_capacity(map->mapper_size);
callback((void**) &map->mapper, sizeof(int) * map->mapper_size, userp);
callback((void**) &map->keys, sizeof(Object*) * capacity, userp);
callback((void**) &map->vals, sizeof(Object*) * capacity, userp);
}
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
{
assert(self != NULL);
assert(self->type == &t_map);
assert(key != NULL);
assert(heap != NULL);
assert(error != NULL);
MapObject *map = (MapObject*) self;
unsigned int mask = map->mapper_size - 1;
unsigned int hash = Object_Hash(key);
unsigned int pert = hash;
int i = hash & mask;
while(1)
{
int k = map->mapper[i];
if(k == -1)
{
// Empty slot.
// This key is not present.
return NULL;
}
else
{
// Found an item.
// Is it the right one?
assert(k >= 0);
if(Object_Compare(key, map->keys[k], error))
// Found it!
return map->vals[k];
if(error->occurred)
// Key doesn't implement compare.
return 0;
// Not the one we wanted.
}
pert >>= 5;
i = (i * 5 + pert + 1) & mask;
}
UNREACHABLE;
return NULL;
}
static _Bool grow(MapObject *map, Heap *heap, Error *error)
{
assert(map != NULL);
int new_mapper_size = map->mapper_size << 1;
int new_capacity = calc_capacity(new_mapper_size);
int *mapper = Heap_RawMalloc(heap, sizeof(int) * new_mapper_size, error);
Object **keys = Heap_RawMalloc(heap, sizeof(Object*) * new_capacity, error);
Object **vals = Heap_RawMalloc(heap, sizeof(Object*) * new_capacity, error);
if(mapper == NULL || keys == NULL || vals == NULL)
return 0;
for(int i = 0; i < map->count; i += 1)
{
keys[i] = map->keys[i];
vals[i] = map->vals[i];
}
for(int i = 0; i < new_mapper_size; i += 1)
mapper[i] = -1;
// Rehash everything.
for(int i = 0; i < map->count; i += 1)
{
// This won't trigger an error because the key
// surely has a hash method since we already
// hashed it once.
int hash = Object_Hash(keys[i]);
int mask = new_mapper_size - 1;
int pert = hash;
int j = hash & mask;
while(1)
{
if(mapper[j] == -1)
{
// No collision.
// Insert here.
mapper[j] = i;
break;
}
// Collided. Find a new place.
pert >>= 5;
j = (j * 5 + pert + 1) & mask;
}
}
// Done.
map->mapper = mapper;
map->mapper_size = new_mapper_size;
map->keys = keys;
map->vals = vals;
return 1;
}
static _Bool insert(Object *self, Object *key, Object *val, Heap *heap, Error *error)
{
assert(error != NULL);
assert(key != NULL);
assert(val != NULL);
assert(heap != NULL);
assert(self != NULL);
assert(self->type == &t_map);
MapObject *map = (MapObject*) self;
if(map->count == calc_capacity(map->mapper_size))
if(!grow(map, heap, error))
return 0;
unsigned int mask = map->mapper_size - 1;
unsigned int hash = Object_Hash(key);
unsigned int pert = hash;
int i = hash & mask;
while(1)
{
int k = map->mapper[i];
if(k == -1)
{
// Empty slot. We can insert it here.
Object *key_copy = Object_Copy(key, heap, error);
if(key_copy == NULL)
return NULL;
map->mapper[i] = map->count;
map->keys[map->count] = key_copy;
map->vals[map->count] = val;
map->count += 1;
return 1;
}
else
{
assert(k >= 0);
if(Object_Compare(key, map->keys[k], error))
{
// Already inserted.
// Overwrite the value.
map->vals[k] = val;
return 1;
}
if(error->occurred)
// Key doesn't implement compare.
return 0;
// Collision.
}
pert >>= 5;
i = (i * 5 + pert + 1) & mask;
}
UNREACHABLE;
return 0;
}
static int count(Object *self)
{
MapObject *map = (MapObject*) self;
return map->count;
}
static void print(Object *self, FILE *fp)
{
MapObject *map = (MapObject*) self;
fprintf(fp, "{");
for(int i = 0; i < map->count; i += 1)
{
Object_Print(map->keys[i], fp);
fprintf(fp, ": ");
Object_Print(map->vals[i], fp);
if(i+1 < map->count)
fprintf(fp, ", ");
}
fprintf(fp, "}");
}
+95
View File
@@ -0,0 +1,95 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include <string.h>
#include "objects.h"
static _Bool op_eql(Object *self, Object *other);
static void print(Object *obj, FILE *fp);
static int hash(Object *self);
static Object *copy(Object *self, Heap *heap, Error *err);
static TypeObject t_none = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "none",
.size = sizeof (Object),
.hash = hash,
.copy = copy,
.print = print,
.op_eql = op_eql,
};
static Object the_none_object = {
.type = &t_none,
.flags = Object_STATIC,
};
_Bool Object_IsNone(Object *obj)
{
return obj == &the_none_object;
}
static int hash(Object *self)
{
assert(self == &the_none_object);
return 0;
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
(void) heap;
(void) err;
return self;
}
Object *Object_NewNone(Heap *heap, Error *error)
{
(void) heap;
(void) error;
return &the_none_object;
}
static void print(Object *obj, FILE *fp)
{
assert(fp != NULL);
assert(obj != NULL);
assert(obj->type == &t_none);
fprintf(fp, "none");
}
static _Bool op_eql(Object *self, Object *other)
{
(void) self;
assert(other->type == &t_none);
return 1;
}
+234
View File
@@ -0,0 +1,234 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <string.h>
#include <assert.h>
#include "../utils/defs.h"
#include "../utils/hash.h"
#include "../utils/utf8.h"
#include "objects.h"
typedef struct {
Object base;
int count;
int bytes;
char *body;
} StringObject;
static int hash(Object *self);
static int count(Object *self);
static Object *copy(Object *self, Heap *heap, Error *err);
static void print(Object *obj, FILE *fp);
static char *to_string(Object *self, int *size, Heap *heap, Error *err);
static _Bool op_eql(Object *self, Object *other);
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
static Object *select(Object *self, Object *key, Heap *heap, Error *error);
static TypeObject t_string = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "string",
.atomic = ATMTP_STRING,
.size = sizeof(StringObject),
.hash = hash,
.count = count,
.copy = copy,
.print = print,
.select = select,
.to_string = to_string,
.op_eql = op_eql,
.walkexts = walkexts,
};
static int char_index_to_offset(StringObject *str, int idx)
{
if(str->count == str->bytes)
return idx;
// Iterate over a string to find the first byte of
// the utf-8 character number [idx].
int scanned_bytes = 0,
last_code_len = 0;
while(idx > 0)
{
last_code_len = utf8_sequence_to_utf32_codepoint(str->body + scanned_bytes, str->bytes - scanned_bytes, NULL);
scanned_bytes += last_code_len;
idx -= 1;
assert(scanned_bytes <= str->bytes);
}
assert(idx == 0);
return scanned_bytes;
}
static Object *select(Object *self, Object *key, Heap *heap, Error *error)
{
assert(self != NULL && self->type == &t_string);
assert(key != NULL && heap != NULL && error != NULL);
if(!Object_IsInt(key))
{
Error_Report(error, 0, "Non integer key");
return NULL;
}
int idx = Object_ToInt(key, error);
assert(error->occurred == 0);
StringObject *str = (StringObject*) self;
if(idx < 0 || idx >= str->count)
{
Error_Report(error, 0, "Out of range index");
return NULL;
}
int byteoffset = char_index_to_offset(str, idx);
int codelength = utf8_sequence_to_utf32_codepoint(str->body + byteoffset, str->bytes - byteoffset, NULL);
return Object_FromString(str->body + byteoffset, codelength, heap, error);
}
static char *to_string(Object *self, int *size, Heap *heap, Error *err)
{
assert(self != NULL);
assert(self->type == &t_string);
(void) heap;
(void) err;
StringObject *s = (StringObject*) self;
if(size)
*size = s->bytes;
return s->body;
}
Object *Object_FromString(const char *str, int len, Heap *heap, Error *error)
{
assert(str != NULL);
assert(heap != NULL);
assert(error != NULL);
if(len < 0)
len = strlen(str);
int count = utf8_strlen(str, len);
if(count < 0)
{
Error_Report(error, 0, "Invalid UTF-8 sequence");
return NULL;
}
StringObject *strobj = Heap_Malloc(heap, &t_string, error);
if(strobj == NULL)
return NULL;
strobj->body = Heap_RawMalloc(heap, len+1, error);
strobj->bytes = len;
strobj->count = count;
if(strobj->body == NULL)
return NULL;
memcpy(strobj->body, str, len);
strobj->body[len] = '\0';
return (Object*) strobj;
}
static int count(Object *self)
{
assert(self != NULL);
assert(self->type == &t_string);
StringObject *strobj = (StringObject*) self;
return strobj->count;
}
static int hash(Object *self)
{
assert(self != NULL);
assert(self->type == &t_string);
StringObject *strobj = (StringObject*) self;
return hashbytes((unsigned char*) strobj->body, strobj->count);
}
static Object *copy(Object *self, Heap *heap, Error *err)
{
assert(self != NULL);
assert(self->type == &t_string);
assert(heap != NULL);
assert(err != NULL);
return self;
}
static _Bool op_eql(Object *self, Object *other)
{
assert(self != NULL);
assert(self->type == &t_string);
assert(other != NULL);
assert(other->type == &t_string);
StringObject *s1 = (StringObject*) self;
StringObject *s2 = (StringObject*) other;
_Bool match = s1->bytes == s2->bytes && !strncmp(s1->body, s2->body, s1->bytes);
return match;
}
static void print(Object *obj, FILE *fp)
{
assert(fp != NULL);
assert(obj != NULL);
assert(obj->type == &t_string);
StringObject *str = (StringObject*) obj;
fprintf(fp, "%.*s", str->bytes, str->body);
}
static void walkexts(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
{
StringObject *str = (StringObject*) self;
callback((void**) &str->body, str->bytes+1, userp);
}
+407
View File
@@ -0,0 +1,407 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include "../utils/defs.h"
#include "objects.h"
static _Bool op_eql(Object *self, Object *other);
TypeObject t_type = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "type",
.size = sizeof (TypeObject),
.op_eql = op_eql,
};
static _Bool op_eql(Object *self, Object *other)
{
return self == other;
}
const char *Object_GetName(const Object *obj)
{
assert(obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type);
const char *name = type->name;
assert(name);
return name;
}
const TypeObject *Object_GetType(const Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type;
}
unsigned int Object_GetSize(const Object *obj, Error *err)
{
assert(err != NULL);
assert(obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type);
return type->size;
}
unsigned int Object_GetDeepSize(const Object *obj, Error *err)
{
assert(err != NULL);
assert(obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->deepsize == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->deepsize(obj);
}
int Object_Hash(Object *obj)
{
assert(obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type != NULL);
assert(type->hash != NULL);
return type->hash(obj);
}
Object *Object_Copy(Object *obj, Heap *heap, Error *err)
{
assert(err != NULL);
assert(obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type != NULL);
if(type->copy == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return NULL;
}
return type->copy(obj, heap, err);
}
int Object_Call(Object *obj, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err)
{
assert(err != NULL && obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->call == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return -1;
}
return type->call(obj, argv, argc, rets, maxrets, heap, err);
}
void Object_Print(Object *obj, FILE *fp)
{
assert(obj != NULL);
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->print == NULL)
fprintf(fp, "<%s is unprintable>", Object_GetName(obj));
else
type->print(obj, fp);
}
Object *Object_Select(Object *coll, Object *key, Heap *heap, Error *err)
{
assert(err);
assert(key);
assert(coll);
const TypeObject *type = Object_GetType(coll);
assert(type);
if(type->select == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return NULL;
}
return type->select(coll, key, heap, err);
}
Object *Object_Delete(Object *coll, Object *key, Heap *heap, Error *err)
{
assert(err);
assert(key);
assert(coll);
const TypeObject *type = Object_GetType(coll);
assert(type);
if(type->delete == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return NULL;
}
return type->delete(coll, key, heap, err);
}
_Bool Object_Insert(Object *coll, Object *key, Object *val, Heap *heap, Error *err)
{
assert(err);
assert(key);
assert(coll);
const TypeObject *type = Object_GetType(coll);
assert(type);
if(type->insert == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return 0;
}
return type->insert(coll, key, val, heap, err);
}
int Object_Count(Object *coll, Error *err)
{
assert(err);
assert(coll);
const TypeObject *type = Object_GetType(coll);
assert(type);
if(type->count == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(coll), __func__);
return -1;
}
return type->count(coll);
}
Object *Object_Next(Object *iter, Heap *heap, Error *err)
{
assert(err);
assert(iter);
const TypeObject *type = Object_GetType(iter);
assert(type);
if(type->next == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(iter), __func__);
return NULL;
}
return type->next(iter, heap, err);
}
Object *Object_Prev(Object *iter, Heap *heap, Error *err)
{
assert(err);
assert(iter);
const TypeObject *type = Object_GetType(iter);
assert(type);
if(type->prev == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(iter), __func__);
return NULL;
}
return type->prev(iter, heap, err);
}
_Bool Object_IsInt(Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type->atomic == ATMTP_INT;
}
_Bool Object_IsBool(Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type->atomic == ATMTP_BOOL;
}
_Bool Object_IsFloat(Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type->atomic == ATMTP_FLOAT;
}
_Bool Object_IsString(Object *obj)
{
assert(obj != NULL);
assert(obj->type != NULL);
return obj->type->atomic == ATMTP_STRING;
}
long long int Object_ToInt(Object *obj, Error *err)
{
assert(err);
assert(obj);
if(!Object_IsInt(obj))
{
Error_Report(err, 0, "Object is not an integer");
return 0;
}
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->to_int == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->to_int(obj, err);
}
_Bool Object_ToBool(Object *obj, Error *err)
{
assert(err);
assert(obj);
if(!Object_IsBool(obj))
{
Error_Report(err, 0, "Object is not a boolean");
return 0;
}
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->to_bool == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->to_bool(obj, err);
}
double Object_ToFloat(Object *obj, Error *err)
{
assert(err);
assert(obj);
if(!Object_IsFloat(obj))
{
Error_Report(err, 0, "Object is not a floating");
return 0;
}
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->to_float == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return 0;
}
return type->to_float(obj, err);
}
const char *Object_ToString(Object *obj, int *size, Heap *heap, Error *err)
{
assert(err != NULL);
assert(obj != NULL);
if(!Object_IsString(obj))
{
Error_Report(err, 0, "Object is not a string");
return NULL;
}
const TypeObject *type = Object_GetType(obj);
assert(type);
if(type->to_string == NULL)
{
Error_Report(err, 0, "Object %s doesn't implement %s", Object_GetName(obj), __func__);
return NULL;
}
return type->to_string(obj, size, heap, err);
}
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error)
{
assert(obj1 != NULL);
assert(obj2 != NULL);
assert(error != NULL);
if(obj1->type != obj2->type)
return 0;
if(obj1->type->op_eql == NULL)
{
Error_Report(error, 0, "Object %s doesn't implement %s", Object_GetName(obj1), __func__);
return 0;
}
return obj1->type->op_eql(obj1, obj2);
}
void Object_WalkReferences(Object *parent, void (*callback)(Object **referer, void *userp), void *userp)
{
assert(parent != NULL);
if(parent->type->walk != NULL)
parent->type->walk(parent, callback, userp);
}
void Object_WalkExtensions(Object *parent, void (*callback)(void **referer, unsigned int size, void *userp), void *userp)
{
assert(parent != NULL);
if(parent->type->walkexts != NULL)
parent->type->walkexts(parent, callback, userp);
}
+172
View File
@@ -0,0 +1,172 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#ifndef OBJECT_H
#define OBJECT_H
#include <dirent.h>
#include <stdio.h>
#include "../utils/error.h"
typedef struct TypeObject TypeObject;
typedef struct Object Object;
typedef struct xHeap Heap;
struct Object {
TypeObject *type;
unsigned int flags;
};
typedef struct {
Object base;
Object *new_location;
} MovedObject;
typedef enum {
ATMTP_NOTATOMIC = 0,
ATMTP_INT,
ATMTP_BOOL,
ATMTP_FLOAT,
ATMTP_STRING,
} AtomicType;
struct TypeObject {
Object base;
// Any.
const char *name;
unsigned int size;
AtomicType atomic;
_Bool (*init)(Object *self, Error *err);
_Bool (*free)(Object *self, Error *err);
int (*hash)(Object *self);
Object* (*copy)(Object *self, Heap *heap, Error *err);
int (*call)(Object *self, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err);
void (*print)(Object *self, FILE *fp);
unsigned int (*deepsize)(const Object *self);
// Collections.
Object *(*select)(Object *self, Object *key, Heap *heap, Error *err);
Object *(*delete)(Object *self, Object *key, Heap *heap, Error *err);
_Bool (*insert)(Object *self, Object *key, Object *val, Heap *heap, Error *err);
int (*count)(Object *self);
// Iterators.
Object *(*next)(Object *self, Heap *heap, Error *err);
Object *(*prev)(Object *self, Heap *heap, Error *err);
// Some.
union {
long long int (*to_int)(Object *self, Error *err);
_Bool (*to_bool)(Object *self, Error *err);
double (*to_float)(Object *self, Error *err);
char *(*to_string)(Object *self, int *size, Heap *heap, Error *err);
};
_Bool (*op_eql)(Object *self, Object *other);
// All.
void (*walk) (Object *self, void (*callback)(Object **referer, void *userp), void *userp);
void (*walkexts)(Object *self, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
};
enum {
Object_STATIC = 1,
Object_MOVED = 2,
};
Heap* Heap_New(int size);
void Heap_Free(Heap *heap);
void* Heap_Malloc (Heap *heap, TypeObject *type, Error *err);
void* Heap_RawMalloc(Heap *heap, int size, Error *err);
_Bool Heap_StartCollection(Heap *heap, Error *error);
_Bool Heap_StopCollection(Heap *heap);
void Heap_CollectReference(Object **referer, void *heap);
float Heap_GetUsagePercentage(Heap *heap);
unsigned int Heap_GetObjectCount(Heap *heap);
void *Heap_GetPointer(Heap *heap);
unsigned int Heap_GetSize(Heap *heap);
const TypeObject* Object_GetType(const Object *obj);
const char* Object_GetName(const Object *obj);
unsigned int Object_GetSize(const Object *obj, Error *err);
unsigned int Object_GetDeepSize(const Object *obj, Error *err);
void *Object_GetBufferAddrAndSize(Object *obj, int *size, Error *error);
int Object_Hash (Object *obj);
Object* Object_Copy (Object *obj, Heap *heap, Error *err);
int Object_Call (Object *obj, Object **argv, unsigned int argc, Object **rets, unsigned int maxrets, Heap *heap, Error *err);
void Object_Print (Object *obj, FILE *fp);
Object* Object_Select(Object *coll, Object *key, Heap *heap, Error *err);
Object* Object_Delete(Object *coll, Object *key, Heap *heap, Error *err);
_Bool Object_Insert(Object *coll, Object *key, Object *val, Heap *heap, Error *err);
int Object_Count (Object *coll, Error *err);
Object* Object_Next (Object *iter, Heap *heap, Error *err);
Object* Object_Prev (Object *iter, Heap *heap, Error *err);
void Object_WalkReferences(Object *parent, void (*callback)(Object **referer, void *userp), void *userp);
void Object_WalkExtensions(Object *parent, void (*callback)(void **referer, unsigned int size, void *userp), void *userp);
Object* Object_NewMap(int num, Heap *heap, Error *error);
Object* Object_NewList(int capacity, Heap *heap, Error *error);
Object* Object_NewList2(int num, Object **items, Heap *heap, Error *error);
Object* Object_NewNone(Heap *heap, Error *error);
Object* Object_NewBuffer(int size, Heap *heap, Error *error);
Object* Object_NewClosure(Object *parent, Object *new_map, Heap *heap, Error *error);
Object* Object_SliceBuffer(Object *buffer, int offset, int length, Heap *heap, Error *error);
Object* Object_FromInt (long long int val, Heap *heap, Error *error);
Object* Object_FromBool (_Bool val, Heap *heap, Error *error);
Object* Object_FromFloat (double val, Heap *heap, Error *error);
Object* Object_FromString(const char *str, int len, Heap *heap, Error *error);
Object* Object_FromStream(FILE *fp, Heap *heap, Error *error);
Object* Object_FromDIR(DIR *handle, Heap *heap, Error *error);
_Bool Object_IsNone(Object *obj);
_Bool Object_IsInt(Object *obj);
_Bool Object_IsBool(Object *obj);
_Bool Object_IsFloat(Object *obj);
_Bool Object_IsString(Object *obj);
_Bool Object_IsBuffer(Object *obj);
_Bool Object_IsFile(Object *obj);
_Bool Object_IsDir(Object *obj);
long long int Object_ToInt (Object *obj, Error *err);
_Bool Object_ToBool (Object *obj, Error *err);
double Object_ToFloat(Object *obj, Error *err);
const char *Object_ToString(Object *obj, int *size, Heap *heap, Error *err);
DIR *Object_ToDIR(Object *obj, Error *error);
FILE *Object_ToStream(Object *obj, Error *error);
_Bool Object_Compare(Object *obj1, Object *obj2, Error *error);
extern TypeObject t_type;
#endif