2017-02-09 20:02:59 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "value.h"
|
2017-02-09 23:50:47 +00:00
|
|
|
#include "ds.h"
|
|
|
|
#include "vm.h"
|
2017-02-09 20:02:59 +00:00
|
|
|
|
2017-02-09 22:12:01 +00:00
|
|
|
/* Print the bytecode for a FuncDef */
|
|
|
|
static void FuncDefBytecodePrint(FuncDef * def) {
|
|
|
|
uint32_t count, i;
|
|
|
|
count = def->byteCodeLen;
|
2017-02-12 20:16:55 +00:00
|
|
|
printf("(bytecode)[");
|
|
|
|
if (count) {
|
|
|
|
for (i = 0; i < count - 1; ++i) {
|
|
|
|
printf("%04x ", def->byteCode[i]);
|
|
|
|
}
|
|
|
|
printf("%04x", def->byteCode[i]);
|
|
|
|
}
|
|
|
|
printf("]");
|
2017-02-09 22:12:01 +00:00
|
|
|
}
|
|
|
|
|
2017-02-09 20:02:59 +00:00
|
|
|
/* Print a value recursively. Used for debugging */
|
2017-02-10 04:28:11 +00:00
|
|
|
void ValuePrint(Value x, uint32_t indent) {
|
2017-02-09 20:02:59 +00:00
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < indent; ++i)
|
|
|
|
fputc(' ', stdout);
|
2017-02-10 04:28:11 +00:00
|
|
|
switch (x.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_NIL:
|
|
|
|
printf("<nil>");
|
|
|
|
break;
|
|
|
|
case TYPE_BOOLEAN:
|
2017-02-10 04:28:11 +00:00
|
|
|
printf(x.data.boolean ? "<true>" : "<false>");
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_NUMBER:
|
2017-02-10 04:28:11 +00:00
|
|
|
printf("%f", x.data.number);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_FORM:
|
|
|
|
case TYPE_ARRAY:
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.type == TYPE_ARRAY) printf(" [\n"); else printf(" (\n");
|
|
|
|
for (i = 0; i < x.data.array->count; ++i) {
|
|
|
|
ValuePrint(x.data.array->data[i], indent + 4);
|
2017-02-09 20:02:59 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
for (i = 0; i < indent; ++i) fputc(' ', stdout);
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.type == TYPE_ARRAY) printf(" ]\n"); else printf(" )\n");
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_STRING:
|
2017-02-10 04:28:11 +00:00
|
|
|
printf("\"%.*s\"", VStringSize(x.data.string), (char *) x.data.string);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_SYMBOL:
|
2017-02-10 04:28:11 +00:00
|
|
|
printf("%.*s", VStringSize(x.data.string), (char *) x.data.string);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_CFUNCTION:
|
|
|
|
printf("<cfunction>");
|
|
|
|
break;
|
|
|
|
case TYPE_FUNCTION:
|
2017-02-09 22:12:01 +00:00
|
|
|
printf("<function ");
|
2017-02-12 20:16:55 +00:00
|
|
|
FuncDefBytecodePrint(x.data.func->def);
|
|
|
|
printf(">");
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_DICTIONARY:
|
|
|
|
printf("<dictionary>");
|
|
|
|
break;
|
|
|
|
case TYPE_BYTEBUFFER:
|
|
|
|
printf("<bytebuffer>");
|
|
|
|
break;
|
|
|
|
case TYPE_FUNCDEF:
|
2017-02-09 22:12:01 +00:00
|
|
|
printf("<funcdef ");
|
2017-02-12 20:16:55 +00:00
|
|
|
FuncDefBytecodePrint(x.data.funcdef);
|
|
|
|
printf(">");
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_FUNCENV:
|
|
|
|
printf("<funcenv>");
|
|
|
|
break;
|
|
|
|
case TYPE_THREAD:
|
|
|
|
printf("<thread>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-09 23:50:47 +00:00
|
|
|
static uint8_t * LoadCString(VM * vm, const char * string, uint32_t len) {
|
|
|
|
uint8_t * data = VMAlloc(vm, len + 2 * sizeof(uint32_t));
|
2017-02-09 20:02:59 +00:00
|
|
|
data += 2 * sizeof(uint32_t);
|
|
|
|
VStringHash(data) = 0;
|
|
|
|
VStringSize(data) = len;
|
|
|
|
memcpy(data, string, len);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2017-02-09 23:50:47 +00:00
|
|
|
Value ValueLoadCString(VM * vm, const char * string) {
|
2017-02-09 20:02:59 +00:00
|
|
|
Value ret;
|
|
|
|
ret.type = TYPE_STRING;
|
2017-02-09 23:50:47 +00:00
|
|
|
ret.data.string = LoadCString(vm, string, strlen(string));
|
2017-02-09 20:02:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-02-09 23:50:47 +00:00
|
|
|
static uint8_t * NumberToString(VM * vm, Number x) {
|
2017-02-09 20:02:59 +00:00
|
|
|
static const uint32_t SIZE = 20;
|
2017-02-09 23:50:47 +00:00
|
|
|
uint8_t * data = VMAlloc(vm, SIZE + 2 * sizeof(uint32_t));
|
2017-02-09 20:02:59 +00:00
|
|
|
data += 2 * sizeof(uint32_t);
|
|
|
|
snprintf((char *) data, SIZE, "%.17g", x);
|
|
|
|
VStringHash(data) = 0;
|
|
|
|
VStringSize(data) = strlen((char *) data);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * HEX_CHARACTERS = "0123456789ABCDEF";
|
|
|
|
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
|
|
|
|
|
|
|
|
/* Returns a string description for a pointer */
|
2017-02-09 23:50:47 +00:00
|
|
|
static uint8_t * StringDescription(VM * vm, const char * title, uint32_t titlelen, void * pointer) {
|
2017-02-09 20:02:59 +00:00
|
|
|
uint32_t len = 3 + titlelen + sizeof(pointer) * 2;
|
|
|
|
uint32_t i;
|
2017-02-09 23:50:47 +00:00
|
|
|
uint8_t * data = VMAlloc(vm, len + 2 * sizeof(uint32_t));
|
2017-02-09 20:02:59 +00:00
|
|
|
uint8_t * c;
|
|
|
|
union {
|
|
|
|
uint8_t bytes[sizeof(void *)];
|
|
|
|
void * p;
|
|
|
|
} buf;
|
|
|
|
buf.p = pointer;
|
|
|
|
data += 2 * sizeof(uint32_t);
|
|
|
|
c = data;
|
|
|
|
*c++ = '<';
|
|
|
|
for (i = 0; i < titlelen; ++i) {
|
|
|
|
*c++ = ((uint8_t *)title) [i];
|
|
|
|
}
|
|
|
|
*c++ = ' ';
|
|
|
|
for (i = 0; i < sizeof(void *); ++i) {
|
|
|
|
uint8_t byte = buf.bytes[i];
|
|
|
|
*c++ = HEX(byte >> 4);
|
|
|
|
*c++ = HEX(byte & 0xF);
|
|
|
|
}
|
|
|
|
*c++ = '>';
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns a string pointer or NULL if could not allocate memory. */
|
2017-02-10 04:28:11 +00:00
|
|
|
uint8_t * ValueToString(VM * vm, Value x) {
|
|
|
|
switch (x.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_NIL:
|
2017-02-09 23:50:47 +00:00
|
|
|
return LoadCString(vm, "nil", 3);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_BOOLEAN:
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.boolean) {
|
2017-02-09 23:50:47 +00:00
|
|
|
return LoadCString(vm, "true", 4);
|
2017-02-09 20:02:59 +00:00
|
|
|
} else {
|
2017-02-09 23:50:47 +00:00
|
|
|
return LoadCString(vm, "false", 5);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
case TYPE_NUMBER:
|
2017-02-10 04:28:11 +00:00
|
|
|
return NumberToString(vm, x.data.number);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_ARRAY:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "array", 5, x.data.array);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_FORM:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "form", 4, x.data.array);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_STRING:
|
|
|
|
case TYPE_SYMBOL:
|
2017-02-10 04:28:11 +00:00
|
|
|
return x.data.string;
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_BYTEBUFFER:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "buffer", 6, x.data.buffer);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_CFUNCTION:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "cfunction", 9, x.data.cfunction);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_FUNCTION:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "function", 8, x.data.func);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_DICTIONARY:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "dictionary", 10, x.data.dict);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_FUNCDEF:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "funcdef", 7, x.data.funcdef);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_FUNCENV:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "funcenv", 7, x.data.funcenv);
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_THREAD:
|
2017-02-10 04:28:11 +00:00
|
|
|
return StringDescription(vm, "thread", 6, x.data.array);
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Simple hash function */
|
|
|
|
uint32_t djb2(const uint8_t * str) {
|
|
|
|
const uint8_t * end = str + VStringSize(str);
|
|
|
|
uint32_t hash = 5381;
|
|
|
|
while (str < end)
|
|
|
|
hash = (hash << 5) + hash + *str++;
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if two values are equal. This is strict equality with no conversion. */
|
2017-02-10 04:28:11 +00:00
|
|
|
int ValueEqual(Value x, Value y) {
|
2017-02-09 23:50:47 +00:00
|
|
|
int result = 0;
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.type != y.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
result = 0;
|
|
|
|
} else {
|
2017-02-10 04:28:11 +00:00
|
|
|
switch (x.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_NIL:
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
case TYPE_BOOLEAN:
|
2017-02-12 15:27:18 +00:00
|
|
|
result = (x.data.boolean == y.data.boolean);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_NUMBER:
|
2017-02-12 15:27:18 +00:00
|
|
|
result = (x.data.number == y.data.number);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
2017-02-12 20:16:55 +00:00
|
|
|
/* Assume that when strings are created, equal strings
|
|
|
|
* are set to the same string */
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_STRING:
|
2017-02-12 20:16:55 +00:00
|
|
|
/* TODO: keep cache to for symbols to get quick equality. */
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_SYMBOL:
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.string == y.data.string) {
|
2017-02-09 20:02:59 +00:00
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ValueHash(x) != ValueHash(y) ||
|
2017-02-10 04:28:11 +00:00
|
|
|
VStringSize(x.data.string) != VStringSize(y.data.string)) {
|
2017-02-09 20:02:59 +00:00
|
|
|
result = 0;
|
|
|
|
break;
|
|
|
|
}
|
2017-02-10 04:28:11 +00:00
|
|
|
if (!strncmp((char *) x.data.string, (char *) y.data.string, VStringSize(x.data.string))) {
|
2017-02-09 20:02:59 +00:00
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
result = 0;
|
|
|
|
break;
|
|
|
|
case TYPE_ARRAY:
|
|
|
|
case TYPE_FORM:
|
|
|
|
case TYPE_BYTEBUFFER:
|
|
|
|
case TYPE_CFUNCTION:
|
|
|
|
case TYPE_DICTIONARY:
|
|
|
|
case TYPE_FUNCTION:
|
|
|
|
case TYPE_FUNCDEF:
|
|
|
|
case TYPE_FUNCENV:
|
|
|
|
case TYPE_THREAD:
|
|
|
|
/* compare pointers */
|
2017-02-12 15:27:18 +00:00
|
|
|
result = (x.data.array == y.data.array);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Computes a hash value for a function */
|
2017-02-10 04:28:11 +00:00
|
|
|
uint32_t ValueHash(Value x) {
|
2017-02-09 23:50:47 +00:00
|
|
|
uint32_t hash = 0;
|
2017-02-10 04:28:11 +00:00
|
|
|
switch (x.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_NIL:
|
|
|
|
hash = 0;
|
|
|
|
break;
|
|
|
|
case TYPE_BOOLEAN:
|
2017-02-10 04:28:11 +00:00
|
|
|
hash = x.data.boolean;
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_NUMBER:
|
|
|
|
{
|
|
|
|
union {
|
|
|
|
uint32_t hash;
|
|
|
|
Number number;
|
|
|
|
} u;
|
2017-02-10 04:28:11 +00:00
|
|
|
u.number = x.data.number;
|
2017-02-09 20:02:59 +00:00
|
|
|
hash = u.hash;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/* String hashes */
|
|
|
|
case TYPE_SYMBOL:
|
|
|
|
case TYPE_STRING:
|
|
|
|
/* Assume 0 is not hashed. */
|
2017-02-10 04:28:11 +00:00
|
|
|
if (VStringHash(x.data.string))
|
|
|
|
hash = VStringHash(x.data.string);
|
2017-02-09 20:02:59 +00:00
|
|
|
else
|
2017-02-10 04:28:11 +00:00
|
|
|
hash = VStringHash(x.data.string) = djb2(x.data.string);
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
case TYPE_ARRAY:
|
|
|
|
case TYPE_FORM:
|
|
|
|
case TYPE_BYTEBUFFER:
|
|
|
|
case TYPE_CFUNCTION:
|
|
|
|
case TYPE_DICTIONARY:
|
|
|
|
case TYPE_FUNCTION:
|
|
|
|
case TYPE_FUNCDEF:
|
|
|
|
case TYPE_FUNCENV:
|
|
|
|
case TYPE_THREAD:
|
|
|
|
/* Cast the pointer */
|
2017-02-09 20:56:45 +00:00
|
|
|
{
|
2017-02-12 20:16:55 +00:00
|
|
|
union {
|
|
|
|
void * pointer;
|
|
|
|
uint32_t hash;
|
|
|
|
} u;
|
|
|
|
u.pointer = x.data.pointer;
|
|
|
|
hash = u.hash;
|
2017-02-09 20:56:45 +00:00
|
|
|
}
|
2017-02-09 20:02:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compares x to y. If they are equal retuns 0. If x is less, returns -1.
|
|
|
|
* If y is less, returns 1. All types are comparable
|
|
|
|
* and should have strict ordering. */
|
2017-02-10 04:28:11 +00:00
|
|
|
int ValueCompare(Value x, Value y) {
|
|
|
|
if (x.type == y.type) {
|
|
|
|
switch (x.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
case TYPE_NIL:
|
|
|
|
return 0;
|
|
|
|
case TYPE_BOOLEAN:
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.boolean == y.data.boolean) {
|
2017-02-09 20:02:59 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
2017-02-10 04:28:11 +00:00
|
|
|
return x.data.boolean ? 1 : -1;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
case TYPE_NUMBER:
|
|
|
|
/* TODO: define behavior for NaN and infinties. */
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.number == y.data.number) {
|
2017-02-09 20:02:59 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
2017-02-10 04:28:11 +00:00
|
|
|
return x.data.number > y.data.number ? 1 : -1;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
case TYPE_STRING:
|
|
|
|
case TYPE_SYMBOL:
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.string == y.data.string) {
|
2017-02-09 20:02:59 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
2017-02-10 04:28:11 +00:00
|
|
|
uint32_t xlen = VStringSize(x.data.string);
|
|
|
|
uint32_t ylen = VStringSize(y.data.string);
|
2017-02-09 20:02:59 +00:00
|
|
|
uint32_t len = xlen > ylen ? ylen : xlen;
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < len; ++i) {
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.string[i] == y.data.string[i]) {
|
2017-02-09 20:02:59 +00:00
|
|
|
continue;
|
2017-02-10 04:28:11 +00:00
|
|
|
} else if (x.data.string[i] < y.data.string[i]) {
|
2017-02-09 20:02:59 +00:00
|
|
|
return 1; /* x is less then y */
|
|
|
|
} else {
|
|
|
|
return -1; /* y is less than x */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (xlen == ylen) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return xlen < ylen ? -1 : 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case TYPE_ARRAY:
|
|
|
|
case TYPE_FORM:
|
|
|
|
case TYPE_BYTEBUFFER:
|
|
|
|
case TYPE_CFUNCTION:
|
|
|
|
case TYPE_FUNCTION:
|
|
|
|
case TYPE_DICTIONARY:
|
|
|
|
case TYPE_FUNCDEF:
|
|
|
|
case TYPE_FUNCENV:
|
|
|
|
case TYPE_THREAD:
|
2017-02-10 04:28:11 +00:00
|
|
|
if (x.data.string == y.data.string) {
|
2017-02-09 20:02:59 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
2017-02-10 04:28:11 +00:00
|
|
|
return x.data.string > y.data.string ? 1 : -1;
|
2017-02-09 20:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-10 04:28:11 +00:00
|
|
|
} else if (x.type < y.type) {
|
2017-02-09 20:02:59 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2017-02-12 20:53:52 +00:00
|
|
|
|
|
|
|
/* Get a value out af an associated data structure. Can throw VM error. */
|
|
|
|
Value ValueGet(VM * vm, Value ds, Value key) {
|
|
|
|
switch (ds.type) {
|
|
|
|
case TYPE_ARRAY:
|
|
|
|
case TYPE_FORM:
|
|
|
|
case TYPE_BYTEBUFFER:
|
|
|
|
case TYPE_SYMBOL:
|
|
|
|
case TYPE_STRING:
|
|
|
|
case TYPE_DICTIONARY:
|
|
|
|
case TYPE_FUNCENV:
|
|
|
|
default:
|
|
|
|
VMError(vm, "Cannot get.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set a value in an associative data structure. Can throw VM error. */
|
|
|
|
int ValueSet(VM * vm, Value ds, Value key, Value value) {
|
|
|
|
|
|
|
|
}
|