Flesh out support for userdata

Add file reading via file objects.
Finalizer option for userdata.
This commit is contained in:
Calvin Rose 2017-04-26 10:21:03 -04:00
parent d295e281e1
commit bf2c16ccb0
12 changed files with 257 additions and 170 deletions

View File

@ -16,7 +16,7 @@ all: $(GST_TARGET)
##### The core vm and runtime #####
###################################
GST_CORE_SOURCES=$(addprefix core/,\
compile.c disasm.c parse.c stl.c strings.c ids.c util.c\
compile.c disasm.c parse.c stl.c ids.c util.c\
value.c vm.c ds.c gc.c thread.c serialize.c)
GST_CORE_OBJECTS=$(patsubst %.c,%.o,$(GST_CORE_SOURCES))
$(GST_CORELIB): $(GST_CORE_OBJECTS) $(GST_HEADERS)

View File

@ -28,5 +28,6 @@
void gst_cache_remove_string(Gst *vm, char *strmem);
void gst_cache_remove_tuple(Gst *vm, char *tuplemem);
void gst_cache_remove_struct(Gst *vm, char *structmem);
void gst_cache_remove_userdata(Gst *vm, char *usermem);
#endif /* end of include guard: CACHE_H_LVYZMBLR */

View File

@ -159,12 +159,13 @@ GstValue gst_array_peek(GstArray *array) {
/****/
/* Create new userdata */
void *gst_userdata(Gst *vm, uint32_t size, const GstValue *meta) {
void *gst_userdata(Gst *vm, uint32_t size, const GstUserType *utype) {
char *data = gst_alloc(vm, sizeof(GstUserdataHeader) + size);
GstUserdataHeader *header = (GstUserdataHeader *)data;
void *user = data + sizeof(GstUserdataHeader);
header->size = size;
header->meta = meta;
header->type = utype;
gst_mem_tag(header, GST_MEMTAG_USER);
return user;
}

View File

@ -174,10 +174,8 @@ void gst_mark(Gst *vm, GstValueUnion x, GstType type) {
if (gc_header(x.string - sizeof(GstUserdataHeader))->color != vm->black) {
GstUserdataHeader *userHeader = (GstUserdataHeader *)x.string - 1;
gc_header(userHeader)->color = vm->black;
GstValueUnion temp;
temp.st = userHeader->meta;
gst_mark(vm, temp, GST_STRUCT);
}
break;
case GST_FUNCENV:
gst_mark_funcenv(vm, x.env);
@ -210,6 +208,12 @@ void gst_sweep(Gst *vm) {
gst_cache_remove_struct(vm, (char *)(current + 1));
if (current->tags & GST_MEMTAG_TUPLE)
gst_cache_remove_tuple(vm, (char *)(current + 1));
if (current->tags & GST_MEMTAG_USER) {
GstUserdataHeader *h = (GstUserdataHeader *)(current + 1);
if (h->type->finalize) {
h->type->finalize(vm, h + 1, h->size);
}
}
}
gst_raw_free(current);
} else {

View File

@ -395,3 +395,25 @@ GstValue gst_string_cv(Gst *vm, const char *str) {
ret.data.string = data;
return ret;
}
/* Compares two strings */
int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
uint32_t xlen = gst_string_length(lhs);
uint32_t ylen = gst_string_length(rhs);
uint32_t len = xlen > ylen ? ylen : xlen;
uint32_t i;
for (i = 0; i < len; ++i) {
if (lhs[i] == rhs[i]) {
continue;
} else if (lhs[i] < rhs[i]) {
return -1; /* x is less than y */
} else {
return 1; /* y is less than x */
}
}
if (xlen == ylen) {
return 0;
} else {
return xlen < ylen ? -1 : 1;
}
}

View File

@ -38,12 +38,12 @@
* Byte 207: Buffer - [u32 length]*[u8... characters]
* Byte 208: Array - [u32 length]*[value... elements]
* Byte 209: Tuple - [u32 length]*[value... elements]
* Byte 210: Thread - [u8 state][u32 frames]*[[value callee][value env]
* [u32 pcoffset][u16 ret][u16 args][u16 size]*[value ...stack]
* Byte 210: Thread - [value parent][u8 state][u32 frames]*[[value callee][value env]
* [u32 pcoffset][u32 ret][u32 args][u32 size]*[value ...stack]]
* Byte 211: Table - [u32 length]*2*[value... kvs]
* Byte 212: FuncDef - [u32 locals][u32 arity][u32 flags][u32 literallen]*[value...
* literals][u32 bytecodelen]*[u16... bytecode]
* Byte 213: FunEnv - [value thread][u32 length]*[value ...upvalues]
* Byte 213: FuncEnv - [value thread][u32 length]*[value ...upvalues]
* (upvalues is not read if thread is a thread object)
* Byte 214: Func - [value parent][value def][value env]
* (nil values indicate empty)
@ -232,14 +232,15 @@ static const char *gst_deserialize_impl(
case 210: /* Thread */
{
GstValue nil;
GstThread *t;
GstValue *stack;
uint16_t prevsize = 0;
uint8_t statusbyte;
nil.type = GST_NIL;
t = gst_thread(vm, nil, 64);
ret.type = GST_THREAD;
t = gst_thread(vm, gst_wrap_nil(), 64);
err = gst_deserialize_impl(vm, data, end, &data, visited, &ret);
if (err != NULL) return err;
if (ret.type != GST_THREAD) return "expected thread parent to thread";
t->parent = ret.data.thread;
ret.data.thread = t;
deser_assert(data < end, UEB);
statusbyte = *data++;
@ -258,16 +259,16 @@ static const char *gst_deserialize_impl(
uint16_t ret, args, size, j;
/* Create a new frame */
if (i > 0)
gst_thread_beginframe(vm, t, nil, 0);
gst_thread_beginframe(vm, t, gst_wrap_nil(), 0);
/* Read the stack */
err = gst_deserialize_impl(vm, data, end, &data, visited, &callee);
if (err != NULL) return err;
err = gst_deserialize_impl(vm, data, end, &data, visited, &env);
if (err != NULL) return err;
read_u32(pcoffset);
read_u16(ret);
read_u16(args);
read_u16(size);
read_u32(ret);
read_u32(args);
read_u32(size);
/* Set up the stack */
stack = gst_thread_stack(t);
if (callee.type == GST_FUNCTION) {
@ -295,7 +296,7 @@ static const char *gst_deserialize_impl(
ret.type = GST_TABLE;
read_u32(length);
ret.data.table = gst_table(vm, 2 * length);
for (i = 0; i < length; i += 2) {
for (i = 0; i < length; ++i) {
GstValue key, value;
err = gst_deserialize_impl(vm, data, end, &data, visited, &key);
if (err != NULL) return err;
@ -393,16 +394,8 @@ static const char *gst_deserialize_impl(
case 215: /* LUdata */
{
GstValue meta;
ret.type = GST_USERDATA;
err = gst_deserialize_impl(vm, data, end, &data, visited, &meta);
if (err != NULL) return err;
deser_assert(meta.type == GST_STRUCT, "userdata requires valid meta struct");
read_u32(length);
deser_datacheck(length);
ret.data.pointer = gst_userdata(vm, length, meta.data.st);
gst_memcpy(ret.data.pointer, data, length);
gst_array_push(vm, visited, ret);
/* TODO enable deserialization of userdata through registration
* to names in vm. */
}
break;
@ -445,7 +438,7 @@ const char *gst_deserialize(
*out = ret;
return NULL;
}
/* Allow appending other types to buffers */
BUFFER_DEFINE(real, GstReal)
BUFFER_DEFINE(integer, GstInteger)
@ -508,56 +501,124 @@ const char *gst_serialize_impl(
/* Check reference types */
switch (x.type) {
default:
return "unable to serialize type";
return "unable to serialize type";
case GST_STRING:
write_byte(205);
count = gst_string_length(x.data.string);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(x.data.string[i]);
}
break;
write_byte(205);
count = gst_string_length(x.data.string);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(x.data.string[i]);
}
break;
case GST_STRUCT:
write_byte(206);
count = gst_struct_length(x.data.st);
write_u32(count);
for (i = 0; i < gst_struct_capacity(x.data.st); i += 2) {
if (x.data.st[i].type != GST_NIL) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.st[i]);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.st[i + 1]);
if (err != NULL) return err;
}
}
break;
write_byte(206);
count = gst_struct_length(x.data.st);
write_u32(count);
for (i = 0; i < gst_struct_capacity(x.data.st); i += 2) {
if (x.data.st[i].type != GST_NIL) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.st[i]);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.st[i + 1]);
if (err != NULL) return err;
}
}
break;
case GST_BYTEBUFFER:
write_byte(207);
count = x.data.buffer->count;
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(207);
count = x.data.buffer->count;
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(x.data.buffer->data[i]);
}
break;
}
break;
case GST_ARRAY:
write_byte(208);
count = x.data.array->count;
write_u32(count);
for (i = 0; i < count; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.array->data[i]);
if (err != NULL) return err;
}
break;
write_byte(208);
count = x.data.array->count;
write_u32(count);
for (i = 0; i < count; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.array->data[i]);
if (err != NULL) return err;
}
break;
case GST_TUPLE:
write_byte(209);
count = gst_tuple_length(x.data.tuple);
write_u32(count);
for (i = 0; i < count; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i]);
if (err != NULL) return err;
}
break;
/*case GST_THREAD:*/
/*break;*/
write_byte(209);
count = gst_tuple_length(x.data.tuple);
write_u32(count);
for (i = 0; i < count; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i]);
if (err != NULL) return err;
}
break;
case GST_THREAD:
{
GstThread *t = x.data.thread;
const GstValue *stack;
uint32_t framecount = gst_thread_countframes(t);
uint32_t i;
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_thread(t->parent));
if (err != NULL) return err;
/* Write the status byte */
if (t->status == GST_THREAD_PENDING) write_byte(0);
else if (t->status == GST_THREAD_ALIVE) write_byte(1);
else write_byte(2);
/* Write number of stack frames */
write_u32(framecount);
/* Write stack frames */
for (i = 0; i < framecount; ++i) {
uint32_t j, size;
GstValue callee = gst_frame_callee(stack);
GstFuncEnv *env = gst_frame_env(stack);
err = gst_serialize_impl(vm, buffer, visited, nextId, callee);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_funcenv(env));
if (err != NULL) return err;
if (callee.type == GST_FUNCTION) {
write_u32(gst_frame_pc(stack) - callee.data.function->def->byteCode);
} else {
write_u32(0);
}
write_u32(gst_frame_ret(stack));
write_u32(gst_frame_args(stack));
size = gst_frame_size(stack);
write_u32(size);
for (j = 0; j < size; ++j) {
err = gst_serialize_impl(vm, buffer, visited, nextId, stack[j]);
if (err != NULL) return err;
}
/* Next stack frame */
stack = t->data + GST_FRAME_SIZE;
}
}
break;
case GST_TABLE:
write_byte(211);
count = x.data.table->count;
write_u32(count);
for (i = 0; i < x.data.table->capacity; i += 2) {
if (x.data.table->data[i].type == GST_NIL) continue;
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i]);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i+1]);
if (err != NULL) return err;
}
break;
case GST_FUNCDEF: /* Funcdef */
{
/* TODO */
}
break;
case GST_FUNCENV: /* Funcenv */
{
/* TODO */
}
break;
case GST_FUNCTION: /* Function */
{
/* TODO */
}
break;
}
/* Record reference */

View File

@ -328,17 +328,21 @@ int gst_stl_struct(Gst *vm) {
/* Create a buffer */
int gst_stl_buffer(Gst *vm) {
uint32_t i, count;
const uint8_t *dat;
uint32_t slen;
GstBuffer *buf = gst_buffer(vm, 10);
count = gst_count_args(vm);
for (i = 0; i < count; ++i) {
const uint8_t *string = gst_to_string(vm, gst_arg(vm, i));
gst_buffer_append(vm, buf, string, gst_string_length(string));
if (gst_chararray_view(gst_arg(vm, i), &dat, &slen))
gst_buffer_append(vm, buf, dat, slen);
else
gst_c_throwc(vm, GST_EXPECTED_STRING);
}
gst_c_return(vm, gst_wrap_buffer(buf));
}
/* Concatenate strings */
int gst_stl_strcat(Gst *vm) {
/* Create a string */
int gst_stl_string(Gst *vm) {
uint32_t j;
uint32_t count = gst_count_args(vm);
uint32_t length = 0;
@ -515,7 +519,13 @@ int gst_stl_setglobal(Gst *vm) {
/* IO */
/****/
/* TODO - add userdata to allow for manipulation of FILE pointers. */
/* File type definition */
static GstUserType gst_stl_filetype = {
"io.file",
NULL,
NULL,
NULL
};
/* Open a a file and return a userdata wrapper arounf the C file API. */
int gst_stl_open(Gst *vm) {
@ -523,20 +533,44 @@ int gst_stl_open(Gst *vm) {
const uint8_t *fmode = gst_to_string(vm, gst_arg(vm, 1));
FILE *f;
FILE **fp;
GstValue *st;
if (gst_count_args(vm) < 2 || gst_arg(vm, 0).type != GST_STRING
|| gst_arg(vm, 1).type != GST_STRING)
gst_c_throwc(vm, "expected filename and filemode");
f = fopen((const char *)fname, (const char *)fmode);
if (!f)
gst_c_throwc(vm, "could not open file");
st = gst_struct_begin(vm, 0);
fp = gst_userdata(vm, sizeof(FILE *), gst_struct_end(vm, st));
fp = gst_userdata(vm, sizeof(FILE *), &gst_stl_filetype);
*fp = f;
gst_c_return(vm, gst_wrap_userdata(fp));
}
/* Write a string to a file in one go. Overwrites an existing file. */
/* Read an entire file into memory */
int gst_stl_slurp(Gst *vm) {
GstBuffer *b;
long fsize;
FILE *f;
FILE **fp = gst_check_userdata(vm, 0, &gst_stl_filetype);
if (fp == NULL) gst_c_throwc(vm, "expected file");
if (!gst_check_buffer(vm, 1, &b)) b = gst_buffer(vm, 10);
f = *fp;
/* Read whole file */
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fseek(f, 0, SEEK_SET);
/* Ensure buffer size */
gst_buffer_ensure(vm, b, b->count + fsize);
fread((char *)(b->data + b->count), fsize, 1, f);
b->count += fsize;
gst_c_return(vm, gst_wrap_buffer(b));
}
/* Close a file */
int gst_stl_close(Gst *vm) {
FILE **fp = gst_check_userdata(vm, 0, &gst_stl_filetype);
if (fp == NULL) gst_c_throwc(vm, "expected file");
fclose(*fp);
gst_c_return(vm, gst_wrap_nil());
}
/****/
/* Temporary */
@ -576,8 +610,8 @@ static const GstModuleItem const std_module[] = {
{"not", gst_stl_not},
{"length", gst_stl_length},
{"hash", gst_stl_hash},
{"to-integer", gst_stl_to_int},
{"to-real", gst_stl_to_real},
{"integer", gst_stl_to_int},
{"real", gst_stl_to_real},
{"type", gst_stl_type},
{"slice", gst_stl_slice},
{"array", gst_stl_array},
@ -585,7 +619,7 @@ static const GstModuleItem const std_module[] = {
{"table", gst_stl_table},
{"struct", gst_stl_struct},
{"buffer", gst_stl_buffer},
{"strcat", gst_stl_strcat},
{"string", gst_stl_string},
{"print", gst_stl_print},
{"tostring", gst_stl_tostring},
{"exit", gst_stl_exit},
@ -601,6 +635,8 @@ static const GstModuleItem const std_module[] = {
{"peek", gst_stl_peek},
{"ensure", gst_stl_ensure},
{"open", gst_stl_open},
{"slurp", gst_stl_slurp},
{"close", gst_stl_close},
{"dasm", gst_stl_dasm},
{NULL, NULL}
};

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
/* Compares two strings */
int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
uint32_t xlen = gst_string_length(lhs);
uint32_t ylen = gst_string_length(rhs);
uint32_t len = xlen > ylen ? ylen : xlen;
uint32_t i;
for (i = 0; i < len; ++i) {
if (lhs[i] == rhs[i]) {
continue;
} else if (lhs[i] < rhs[i]) {
return -1; /* x is less than y */
} else {
return 1; /* y is less than x */
}
}
if (xlen == ylen) {
return 0;
} else {
return xlen < ylen ? -1 : 1;
}
}

View File

@ -173,3 +173,15 @@ GstValue *gst_thread_popframe(Gst *vm, GstThread *thread) {
else
return NULL;
}
/* Count the number of stack frames in a thread */
uint32_t gst_thread_countframes(GstThread *thread) {
uint32_t count = 0;
const GstValue *stack = thread->data + GST_FRAME_SIZE;
const GstValue *laststack = thread->data + thread->count;
while (stack <= laststack) {
++count;
stack += gst_frame_size(stack) + GST_FRAME_SIZE;
}
return count;
}

View File

@ -63,12 +63,27 @@ GST_WRAP_DEFINE(buffer, GstBuffer *, GST_BYTEBUFFER, buffer)
GST_WRAP_DEFINE(function, GstFunction *, GST_FUNCTION, function)
GST_WRAP_DEFINE(cfunction, GstCFunction, GST_CFUNCTION, cfunction)
GST_WRAP_DEFINE(table, GstTable *, GST_TABLE, table)
GST_WRAP_DEFINE(userdata, void *, GST_USERDATA, pointer)
GST_WRAP_DEFINE(funcenv, GstFuncEnv *, GST_FUNCENV, env)
GST_WRAP_DEFINE(funcdef, GstFuncDef *, GST_FUNCDEF, def)
#undef GST_WRAP_DEFINE
GstValue gst_wrap_userdata(void *x) {
GstValue ret;
ret.type = GST_USERDATA;
ret.data.pointer = x;
return ret;
}
void *gst_check_userdata(Gst *vm, uint32_t i, const GstUserType *type) {
GstValue x = gst_arg(vm, i);
GstUserdataHeader *h;
if (x.type != GST_USERDATA) return NULL;
h = ((GstUserdataHeader *)x.data.pointer) - 1;
if (h->type != type) return NULL;
return x.data.pointer;
}
GstValue gst_cmodule_table(Gst *vm, const GstModuleItem *mod) {
GstTable *module = gst_table(vm, 10);
while (mod->name != NULL) {

View File

@ -148,14 +148,13 @@ typedef struct GstTable GstTable;
typedef struct GstThread GstThread;
typedef int (*GstCFunction)(Gst * vm);
/* Implementation details */
/* Other structs */
typedef struct GstUserdataHeader GstUserdataHeader;
typedef struct GstFuncDef GstFuncDef;
typedef struct GstFuncEnv GstFuncEnv;
typedef union GstValueUnion GstValueUnion;
/* API Types */
typedef struct GstModuleItem GstModuleItem;
typedef struct GstUserType GstUserType;
/* C Api data types */
struct GstModuleItem {
@ -259,10 +258,18 @@ struct GstFunction {
GstFunction *parent;
};
/* Defines a type for userdata */
struct GstUserType {
const char *id;
GstValue (*serialize)(Gst *vm, void *data, uint32_t len);
GstValue (*deserialize)(Gst *vm, GstValue in);
void (*finalize)(Gst *vm, void *data, uint32_t len);
};
/* Contains information about userdata */
struct GstUserdataHeader {
uint32_t size;
const GstValue *meta;
const GstUserType *type;
};
/* VM return status from c function */
@ -356,7 +363,7 @@ GstValue gst_array_peek(GstArray *array);
/* Userdata functions */
/****/
void *gst_userdata(Gst *vm, uint32_t size, const GstValue *meta);
void *gst_userdata(Gst *vm, uint32_t size, const GstUserType *utype);
/****/
/* Tuple functions */
@ -409,6 +416,7 @@ void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n);
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity);
void gst_thread_endframe(Gst *vm, GstThread *thread);
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread);
uint32_t gst_thread_countframes(GstThread *thread);
/****/
/* Value manipulation */
@ -427,43 +435,13 @@ GstInteger gst_length(Gst *vm, GstValue x);
/* Serialization */
/****/
/**
* Data format
* State is encoded as a string of unsigned bytes.
*
* Types:
*
* Byte 0 to 200: small integer byte - 100
* Byte 201: Nil
* Byte 202: True
* Byte 203: False
* Byte 204: Number - double format
* Byte 205: String - [u32 length]*[u8... characters]
* Byte 206: Symbol - [u32 length]*[u8... characters]
* Byte 207: Buffer - [u32 length]*[u8... characters]
* Byte 208: Array - [u32 length]*[value... elements]
* Byte 209: Tuple - [u32 length]*[value... elements]
* Byte 210: Thread - [u8 state][u32 frames]*[[value callee][value env]
* [u32 pcoffset][u32 erroffset][u16 ret][u16 errloc][u16 size]*[value ...stack]
* Byte 211: Table - [u32 length]*2*[value... kvs]
* Byte 212: FuncDef - [u32 locals][u32 arity][u32 flags][u32 literallen]*[value...
* literals][u32 bytecodelen]*[u16... bytecode]
* Byte 213: FunEnv - [value thread][u32 length]*[value ...upvalues]
* (upvalues is not read if thread is a thread object)
* Byte 214: Func - [value parent][value def][value env]
* (nil values indicate empty)
* Byte 215: LUdata - [value meta][u32 length]*[u8... bytes]
* Byte 216: CFunc - [u32 length]*[u8... idstring]
* Byte 217: Ref - [u32 id]
* Byte 218: Integer - [i64 value]
*/
const char *gst_deserialize(
Gst *vm,
const uint8_t *data,
uint32_t len,
GstValue *out,
const uint8_t *nextData);
const char *gst_serialize(Gst *vm, GstBuffer *buffer, GstValue x);
/****/
@ -473,6 +451,7 @@ const char *gst_serialize(Gst *vm, GstBuffer *buffer, GstValue x);
#define GST_MEMTAG_STRING 4
#define GST_MEMTAG_TUPLE 8
#define GST_MEMTAG_STRUCT 16
#define GST_MEMTAG_USER 32
void gst_mark_value(Gst *vm, GstValue x);
void gst_mark(Gst *vm, GstValueUnion x, GstType type);
@ -539,10 +518,11 @@ int gst_check_buffer(Gst *vm, uint32_t i, GstBuffer *(*x));
int gst_check_function(Gst *vm, uint32_t i, GstFunction *(*x));
int gst_check_cfunction(Gst *vm, uint32_t i, GstCFunction (*x));
int gst_check_table(Gst *vm, uint32_t i, GstTable *(*x));
int gst_check_userdata(Gst *vm, uint32_t i, void *(*x));
int gst_check_funcenv(Gst *vm, uint32_t i, GstFuncEnv *(*x));
int gst_check_funcdef(Gst *vm, uint32_t i, GstFuncDef *(*x));
void *gst_check_userdata(Gst *vm, uint32_t i, const GstUserType *type);
/* Treat similar types through uniform interfaces */
int gst_seq_view(GstValue seq, const GstValue **data, uint32_t *len);
int gst_chararray_view(GstValue str, const uint8_t **data, uint32_t *len);
int gst_hashtable_view(GstValue tab, const GstValue **data, uint32_t *cap);

View File

@ -14,7 +14,7 @@
(: i (+ 1 i)))
(if (> len 0) (pop parts))
(push parts end)
(apply strcat start parts)))
(apply string start parts)))
# Pretty print an object or struct
(: print-struct (fn [start end s]
@ -28,7 +28,7 @@
(: key (next s key)))
(if (> (length parts) 0) (pop parts))
(push parts end)
(apply strcat start parts)))
(apply string start parts)))
# Pretty