mirror of
https://github.com/janet-lang/janet
synced 2024-12-26 00:10:27 +00:00
More work on open hashing implementation of objects.
Add metatable support for callable objects.
This commit is contained in:
parent
9c94bfab4d
commit
1effd9e740
@ -3,8 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Flag for immutability in an otherwise mutable datastructure */
|
||||
#define GST_IMMUTABLE 1
|
||||
/* Max search depth for classes. */
|
||||
#define GST_MAX_SEARCH_DEPTH 128
|
||||
|
||||
/* Verious types */
|
||||
typedef enum GstType {
|
||||
@ -89,7 +89,6 @@ struct GstArray {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
GstValue *data;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* A bytebuffer type. Used as a mutable string or string builder. */
|
||||
@ -97,7 +96,6 @@ struct GstBuffer {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
uint8_t *data;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* The main Gst type, an obect. Objects are just hashtables with some meta
|
||||
@ -106,7 +104,6 @@ struct GstObject {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
GstBucket **buckets;
|
||||
uint32_t flags;
|
||||
GstObject *meta;
|
||||
};
|
||||
|
||||
@ -168,6 +165,8 @@ struct Gst {
|
||||
/* Return state */
|
||||
const char *crash;
|
||||
GstValue ret; /* Returned value from gst_start. Also holds errors. */
|
||||
/* Temporary array for use in function dispatch */
|
||||
GstValue tempArray[GST_MAX_SEARCH_DEPTH];
|
||||
};
|
||||
|
||||
/* Bytecode */
|
||||
|
354
dict.c
354
dict.c
@ -1,130 +1,264 @@
|
||||
#include "dict.h"
|
||||
#include "util.h"
|
||||
#include "value.h"
|
||||
#include "vm.h"
|
||||
|
||||
/****/
|
||||
/* Bag implementation */
|
||||
/****/
|
||||
|
||||
/* Find a kv pair in a bag */
|
||||
static GstValue *gst_object_bag_find(GstDict *obj, GstValue key) {
|
||||
GstValue *start = obj->data;
|
||||
GstValue *end = obj->data + obj->count * 2;
|
||||
while (start < end) {
|
||||
if (gst_equals(*start, key))
|
||||
return start;
|
||||
start += 2;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check for string equality */
|
||||
static int str_equal_value(GstValue v, const char *str, uint32_t len, uint32_t hash) {
|
||||
uint32_t i;
|
||||
if (v.type != GST_STRING) return 0;
|
||||
if (gst_string_length(str) != len) return 0;
|
||||
if (!gst_string_hash(str))
|
||||
gst_string_hash(str) = gst_string_calchash((uint8_t *)str);
|
||||
if (gst_string_hash(str) != hash) return 0;
|
||||
for (i = 0; i < len; ++i)
|
||||
if (str[1] != v.data.string[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Find key value pair with c string key */
|
||||
static GstValue *gst_object_bag_findcstring(GstDict *obj, const char *key) {
|
||||
uint32_t len, hash;
|
||||
for (len = 0; key[len]; ++len);
|
||||
hash = gst_cstring_calchash((uint8_t *)key, len);
|
||||
GstValue *start = obj->data;
|
||||
GstValue *end = obj->data + obj->count * 2;
|
||||
while (start < end) {
|
||||
if (start->type == GST_STRING) {
|
||||
uint8_t *str = start->data.string;
|
||||
if (gst_string_length(str) == len) {
|
||||
if (!gst_string_hash(str))
|
||||
gst_string_hash(str) = gst_string_calchash(str);
|
||||
if (gst_string_hash(str) == hash) {
|
||||
return start
|
||||
}
|
||||
}
|
||||
}
|
||||
start += 2;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove a key from a bag */
|
||||
static void gst_object_bag_remove(GstDict *obj, GstValue key) {
|
||||
GstValue *kv = gst_object_bag_find(obj, key);
|
||||
if (kv != NULL) {
|
||||
GstValue *lastKv = obj->data + --obj->count * 2;
|
||||
kv[0] = lastKv[0];
|
||||
kv[1] = lastKv[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a key to a bag */
|
||||
static void gst_object_bag_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) {
|
||||
GstValue *kv = gst_object_bag_find(obj, key);
|
||||
if (kv != NULL) {
|
||||
/* Replace value */
|
||||
kv[1] = value;
|
||||
} else {
|
||||
/* Check for need to resize */
|
||||
if (obj->count + 1 > obj->capacity) {
|
||||
uint32_t newCap = 2 * obj->count + 2;
|
||||
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * 2 * newCap);
|
||||
gst_memcpy(newData, obj->data, obj->count * 2 * sizeof(GstValue));
|
||||
obj->data = newData;
|
||||
obj->capacity = newCap;
|
||||
}
|
||||
/* Push to end */
|
||||
kv = obj->data + obj->count * 2;
|
||||
kv[0] = key;
|
||||
kv[1] = value;
|
||||
++obj->count;
|
||||
}
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Hashtable implementaion */
|
||||
/****/
|
||||
|
||||
/* Add a key value pair to a given array. Returns if key successfully added. */
|
||||
static void hash_putkv(GstValue *data, uint32_t cap, GstValue key, GstValue value) {
|
||||
GstValue *end = data + 2 * cap;
|
||||
GstValue *start = data + (gst_hash(key) % cap) * 2;
|
||||
GstValue *bucket;
|
||||
/* Check second half of array */
|
||||
for (bucket = start; bucket < end; bucket += 2) {
|
||||
if (bucket[0].type == GST_NIL) {
|
||||
bucket[0] = key;
|
||||
bucket[1] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Check first half of array */
|
||||
for (bucket = data; bucket < start; bucket += 2) {
|
||||
if (bucket[0].type == GST_NIL) {
|
||||
bucket[0] = key;
|
||||
bucket[1] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Should never reach here - data would be full */
|
||||
}
|
||||
|
||||
/* Find a bucket in the hastable */
|
||||
static GstValue *hash_findkv(GstValue *data, uint32_t cap, GstValue key, GstValue **out) {
|
||||
GstValue *end = data + 2 * cap;
|
||||
GstValue *start = data + (gst_hash(key) % cap) * 2;
|
||||
GstValue *bucket;
|
||||
/* Check second half of array */
|
||||
for (bucket = start; bucket < end; bucket += 2)
|
||||
if (bucket[0].type == GST_NIL)
|
||||
if (bucket[1].type == GST_BOOLEAN) /* Check if just marked deleted */
|
||||
continue;
|
||||
else {
|
||||
*out = bucket;
|
||||
return NULL;
|
||||
}
|
||||
else if (gst_equals(bucket[0], key))
|
||||
return bucket;
|
||||
/* Check first half of array */
|
||||
for (bucket = data; bucket < start; bucket += 2)
|
||||
if (bucket[0].type == GST_NIL)
|
||||
if (bucket[1].type == GST_BOOLEAN) /* Check if just marked deleted */
|
||||
continue;
|
||||
else {
|
||||
*out = bucket;
|
||||
return NULL;
|
||||
}
|
||||
else if (gst_equals(bucket[0], key))
|
||||
return bucket;
|
||||
/* Should never reach here - data would be full */
|
||||
*out = bucket;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Resize internal hashtable. Also works if currently a bag. */
|
||||
static void gst_object_rehash(Gst *vm, GstDict *obj, uint32_t capacity) {
|
||||
GstValue *toData, *fromBucket, *toBucket, *toStart *fromEnd, *toEnd;
|
||||
toData = gst_alloc(vm, capacity * 2 * sizeof(GstValue));
|
||||
toEnd = toData + 2 * capacity;
|
||||
fromBucket = obj->data;
|
||||
fromEnd = fromBucket + obj->count * 2;
|
||||
for (; fromBucket < fromEnd; fromBucket += 2) {
|
||||
if (fromBucket[0].type == GST_NIL) continue;
|
||||
toStart = toData + (gst_hash(fromBucket[0]) % capacity) * 2;
|
||||
/* Check second half of array */
|
||||
for (toBucket = toStart; toBucket < toEnd; toBucket += 2) {
|
||||
if (toBucket[0].type == GST_NIL) {
|
||||
toBucket[0] = fromBucket[0];
|
||||
toBucket[1] = fromBucket[1];
|
||||
goto finish_put;
|
||||
}
|
||||
}
|
||||
/* Check first half of array */
|
||||
for (toBucket = toData; toBucket < toStart; toBucket += 2) {
|
||||
if (toBucket[0].type == GST_NIL) {
|
||||
toBucket[0] = fromBucket[0];
|
||||
toBucket[1] = fromBucket[1];
|
||||
goto finish_put;
|
||||
}
|
||||
}
|
||||
/* Error if we got here - backing array to small. */
|
||||
;
|
||||
/* Continue. */
|
||||
finish_put: continue;
|
||||
}
|
||||
obj->capacity = capacity;
|
||||
obj->data = toData;
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Interface */
|
||||
/****/
|
||||
|
||||
/* Initialize a dictionary */
|
||||
GstDict *gst_dict_init(GstDict *dict, uint32_t capacity) {
|
||||
GstDictBucket *buckets = gst_raw_calloc(1, sizeof(GstDictBucket) * capacity);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
dict->buckets = buckets;
|
||||
GstDict *gst_dict(Gst *vm, uint32_t capacity) {
|
||||
GstDict *dict = gst_alloc(vm, sizeof(GstDict));
|
||||
GstValue *data = gst_zalloc(vm, sizeof(GstValue) * 2 * capacity);
|
||||
dict->data = data;
|
||||
dict->capacity = capacity;
|
||||
dict->count = 0;
|
||||
dict->flags = (capacity < GST_OBJECT_BAG_THRESHOLD) ? GST_OBJECT_FLAG_ISBAG : 0;
|
||||
return dict;
|
||||
}
|
||||
|
||||
/* Deinitialize a dictionary */
|
||||
GstDict *gst_dict_free(GstDict *dict) {
|
||||
gst_raw_free(dict->buckets);
|
||||
}
|
||||
|
||||
/* Rehash a dictionary */
|
||||
GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity) {
|
||||
GstDictBucket *newBuckets = gst_raw_calloc(1, sizeof(GstDictBucket) * newCapacity);
|
||||
GstDictBucket *buckets = dict->buckets;
|
||||
uint32_t i, j;
|
||||
if (newBuckets == NULL)
|
||||
return NULL;
|
||||
for (i = 0; i < dict->capacity; ++i) {
|
||||
int index;
|
||||
if (!(buckets[i].flags & GST_DICT_FLAG_OCCUPIED)) continue;
|
||||
if (buckets[i].flags & GST_DICT_FLAG_TOMBSTONE) continue;
|
||||
index = gst_hash(buckets[i].key) % newCapacity;
|
||||
for (j = index; j < dict->capacity; ++j) {
|
||||
if (newBuckets[j].flags & GST_DICT_FLAG_OCCUPIED) continue;
|
||||
newBuckets[j] = buckets[i];
|
||||
goto done;
|
||||
}
|
||||
for (j = 0; j < index; ++j) {
|
||||
if (newBuckets[j].flags & GST_DICT_FLAG_OCCUPIED) continue;
|
||||
newBuckets[j] = buckets[i];
|
||||
goto done;
|
||||
}
|
||||
/* Error - could not rehash a bucket - this should never happen */
|
||||
gst_raw_free(newBuckets);
|
||||
return NULL;
|
||||
/* Successfully rehashed bucket */
|
||||
done:
|
||||
}
|
||||
dict->capacity = newCapacity;
|
||||
return dict;
|
||||
}
|
||||
|
||||
/* Find a bucket with a given key */
|
||||
static int gst_dict_find(GstDict *dict, GstValue key, GstDictBucket **out) {
|
||||
uint32_t index, i;
|
||||
GstDictBucket *buckets = dict->buckets;
|
||||
index = gst_hash(key) % dict->capacity;
|
||||
for (i = index; i < dict->capacity; ++i) {
|
||||
if (buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) continue;
|
||||
if (!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) continue;
|
||||
if (!gst_equals(key, buckets[i].key)) continue;
|
||||
*out = buckets + i;
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < index; ++i) {
|
||||
if (buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) continue;
|
||||
if (!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) continue;
|
||||
if (!gst_equals(key, buckets[i].key)) continue;
|
||||
*out = buckets + i;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get item from dictionary */
|
||||
int gst_dict_get(GstDict *dict, GstValue key, GstValue *value) {
|
||||
GstDictBucket *bucket;
|
||||
int found = gst_dict_find(dict, key, &bucket);
|
||||
if (found)
|
||||
*value = bucket->value;
|
||||
return found;
|
||||
GstValue gst_dict_get(GstDict *dict, GstValue key) {
|
||||
GstValue *bucket *notused;
|
||||
if (dict->flags & GST_OBJECT_FLAG_ISBAG) {
|
||||
bucket = gst_object_bag_find(dict, key);
|
||||
} else {
|
||||
bucket = hash_findkv(obj->data, obj->capacity, key, ¬used);
|
||||
}
|
||||
if (bucket != NULL) {
|
||||
return bucket[1];
|
||||
} else {
|
||||
GstValue ret;
|
||||
ret.type = GST_NIL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get item with c string key */
|
||||
GstValue gst_dict_get_cstring(GstDict *dict, const char *key);
|
||||
|
||||
/* Add item to dictionary */
|
||||
GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value) {
|
||||
/* Check if we need to increase capacity. The load factor is low
|
||||
* because we are using linear probing */
|
||||
uint32_t index, i;
|
||||
uint32_t newCap = dict->count * 2 + 1;
|
||||
GstBucket *buckets;
|
||||
if (newCap > dict->capacity) {
|
||||
dict = gst_dict_rehash(dict, newCap);
|
||||
if (!dict) return dict;
|
||||
void gst_dict_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) {
|
||||
if (obj->flags & GST_OBJECT_FLAG_ISBAG) {
|
||||
if (obj->count > GST_OBJECT_BAG_THRESHOLD) {
|
||||
/* Change to hashtable */
|
||||
obj->flags |= GST_OBJECT_FLAG_ISBAG;
|
||||
gst_object_rehash(vm, obj, 4 * obj->count);
|
||||
goto put_hash;
|
||||
}
|
||||
gst_object_bag_put(vm, obj, key, value);
|
||||
} else {
|
||||
GstValue *bucket, *out;
|
||||
put_hash:
|
||||
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
|
||||
if (bucket != NULL) {
|
||||
bucket[1] = value;
|
||||
} else {
|
||||
/* Check for resize */
|
||||
if (obj->count + 1 > obj->capacity) {
|
||||
gst_object_rehash(vm, obj, 2 * (obj->count + 1));
|
||||
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
|
||||
}
|
||||
out[0] = key;
|
||||
out[1] = value;
|
||||
++obj->count;
|
||||
}
|
||||
}
|
||||
index = gst_hash(key) % dict->capacity;
|
||||
buckets = dict->buckets;
|
||||
for (i = index; i < dict->capacity; ++i) {
|
||||
if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) ||
|
||||
!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED))
|
||||
continue;
|
||||
dict->buckets[i].key = key;
|
||||
dict->buckets[i].value = value;
|
||||
dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED;
|
||||
dict->count++;
|
||||
return dict;
|
||||
}
|
||||
for (i = 0; i < index; ++i) {
|
||||
if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) ||
|
||||
!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED))
|
||||
continue;
|
||||
dict->buckets[i].key = key;
|
||||
dict->buckets[i].value = value;
|
||||
dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED;
|
||||
dict->count++;
|
||||
return dict;
|
||||
}
|
||||
/* Error - should never get here */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove item from dictionary */
|
||||
int gst_dict_remove(GstDict *dict, GstValue key) {
|
||||
GstDictBucket *bucket;
|
||||
int found = gst_dict_find(dict, key, &bucket);
|
||||
if (found) {
|
||||
bucket->flags |= GST_DICT_FLAGS_TOMBSTONE;
|
||||
dict->count--;
|
||||
void gst_dict_remove(GstDict *obj, GstValue key) {
|
||||
if (obj->flags & GST_OBJECT_FLAG_ISBAG) {
|
||||
gst_object_bag_remove(obj, key);
|
||||
} else {
|
||||
GstValue *bucket, *out;
|
||||
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
|
||||
if (bucket != NULL) {
|
||||
--obj->count;
|
||||
bucket[0].type = GST_NIL;
|
||||
bucket[1].type = GST_BOOLEAN;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
35
dict.h
35
dict.h
@ -3,39 +3,36 @@
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
#define GST_DICT_FLAG_OCCUPIED 1
|
||||
#define GST_DICT_FLAG_TOMBSTONE 2
|
||||
/* Indicates object is implement as unsorted array of keypairs */
|
||||
#define GST_OBJECT_FLAG_ISBAG (1 << 31)
|
||||
|
||||
typedef struct GstDictBucket GstDictBucket;
|
||||
struct GstDictBucket {
|
||||
GstValue key;
|
||||
GstValue value;
|
||||
uint8_t flags;
|
||||
};
|
||||
/* Indicates object is immutable */
|
||||
#define GST_OBJECT_IMMUTABLE (1 << 30)
|
||||
|
||||
/* Count at which the object goes from a linear search to a hash table */
|
||||
#define GST_OBJECT_BAG_THRESHOLD 8
|
||||
|
||||
typedef struct GstDict GstDict;
|
||||
struct GstDict {
|
||||
uint32_t capacity;
|
||||
uint32_t count;
|
||||
GstDictBucket *buckets;
|
||||
uint32_t flags;
|
||||
GstValue *data;
|
||||
};
|
||||
|
||||
/* Initialize a dictionary */
|
||||
GstDict *gst_dict_init(GstDict *dict, uint32_t capacity);
|
||||
|
||||
/* Deinitialize a dictionary */
|
||||
GstDict *gst_dict_free(GstDict *dict);
|
||||
|
||||
/* Rehash a dictionary */
|
||||
GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity);
|
||||
GstDict *gst_dict(Gst *vm, uint32_t capacity);
|
||||
|
||||
/* Get item from dictionary */
|
||||
int gst_dict_get(GstDict *dict, GstValue key, GstValue *value);
|
||||
GstValue gst_dict_get(GstDict *dict, GstValue key);
|
||||
|
||||
/* Get c string from object */
|
||||
GstValue gst_dict_get_cstring(GstDict *dict, const char *key);
|
||||
|
||||
/* Add item to dictionary */
|
||||
GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value);
|
||||
void gst_dict_put(Gst *vm, GstDict *dict, GstValue key, GstValue value);
|
||||
|
||||
/* Remove item from dictionary */
|
||||
int gst_dict_remove(GstDict *dict, GstValue key);
|
||||
void gst_dict_remove(GstDict *dict, GstValue key);
|
||||
|
||||
#endif // dict_h_INCLUDED
|
||||
|
9
ds.c
9
ds.c
@ -14,7 +14,6 @@ GstBuffer *gst_buffer(Gst *vm, uint32_t capacity) {
|
||||
buffer->data = data;
|
||||
buffer->count = 0;
|
||||
buffer->capacity = capacity;
|
||||
buffer->flags = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@ -76,7 +75,6 @@ GstArray *gst_array(Gst * vm, uint32_t capacity) {
|
||||
array->data = data;
|
||||
array->count = 0;
|
||||
array->capacity = capacity;
|
||||
array->flags = 0;
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -181,7 +179,7 @@ GstObject* gst_object(Gst *vm, uint32_t capacity) {
|
||||
o->buckets = buckets;
|
||||
o->capacity = capacity;
|
||||
o->count = 0;
|
||||
o->flags = 0;
|
||||
o->meta = NULL;
|
||||
return o;
|
||||
}
|
||||
|
||||
@ -230,9 +228,8 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
|
||||
|
||||
/* Get a value of the object with a cstring key */
|
||||
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
|
||||
const char *end = key;
|
||||
while (*end++);
|
||||
uint32_t len = end - key;
|
||||
uint32_t len;
|
||||
for (len = 0; key[len]; ++len);
|
||||
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
|
||||
uint32_t index = hash % obj->capacity;
|
||||
GstBucket *bucket = obj->buckets[index];
|
||||
|
6
gc.c
6
gc.c
@ -149,6 +149,12 @@ void gst_mark(Gst *vm, GstValue *x) {
|
||||
bucket = bucket->next;
|
||||
}
|
||||
}
|
||||
if (x->data.object->meta != NULL) {
|
||||
GstValue temp;
|
||||
temp.type = GST_OBJECT;
|
||||
temp.data.object = x->data.object->meta;
|
||||
gst_mark(vm, &temp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
11
thread.c
Normal file
11
thread.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "datatypes.h"
|
||||
|
||||
/* Push Value to thread */
|
||||
void gst_thread_push(Gst *vm, GstThread *thread, GstValue x) {
|
||||
|
||||
}
|
||||
|
||||
/* Push stack frame to thread */
|
||||
void gst_thread_pushframe(Gst *vm, GstThread *thread, GstValue callee) {
|
||||
|
||||
}
|
8
value.c
8
value.c
@ -444,16 +444,12 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
||||
int32_t index;
|
||||
switch (ds.type) {
|
||||
case GST_ARRAY:
|
||||
if (ds.data.array->flags & GST_IMMUTABLE)
|
||||
return "cannot set immutable value";
|
||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||
index = to_index(key.data.number, ds.data.array->count);
|
||||
if (index == -1) return "invalid array access";
|
||||
ds.data.array->data[index] = value;
|
||||
break;
|
||||
case GST_BYTEBUFFER:
|
||||
if (ds.data.buffer->flags & GST_IMMUTABLE)
|
||||
return "cannot set immutable value";
|
||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||
if (value.type != GST_NUMBER) return "expected numeric value";
|
||||
index = to_index(key.data.number, ds.data.buffer->count);
|
||||
@ -461,8 +457,8 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
||||
ds.data.buffer->data[index] = to_byte(value.data.number);
|
||||
break;
|
||||
case GST_OBJECT:
|
||||
if (ds.data.object->flags & GST_IMMUTABLE)
|
||||
return "cannot set immutable value";
|
||||
if (ds.data.object->meta != NULL) {
|
||||
}
|
||||
gst_object_put(vm, ds.data.object, key, value);
|
||||
break;
|
||||
default:
|
||||
|
82
vm.c
82
vm.c
@ -67,7 +67,7 @@ static void gst_load(Gst *vm, GstValue callee) {
|
||||
#define GST_STATE_WRITE() do { \
|
||||
*vm->thread = thread; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Start running the VM from where it left off. Continue running
|
||||
* until the stack size is smaller than minStackSize. */
|
||||
static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
@ -382,27 +382,53 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
case GST_OP_PSH: /* Push stack frame */
|
||||
{
|
||||
GstValue *nextStack;
|
||||
uint32_t expectedArity, normalArity, arity, varArgs, i, locals, nextCount;
|
||||
uint32_t argSlots, fullArgSlots, arity, prefixCount, varArgs, tupleCount, i, locals, nextCount;
|
||||
|
||||
/* Get arguments to op */
|
||||
temp = stack[pc[1]];
|
||||
arity = pc[2];
|
||||
|
||||
/* Get the size of next stack frame */
|
||||
if (temp.type == GST_FUNCTION) {
|
||||
GstFunction *fn = temp.data.function;
|
||||
locals = fn->def->locals;
|
||||
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
||||
expectedArity = fn->def->arity;
|
||||
if (arity > expectedArity)
|
||||
normalArity = expectedArity;
|
||||
else
|
||||
normalArity = arity;
|
||||
} else if (temp.type == GST_CFUNCTION) {
|
||||
locals = normalArity = expectedArity = arity;
|
||||
varArgs = 0;
|
||||
} else {
|
||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||
prefixCount = 0;
|
||||
recur:
|
||||
switch(temp.type) {
|
||||
default: gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||
case GST_FUNCTION:
|
||||
{
|
||||
GstFunction *fn = temp.data.function;
|
||||
locals = fn->def->locals;
|
||||
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
||||
argSlots = fn->def->arity;
|
||||
if (arity + prefixCount > argSlots) {
|
||||
fullArgSlots = argSlots;
|
||||
tupleCount = arity + prefixCount - fullArgSlots;
|
||||
} else {
|
||||
fullArgSlots = arity + prefixCount;
|
||||
tupleCount = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_CFUNCTION:
|
||||
{
|
||||
locals = argSlots = fullArgSlots = arity + prefixCount;
|
||||
varArgs = tupleCount = 0;
|
||||
break;
|
||||
}
|
||||
case GST_OBJECT:
|
||||
{
|
||||
GstObject *meta = temp.data.object->meta;
|
||||
if (meta == NULL) gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||
vm->tempArray[prefixCount++] = temp;
|
||||
temp = gst_object_get_cstring(meta, "call");
|
||||
goto recur;
|
||||
}
|
||||
case GST_USERDATA:
|
||||
{
|
||||
GstObject *meta = ((GstUserdataHeader *)temp.data.pointer - 1)->meta;
|
||||
vm->tempArray[prefixCount++] = temp;
|
||||
temp = gst_object_get_cstring(meta, "call");
|
||||
goto recur;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get next frame size */
|
||||
@ -426,12 +452,16 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
gst_frame_env(nextStack) = NULL;
|
||||
gst_frame_callee(nextStack) = temp;
|
||||
gst_frame_errjmp(nextStack) = NULL;
|
||||
|
||||
/* Write prefix args to stack */
|
||||
for (i = 0; i < prefixCount; ++i)
|
||||
nextStack[i] = vm->tempArray[i];
|
||||
|
||||
/* Write arguments to new stack */
|
||||
for (i = 0; i < normalArity; ++i)
|
||||
nextStack[i] = stack[pc[3 + i]];
|
||||
for (; i < fullArgSlots; ++i)
|
||||
nextStack[i] = stack[pc[3 + i - prefixCount]];
|
||||
|
||||
/* Clear stack */
|
||||
/* Clear rest of stack */
|
||||
for (; i < locals; ++i)
|
||||
nextStack[i].type = GST_NIL;
|
||||
|
||||
@ -439,11 +469,14 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
if (varArgs) {
|
||||
GstValue *tuple;
|
||||
uint32_t j;
|
||||
tuple = gst_tuple(vm, arity - expectedArity);
|
||||
for (j = expectedArity; j < arity; ++j)
|
||||
tuple[j - expectedArity] = stack[pc[3 + j]];
|
||||
nextStack[expectedArity].type = GST_TUPLE;
|
||||
nextStack[expectedArity].data.tuple = tuple;
|
||||
tuple = gst_tuple(vm, tupleCount);
|
||||
for (j = argSlots; j < arity; ++j)
|
||||
if (j < prefixCount)
|
||||
tuple[j - argSlots] = vm->tempArray[j - prefixCount];
|
||||
else
|
||||
tuple[j - argSlots] = stack[pc[3 + j - prefixCount]];
|
||||
nextStack[argSlots].type = GST_TUPLE;
|
||||
nextStack[argSlots].data.tuple = tuple;
|
||||
}
|
||||
|
||||
/* Increment pc */
|
||||
@ -486,6 +519,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
} else if (v1.type == GST_CFUNCTION) {
|
||||
int status;
|
||||
GST_STATE_WRITE();
|
||||
vm->ret.type = GST_NIL;
|
||||
status = v1.data.cfunction(vm);
|
||||
GST_STATE_SYNC();
|
||||
if (status == GST_RETURN_OK)
|
||||
|
Loading…
Reference in New Issue
Block a user