Add serialization and deserialization

This commit is contained in:
Bruce Hill 2024-11-29 18:09:12 -05:00
parent 4b5e4cd1f2
commit f66f8ad711
35 changed files with 678 additions and 31 deletions

2
ast.c
View File

@ -161,6 +161,8 @@ CORD ast_to_xml(ast_t *ast)
T(DocTest, "<DocTest>%r<output>%r</output></DocTest>", optional_tagged("expression", data.expr), xml_escape(data.output))
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
T(Serialize, "<Serialize>%r</Serialize>", ast_to_xml(data.value))
T(Deserialize, "<Deserialize><type>%r</type>%r</Deserialize>", type_ast_to_xml(data.type), ast_to_xml(data.value))
default: return "???";
#undef T
}

8
ast.h
View File

@ -145,6 +145,7 @@ typedef enum {
DocTest,
Use,
InlineCCode,
Serialize, Deserialize,
} ast_e;
struct ast_s {
@ -326,6 +327,13 @@ struct ast_s {
struct type_s *type;
type_ast_t *type_ast;
} InlineCCode;
struct {
ast_t *value;
} Serialize;
struct {
ast_t *value;
type_ast_t *type;
} Deserialize;
} __data;
};

View File

@ -50,7 +50,7 @@ CORD promote_to_optional(type_t *t, CORD code)
} else if (t->tag == ByteType) {
return CORD_all("((OptionalByte_t){", code, "})");
} else if (t->tag == StructType) {
return CORD_all("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_null = false; nonnull; })");
return CORD_all("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_none = false; nonnull; })");
} else {
return code;
}
@ -205,6 +205,7 @@ CORD compile_declaration(type_t *t, CORD name)
code = CORD_all(code, compile_type(arg->type));
if (arg->next) code = CORD_cat(code, ", ");
}
if (!fn->args) code = CORD_all(code, "void");
return CORD_all(code, ")");
} else if (t->tag != ModuleType) {
return CORD_all(compile_type(t), " ", name);
@ -269,6 +270,8 @@ CORD compile_type(type_t *t)
code = CORD_all(code, compile_type(arg->type));
if (arg->next) code = CORD_cat(code, ", ");
}
if (!fn->args)
code = CORD_all(code, "void");
return CORD_all(code, ")");
}
case ClosureType: return "Closure_t";
@ -425,7 +428,7 @@ CORD check_null(type_t *t, CORD value)
else if (t->tag == TextType)
return CORD_all("((", value, ").length < 0)");
else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType)
return CORD_all("(", value, ").is_null");
return CORD_all("(", value, ").is_none");
else if (t->tag == EnumType)
return CORD_all("((", value, ").tag == 0)");
else if (t->tag == MomentType)
@ -911,7 +914,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
all_args = CORD_all(all_args, "$", arg->name, arg->next ? ", " : CORD_EMPTY);
CORD pop_code = CORD_EMPTY;
if (fndef->cache->tag == Int && !cache_size.is_null && cache_size.i > 0) {
if (fndef->cache->tag == Int && !cache_size.is_none && cache_size.i > 0) {
pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.i),
") Table$remove(&cache, cache.entries.data + cache.entries.stride*RNG$int64(default_rng, I(0), I(cache.entries.length-1)), table_type);\n");
}
@ -1904,7 +1907,7 @@ CORD compile_null(type_t *t)
case PointerType: return CORD_all("((", compile_type(t), ")NULL)");
case ClosureType: return "NONE_CLOSURE";
case NumType: return "nan(\"null\")";
case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_null=yes})");
case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_none=true})");
case EnumType: {
env_t *enum_env = Match(t, EnumType)->env;
return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})");
@ -2723,6 +2726,14 @@ CORD compile(env_t *env, ast_t *ast)
case MethodCall: {
auto call = Match(ast, MethodCall);
type_t *self_t = get_type(env, call->self);
if (streq(call->name, "serialize")) {
if (call->args)
code_err(ast, ":serialize() doesn't take any arguments");
return CORD_all("generic_serialize((", compile_declaration(self_t, "[1]"), "){",
compile(env, call->self), "}, ", compile_type_info(env, self_t), ")");
}
int64_t pointer_depth = 0;
type_t *self_value_t = self_t;
for (; self_value_t->tag == PointerType; self_value_t = Match(self_value_t, PointerType)->pointed)
@ -3191,6 +3202,21 @@ CORD compile(env_t *env, ast_t *ast)
code_err(call->fn, "This is not a function, it's a %T", fn_t);
}
}
case Serialize: {
ast_t *value = Match(ast, Serialize)->value;
type_t *t = get_type(env, value);
return CORD_all("generic_serialize((", compile_declaration(t, "[1]"), "){", compile(env, value), "}, ", compile_type_info(env, t), ")");
}
case Deserialize: {
ast_t *value = Match(ast, Deserialize)->value;
type_t *value_type = get_type(env, value);
if (!type_eq(value_type, Type(ArrayType, Type(ByteType))))
code_err(value, "This value should be an array of bytes, not a %T", value_type);
type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type);
return CORD_all("({ ", compile_declaration(t, "deserialized"), ";\n"
"generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(env, t), ");\n"
"deserialized; })");
}
case When: {
auto original = Match(ast, When);
ast_t *when_var = WrapAST(ast, Var, .name="when");

19
parse.c
View File

@ -54,6 +54,7 @@ static const char *keywords[] = {
"yes", "xor", "while", "when", "use", "unless", "struct", "stop", "skip", "return",
"or", "not", "no", "mod1", "mod", "pass", "lang", "inline", "in", "if",
"func", "for", "extern", "enum", "else", "do", "defer", "and", "NONE", "_min_", "_max_",
"DESERIALIZE",
NULL,
};
@ -139,6 +140,7 @@ static PARSER(parse_use);
static PARSER(parse_var);
static PARSER(parse_when);
static PARSER(parse_while);
static PARSER(parse_deserialize);
//
// Print a parse error and exit (or use the on_err longjmp)
@ -1547,6 +1549,22 @@ PARSER(parse_none) {
return NewAST(ctx->file, start, type->end, None, .type=type);
}
PARSER(parse_deserialize) {
const char *start = pos;
if (!match_word(&pos, "DESERIALIZE"))
return NULL;
spaces(&pos);
ast_t *value = expect(ctx, start, &pos, parse_parens, "I expected an expression here");
spaces(&pos);
if (!match(&pos, ":"))
parser_err(ctx, pos, pos, "I expected a ':' and a type here");
type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this deserialization");
return NewAST(ctx->file, start, pos, Deserialize, .value=value, .type=type);
}
PARSER(parse_var) {
const char *start = pos;
const char* name = get_id(&pos);
@ -1572,6 +1590,7 @@ PARSER(parse_term_no_suffix) {
|| (term=parse_parens(ctx, pos))
|| (term=parse_table(ctx, pos))
|| (term=parse_set(ctx, pos))
|| (term=parse_deserialize(ctx, pos))
|| (term=parse_var(ctx, pos))
|| (term=parse_array(ctx, pos))
|| (term=parse_channel(ctx, pos))

View File

@ -6,6 +6,7 @@
#include <sys/param.h>
#include "arrays.h"
#include "integers.h"
#include "metamethods.h"
#include "optionals.h"
#include "rng.h"
@ -701,4 +702,46 @@ public PUREFUNC bool Array$is_none(const void *obj, const TypeInfo_t*)
return ((Array_t*)obj)->length < 0;
}
public void Array$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
Array_t arr = *(Array_t*)obj;
int64_t len = arr.length;
Int64$serialize(&len, out, pointers, &Int64$info);
auto item_serialize = type->ArrayInfo.item->metamethods.serialize;
if (item_serialize) {
for (int64_t i = 0; i < len; i++)
item_serialize(arr.data + i*arr.stride, out, pointers, type->ArrayInfo.item);
} else if (arr.stride == type->ArrayInfo.item->size) {
fwrite(arr.data, (size_t)type->ArrayInfo.item->size, (size_t)len, out);
} else {
for (int64_t i = 0; i < len; i++)
fwrite(arr.data + i*arr.stride, (size_t)type->ArrayInfo.item->size, 1, out);
}
}
public void Array$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type)
{
int64_t len = -1;
Int64$deserialize(in, &len, pointers, &Int64$info);
int64_t padded_size = type->ArrayInfo.item->size;
if (type->ArrayInfo.item->align > 0 && padded_size % type->ArrayInfo.item->align > 0)
padded_size += type->ArrayInfo.item->align - (padded_size % type->ArrayInfo.item->align);
Array_t arr = {
.length=len,
.data=GC_MALLOC((size_t)(len*padded_size)),
.stride=padded_size,
};
auto item_deserialize = type->ArrayInfo.item->metamethods.deserialize;
if (item_deserialize) {
for (int64_t i = 0; i < len; i++)
item_deserialize(in, arr.data + i*arr.stride, pointers, type->ArrayInfo.item);
} else if (arr.stride == type->ArrayInfo.item->size) {
fread(arr.data, (size_t)type->ArrayInfo.item->size, (size_t)len, in);
} else {
for (int64_t i = 0; i < len; i++)
fread(arr.data + i*arr.stride, (size_t)type->ArrayInfo.item->size, 1, in);
}
*(Array_t*)obj = arr;
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -100,6 +100,8 @@ void Array$heap_pop(Array_t *heap, Closure_t comparison, int64_t padded_item_siz
Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison);
#define Array$binary_search_value(array, target, comparison) \
({ __typeof(target) _target = target; Array$binary_search(array, &_target, comparison); })
void Array$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Array$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type);
#define Array$metamethods ((metamethods_t){ \
.as_text=Array$as_text, \
@ -107,6 +109,8 @@ Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison);
.equal=Array$equal, \
.hash=Array$hash, \
.is_none=Array$is_none, \
.serialize=Array$serialize, \
.deserialize=Array$deserialize, \
})
#define Array$info(item_info) &((TypeInfo_t){.size=sizeof(Array_t), .align=__alignof__(Array_t), \

View File

@ -32,9 +32,9 @@ Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix);
typedef struct {
Byte_t value;
bool is_null:1;
bool has_value:1;
} OptionalByte_t;
#define NONE_BYTE ((OptionalByte_t){.is_null=true})
#define NONE_BYTE ((OptionalByte_t){.has_value=false})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -4,6 +4,7 @@
#include <gc.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "text.h"
@ -50,6 +51,24 @@ PUREFUNC public bool CString$is_none(const void *c_str, const TypeInfo_t*)
return *(char**)c_str == NULL;
}
static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t*)
{
const char *str = *(const char **)obj;
int64_t len = (int64_t)strlen(str);
Int64$serialize(&len, out, pointers, &Int64$info);
fwrite(str, sizeof(char), (size_t)len, out);
}
static void CString$deserialize(FILE *in, void *out, Array_t *pointers, const TypeInfo_t *)
{
int64_t len = -1;
Int64$deserialize(in, &len, pointers, &Int64$info);
char *str = GC_MALLOC_ATOMIC((size_t)len+1);
fread(str, sizeof(char), (size_t)len, in);
str[len+1] = '\0';
*(char**)out = str;
}
public const TypeInfo_t CString$info = {
.size=sizeof(char*),
.align=__alignof__(char*),
@ -59,6 +78,8 @@ public const TypeInfo_t CString$info = {
.equal=CString$equal,
.as_text=CString$as_text,
.is_none=CString$is_none,
.serialize=CString$serialize,
.deserialize=CString$deserialize,
},
};

View File

@ -137,4 +137,21 @@ public PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*)
return *(void**)obj == NULL;
}
public void Channel$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
Channel_t *channel = (Channel_t*)obj;
Array$serialize(&channel->items, out, pointers, Array$info(type->ChannelInfo.item));
Int64$serialize(&channel->max_size, out, pointers, &Int64$info);
}
public void Channel$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
Channel_t *channel = new(Channel_t);
channel->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
channel->cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
Array$deserialize(in, &channel->items, pointers, Array$info(type->ChannelInfo.item));
Int64$deserialize(in, &channel->max_size, pointers, &Int64$info);
*(Channel_t**)outval = channel;
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -25,6 +25,8 @@ PUREFUNC int32_t Channel$compare(const void *x, const void *y, const TypeInfo_t
PUREFUNC bool Channel$equal(const void *x, const void *y, const TypeInfo_t *type);
Text_t Channel$as_text(const void *channel, bool colorize, const TypeInfo_t *type);
PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*);
void Channel$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t*);
void Channel$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t*);
#define Channel$metamethods ((metamethods_t){ \
.as_text=Channel$as_text, \
@ -32,6 +34,8 @@ PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*);
.equal=Channel$equal, \
.hash=Channel$hash, \
.is_none=Channel$is_none, \
.serialize=Channel$serialize, \
.deserialize=Channel$deserialize, \
})
#define Channel$info(item_info) &((TypeInfo_t){.size=sizeof(Channel_t), .align=__alignof__(Channel_t), \

View File

@ -7,6 +7,7 @@
#include "bools.h"
#include "channels.h"
#include "functiontype.h"
#include "integers.h"
#include "metamethods.h"
#include "optionals.h"
#include "pointers.h"
@ -89,4 +90,32 @@ PUREFUNC public bool Enum$is_none(const void *x, const TypeInfo_t*)
return *(int32_t*)x == 0;
}
public void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
int32_t tag = *(int32_t*)obj;
Int32$serialize(&tag, out, pointers, &Int32$info);
NamedType_t value = type->EnumInfo.tags[tag-1];
if (value.type && value.type->size > 0) {
ptrdiff_t byte_offset = sizeof(int32_t);
if (value.type->align && byte_offset % value.type->align > 0)
byte_offset += value.type->align - (byte_offset % value.type->align);
_serialize(obj + byte_offset, out, pointers, value.type);
}
}
public void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
int32_t tag = 0;
Int32$deserialize(in, &tag, pointers, &Int32$info);
NamedType_t value = type->EnumInfo.tags[tag-1];
if (value.type && value.type->size > 0) {
ptrdiff_t byte_offset = sizeof(int32_t);
if (value.type->align && byte_offset % value.type->align > 0)
byte_offset += value.type->align - (byte_offset % value.type->align);
_deserialize(in, outval + byte_offset, pointers, value.type);
}
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -12,6 +12,8 @@ PUREFUNC int32_t Enum$compare(const void *x, const void *y, const TypeInfo_t *ty
PUREFUNC bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type);
PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type);
void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Enum$metamethods ((metamethods_t){ \
.as_text=Enum$as_text, \
@ -19,6 +21,8 @@ PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type);
.equal=Enum$equal, \
.hash=Enum$hash, \
.is_none=Enum$is_none, \
.serialize=Enum$serialize, \
.deserialize=Enum$deserialize, \
})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -2,6 +2,7 @@
#include <stdbool.h>
#include "metamethods.h"
#include "types.h"
#include "util.h"
@ -15,6 +16,8 @@ PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t*);
#define Func$metamethods ((metamethods_t){ \
.as_text=Func$as_text, \
.is_none=Func$is_none, \
.serialize=cannot_serialize, \
.deserialize=cannot_deserialize, \
})
#define Function$info(typestr) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \

View File

@ -1,4 +1,6 @@
// Integer type infos and methods
#include <stdio.h> // Must be before gmp.h
#include <ctype.h>
#include <gc.h>
#include <gmp.h>
@ -367,6 +369,21 @@ static bool Int$is_none(const void *i, const TypeInfo_t*)
return ((Int_t*)i)->small == 0;
}
static void Int$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*)
{
mpz_t n;
mpz_init_set_int(n, *(Int_t*)obj);
mpz_out_raw(out, n);
}
static void Int$deserialize(FILE *in, void *obj, Array_t*, const TypeInfo_t*)
{
mpz_t n;
mpz_init(n);
mpz_inp_raw(n, in);
*(Int_t*)obj = Int$from_mpz(n);
}
public const TypeInfo_t Int$info = {
.size=sizeof(Int_t),
.align=__alignof__(Int_t),
@ -376,9 +393,61 @@ public const TypeInfo_t Int$info = {
.hash=Int$hash,
.as_text=Int$as_text,
.is_none=Int$is_none,
.serialize=Int$serialize,
.deserialize=Int$deserialize,
},
};
public void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*)
{
int64_t i = *(int64_t*)obj;
uint64_t z = (uint64_t)((i << 1) ^ (i >> 63)); // Zigzag encode
while (z >= 0x80) {
fputc((uint8_t)(z | 0x80), out);
z >>= 7;
}
fputc((uint8_t)z, out);
}
public void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*)
{
uint64_t z = 0;
for(size_t shift = 0; ; shift += 7) {
uint8_t byte = (uint8_t)fgetc(in);
z |= ((uint64_t)(byte & 0x7F)) << shift;
if ((byte & 0x80) == 0) break;
}
*(int64_t*)outval = (int64_t)((z >> 1) ^ -(z & 1)); // Zigzag decode
}
public void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*)
{
int32_t i = *(int32_t*)obj;
uint32_t z = (uint32_t)((i << 1) ^ (i >> 31)); // Zigzag encode
while (z >= 0x80) {
fputc((uint8_t)(z | 0x80), out);
z >>= 7;
}
fputc((uint8_t)z, out);
}
public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*)
{
uint32_t z = 0;
for(size_t shift = 0; ; shift += 7) {
uint8_t byte = (uint8_t)fgetc(in);
z |= ((uint32_t)(byte & 0x7F)) << shift;
if ((byte & 0x80) == 0) break;
}
*(int32_t*)outval = (int32_t)((z >> 1) ^ -(z & 1)); // Zigzag decode
}
// The space savings for smaller ints are not worth having:
#define Int16$serialize NULL
#define Int16$deserialize NULL
#define Int8$serialize NULL
#define Int8$deserialize NULL
#define DEFINE_INT_TYPE(c_type, KindOfInt, fmt, min_val, max_val, to_attr)\
public Text_t KindOfInt ## $as_text(const void *i, bool colorize, const TypeInfo_t*) { \
if (!i) return Text(#KindOfInt); \
@ -416,12 +485,12 @@ public const TypeInfo_t Int$info = {
} \
public PUREFUNC Optional ## KindOfInt ## _t KindOfInt ## $parse(Text_t text) { \
OptionalInt_t full_int = Int$parse(text); \
if (full_int.small == 0) return (Optional ## KindOfInt ## _t){.is_null=true}; \
if (full_int.small == 0) return (Optional ## KindOfInt ## _t){.is_none=true}; \
if (Int$compare_value(full_int, I(min_val)) < 0) { \
return (Optional ## KindOfInt ## _t){.is_null=true}; \
return (Optional ## KindOfInt ## _t){.is_none=true}; \
} \
if (Int$compare_value(full_int, I(max_val)) > 0) { \
return (Optional ## KindOfInt ## _t){.is_null=true}; \
return (Optional ## KindOfInt ## _t){.is_none=true}; \
} \
return (Optional ## KindOfInt ## _t){.i=Int_to_ ## KindOfInt(full_int, true)}; \
} \
@ -433,6 +502,8 @@ public const TypeInfo_t Int$info = {
.metamethods={ \
.compare=KindOfInt##$compare, \
.as_text=KindOfInt##$as_text, \
.serialize=KindOfInt##$serialize, \
.deserialize=KindOfInt##$deserialize, \
}, \
};

View File

@ -25,7 +25,7 @@
#define DEFINE_INT_TYPE(c_type, type_name, to_attr) \
typedef struct { \
c_type i; \
bool is_null:1; \
bool is_none:1; \
} Optional ## type_name ## _t; \
Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \
PUREFUNC int32_t type_name ## $compare(const void *x, const void *y, const TypeInfo_t *type); \
@ -73,16 +73,21 @@ DEFINE_INT_TYPE(int16_t, Int16, CONSTFUNC)
DEFINE_INT_TYPE(int8_t, Int8, CONSTFUNC)
#undef DEFINE_INT_TYPE
#define NONE_INT64 ((OptionalInt64_t){.is_null=true})
#define NONE_INT32 ((OptionalInt32_t){.is_null=true})
#define NONE_INT16 ((OptionalInt16_t){.is_null=true})
#define NONE_INT8 ((OptionalInt8_t){.is_null=true})
#define NONE_INT64 ((OptionalInt64_t){.is_none=true})
#define NONE_INT32 ((OptionalInt32_t){.is_none=true})
#define NONE_INT16 ((OptionalInt16_t){.is_none=true})
#define NONE_INT8 ((OptionalInt8_t){.is_none=true})
#define Int64$abs(...) I64(labs(__VA_ARGS__))
#define Int32$abs(...) I32(abs(__VA_ARGS__))
#define Int16$abs(...) I16(abs(__VA_ARGS__))
#define Int8$abs(...) I8(abs(__VA_ARGS__))
void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*);
void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*);
void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*);
void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*);
#define OptionalInt_t Int_t
Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type);

View File

@ -8,6 +8,7 @@
#include <err.h>
#include "memory.h"
#include "metamethods.h"
#include "text.h"
#include "types.h"
#include "util.h"
@ -22,6 +23,8 @@ public const TypeInfo_t Memory$info = {
.align=0,
.metamethods={
.as_text=Memory$as_text,
.serialize=cannot_serialize,
.deserialize=cannot_deserialize,
},
};

View File

@ -5,14 +5,17 @@
#include "arrays.h"
#include "bools.h"
#include "bytes.h"
#include "channels.h"
#include "functiontype.h"
#include "integers.h"
#include "metamethods.h"
#include "optionals.h"
#include "pointers.h"
#include "siphash.h"
#include "tables.h"
#include "text.h"
#include "types.h"
#include "util.h"
PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type)
@ -51,10 +54,72 @@ public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *
return type->metamethods.as_text(obj, colorize, type);
}
public void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
if (type->metamethods.serialize)
return type->metamethods.serialize(obj, out, pointers, type);
fwrite(obj, (size_t)type->size, 1, out);
}
public Array_t generic_serialize(const void *x, const TypeInfo_t *type)
{
char *buf = NULL;
size_t size = 0;
FILE *stream = open_memstream(&buf, &size);
Table_t pointers = {};
_serialize(x, stream, &pointers, type);
fclose(stream);
Array_t bytes = {
.data=GC_MALLOC_ATOMIC(size),
.length=(int64_t)size,
.stride=1,
.atomic=1,
};
memcpy(bytes.data, buf, size);
free(buf);
return bytes;
}
public void _deserialize(FILE *input, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
if (type->metamethods.deserialize) {
type->metamethods.deserialize(input, outval, pointers, type);
return;
}
fread(outval, (size_t)type->size, 1, input);
}
public void generic_deserialize(Array_t bytes, void *outval, const TypeInfo_t *type)
{
if (bytes.stride != 1)
Array$compact(&bytes, 1);
FILE *input = fmemopen(bytes.data, (size_t)bytes.length, "r");
Array_t pointers = {};
_deserialize(input, outval, &pointers, type);
fclose(input);
}
public int generic_print(const void *obj, bool colorize, const TypeInfo_t *type)
{
Text_t text = generic_as_text(obj, colorize, type);
return Text$print(stdout, text) + printf("\n");
}
__attribute__((noreturn))
public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type)
{
Text_t typestr = generic_as_text(NULL, false, type);
fail("Values of type %k cannot be serialized or deserialized!", &typestr);
}
__attribute__((noreturn))
public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type)
{
Text_t typestr = generic_as_text(NULL, false, type);
fail("Values of type %k cannot be serialized or deserialized!", &typestr);
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include "datatypes.h"
#include "types.h"
#include "util.h"
@ -10,6 +11,12 @@ PUREFUNC uint64_t generic_hash(const void *obj, const TypeInfo_t *type);
PUREFUNC int32_t generic_compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool generic_equal(const void *x, const void *y, const TypeInfo_t *type);
Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type);
void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
Array_t generic_serialize(const void *x, const TypeInfo_t *type);
void _deserialize(FILE *input, void *outval, Array_t *pointers, const TypeInfo_t *type);
void generic_deserialize(Array_t bytes, void *outval, const TypeInfo_t *type);
int generic_print(const void *obj, bool colorize, const TypeInfo_t *type);
void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type);
void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type);
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -2,6 +2,7 @@
#include <ctype.h>
#include <gc.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

View File

@ -13,7 +13,7 @@
#include "threads.h"
#include "util.h"
public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type)
public PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type)
{
if (non_optional_type->metamethods.is_none)
return non_optional_type->metamethods.is_none(obj, non_optional_type);
@ -23,14 +23,14 @@ public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_typ
PUREFUNC public uint64_t Optional$hash(const void *obj, const TypeInfo_t *type)
{
return is_null(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type);
return is_none(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type);
}
PUREFUNC public int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type)
{
if (x == y) return 0;
bool x_is_null = is_null(x, type->OptionalInfo.type);
bool y_is_null = is_null(y, type->OptionalInfo.type);
bool x_is_null = is_none(x, type->OptionalInfo.type);
bool y_is_null = is_none(y, type->OptionalInfo.type);
if (x_is_null && y_is_null) return 0;
else if (x_is_null != y_is_null) return (int32_t)y_is_null - (int32_t)x_is_null;
else return generic_compare(x, y, type->OptionalInfo.type);
@ -40,8 +40,8 @@ PUREFUNC public bool Optional$equal(const void *x, const void *y, const TypeInfo
{
if (x == y) return true;
bool x_is_null = is_null(x, type->OptionalInfo.type);
bool y_is_null = is_null(y, type->OptionalInfo.type);
bool x_is_null = is_none(x, type->OptionalInfo.type);
bool y_is_null = is_none(y, type->OptionalInfo.type);
if (x_is_null && y_is_null) return true;
else if (x_is_null != y_is_null) return false;
else return generic_equal(x, y, type->OptionalInfo.type);
@ -52,9 +52,42 @@ public Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t
if (!obj)
return Text$concat(generic_as_text(obj, colorize, type->OptionalInfo.type), Text("?"));
if (is_null(obj, type->OptionalInfo.type))
if (is_none(obj, type->OptionalInfo.type))
return colorize ? Text("\x1b[31mNONE\x1b[m") : Text("NONE");
return generic_as_text(obj, colorize, type->OptionalInfo.type);
}
public void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
bool has_value = !is_none(obj, type->OptionalInfo.type);
fputc((int)has_value, out);
if (has_value)
_serialize(obj, out, pointers, type->OptionalInfo.type);
}
public void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
bool has_value = (bool)fgetc(in);
const TypeInfo_t *nonnull = type->OptionalInfo.type;
if (has_value) {
memset(outval, 0, (size_t)type->size);
_deserialize(in, outval, pointers, nonnull);
} else {
if (nonnull->tag == TextInfo)
*(Text_t*)outval = (Text_t){.length=-1};
else if (nonnull->tag == ArrayInfo)
*(Array_t*)outval = (Array_t){.length=-1};
else if (nonnull->tag == TableInfo)
*(Table_t*)outval = (Table_t){.entries={.length=-1}};
else if (nonnull == &Num$info)
*(double*)outval = NAN;
else if (nonnull == &Num32$info)
*(float*)outval = NAN;
else if (nonnull->tag == StructInfo || (nonnull->tag == OpaqueInfo && type->size > nonnull->size))
memset(outval + type->size, -1, (size_t)(type->size - nonnull->size));
else
memset(outval, 0, (size_t)type->size);
}
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1

View File

@ -29,12 +29,16 @@ PUREFUNC uint64_t Optional$hash(const void *obj, const TypeInfo_t *type);
PUREFUNC int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type);
Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type);
void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Optional$metamethods ((metamethods_t){ \
.hash=Optional$hash, \
.compare=Optional$compare, \
.equal=Optional$equal, \
.as_text=Optional$as_text, \
.serialize=Optional$serialize, \
.deserialize=Optional$deserialize, \
})
#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \

View File

@ -83,4 +83,43 @@ PUREFUNC public bool Pointer$is_none(const void *x, const TypeInfo_t*)
return *(void**)x == NULL;
}
public void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
void *ptr = *(void**)obj;
assert(ptr != NULL);
const TypeInfo_t ptr_to_int_table = {.size=sizeof(Table_t), .align=__alignof__(Table_t),
.tag=TableInfo, .TableInfo.key=type, .TableInfo.value=&Int64$info};
int64_t *id_ptr = Table$get(*pointers, &ptr, &ptr_to_int_table);
int64_t id;
if (id_ptr) {
id = *id_ptr;
} else {
id = pointers->entries.length + 1;
Table$set(pointers, &ptr, &id, &ptr_to_int_table);
}
Int64$serialize(&id, out, pointers, &Int64$info);
if (!id_ptr)
_serialize(ptr, out, pointers, type->PointerInfo.pointed);
}
public void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
int64_t id = 0;
Int64$deserialize(in, &id, pointers, &Int64$info);
assert(id != 0);
if (id > pointers->length) {
void *obj = GC_MALLOC((size_t)type->PointerInfo.pointed->size);
Array$insert(pointers, &obj, I(0), sizeof(void*));
_deserialize(in, obj, pointers, type->PointerInfo.pointed);
*(void**)outval = obj;
} else {
*(void**)outval = *(void**)(pointers->data + (id-1)*pointers->stride);
}
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -5,6 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "datatypes.h"
#include "types.h"
#include "util.h"
@ -12,6 +13,8 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type);
PUREFUNC int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*);
void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Null(t) (t*)NULL
#define POINTER_TYPE(_sigil, _pointed) (&(TypeInfo_t){\
@ -22,6 +25,8 @@ PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*);
.compare=Pointer$compare, \
.equal=Pointer$equal, \
.is_none=Pointer$is_none, \
.serialize=Pointer$serialize, \
.deserialize=Pointer$deserialize, \
})
#define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \

View File

@ -76,24 +76,24 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
return parsed.small != 0;
} else if (info == &Int64$info) {
OptionalInt64_t parsed = Int64$parse(Text$from_str(arg));
if (!parsed.is_null)
if (!parsed.is_none)
*(OptionalInt64_t*)dest = parsed;
return !parsed.is_null;
return !parsed.is_none;
} else if (info == &Int32$info) {
OptionalInt32_t parsed = Int32$parse(Text$from_str(arg));
if (!parsed.is_null)
if (!parsed.is_none)
*(OptionalInt32_t*)dest = parsed;
return !parsed.is_null;
return !parsed.is_none;
} else if (info == &Int16$info) {
OptionalInt16_t parsed = Int16$parse(Text$from_str(arg));
if (!parsed.is_null)
if (!parsed.is_none)
*(OptionalInt16_t*)dest = parsed;
return !parsed.is_null;
return !parsed.is_none;
} else if (info == &Int8$info) {
OptionalInt8_t parsed = Int8$parse(Text$from_str(arg));
if (!parsed.is_null)
if (!parsed.is_none)
*(OptionalInt8_t*)dest = parsed;
return !parsed.is_null;
return !parsed.is_none;
} else if (info == &Bool$info) {
OptionalBool_t parsed = Bool$parse(Text$from_str(arg));
if (parsed != NONE_BOOL)

View File

@ -167,4 +167,58 @@ PUREFUNC public bool Struct$is_none(const void *obj, const TypeInfo_t *type)
return *(bool*)(obj + type->size);
}
public void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
ptrdiff_t byte_offset = 0;
ptrdiff_t bit_offset = 0;
for (int i = 0; i < type->StructInfo.num_fields; i++) {
NamedType_t field = type->StructInfo.fields[i];
if (field.type == &Bool$info) {
bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1;
fputc((int)b, out);
bit_offset += 1;
if (bit_offset >= 8) {
byte_offset += 1;
bit_offset = 0;
}
} else {
if (bit_offset > 0) {
byte_offset += 1;
bit_offset = 0;
}
if (field.type->align && byte_offset % field.type->align > 0)
byte_offset += field.type->align - (byte_offset % field.type->align);
_serialize(obj + byte_offset, out, pointers, field.type);
byte_offset += field.type->size;
}
}
}
public void Struct$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
ptrdiff_t byte_offset = 0;
ptrdiff_t bit_offset = 0;
for (int i = 0; i < type->StructInfo.num_fields; i++) {
NamedType_t field = type->StructInfo.fields[i];
if (field.type == &Bool$info) {
bool b = (bool)fgetc(in);
*(char*)(outval + byte_offset) |= (b << bit_offset);
bit_offset += 1;
if (bit_offset >= 8) {
byte_offset += 1;
bit_offset = 0;
}
} else {
if (bit_offset > 0) {
byte_offset += 1;
bit_offset = 0;
}
if (field.type->align && byte_offset % field.type->align > 0)
byte_offset += field.type->align - (byte_offset % field.type->align);
_deserialize(in, outval + byte_offset, pointers, field.type);
byte_offset += field.type->size;
}
}
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -12,6 +12,8 @@ PUREFUNC int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *
PUREFUNC bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type);
PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type);
void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Struct$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Struct$metamethods ((metamethods_t){ \
.hash=Struct$hash, \
@ -19,6 +21,8 @@ PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type);
.equal=Struct$equal, \
.as_text=Struct$as_text, \
.is_none=Struct$is_none, \
.serialize=Struct$serialize, \
.deserialize=Struct$deserialize, \
})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -647,4 +647,41 @@ PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t*)
return ((Table_t*)obj)->entries.length < 0;
}
public void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
{
Table_t *t = (Table_t*)obj;
int64_t len = t->entries.length;
Int64$serialize(&len, out, pointers, &Int64$info);
size_t offset = value_offset(type);
for (int64_t i = 0; i < len; i++) {
_serialize(t->entries.data + i*t->entries.stride, out, pointers, type->TableInfo.key);
_serialize(t->entries.data + i*t->entries.stride + offset, out, pointers, type->TableInfo.value);
}
Optional$serialize(&t->fallback, out, pointers, Optional$info(sizeof(void*), __alignof__(void*), Pointer$info("&", type)));
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-protector"
public void Table$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
{
int64_t len;
Int64$deserialize(in, &len, pointers, &Int$info);
Table_t t = {};
for (int64_t i = 0; i < len; i++) {
char key[type->TableInfo.key->size];
_deserialize(in, key, pointers, type->TableInfo.key);
char value[type->TableInfo.value->size];
_deserialize(in, value, pointers, type->TableInfo.value);
Table$set(&t, key, value, type);
}
Optional$deserialize(in, &t.fallback, pointers, Optional$info(sizeof(void*), __alignof__(void*), Pointer$info("&", type)));
*(Table_t*)outval = t;
}
#pragma GCC diagnostic pop
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1

View File

@ -77,6 +77,8 @@ PUREFUNC void *Table$str_get_raw(Table_t t, const char *key);
void Table$str_set(Table_t *t, const char *key, const void *value);
void *Table$str_reserve(Table_t *t, const char *key, const void *value);
void Table$str_remove(Table_t *t, const char *key);
void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
void Table$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Table$length(t) ((t).entries.length)
@ -88,6 +90,8 @@ extern const TypeInfo_t CStrToVoidStarTable;
.equal=Table$equal, \
.hash=Table$hash, \
.is_none=Table$is_none, \
.serialize=Table$serialize, \
.deserialize=Table$deserialize, \
})
#define Table$info(key_expr, value_expr) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \

View File

@ -1377,6 +1377,24 @@ PUREFUNC public bool Text$is_none(const void *t, const TypeInfo_t*)
return ((Text_t*)t)->length < 0;
}
public void Text$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *)
{
const char *str = Text$as_c_string(*(Text_t*)obj);
int64_t len = (int64_t)strlen(str);
Int64$serialize(&len, out, pointers, &Int64$info);
fwrite(str, sizeof(char), (size_t)len, out);
}
public void Text$deserialize(FILE *in, void *out, Array_t *pointers, const TypeInfo_t *)
{
int64_t len = -1;
Int64$deserialize(in, &len, pointers, &Int64$info);
char *buf = GC_MALLOC_ATOMIC((size_t)len+1);
fread(buf, sizeof(char), (size_t)len, in);
buf[len+1] = '\0';
*(Text_t*)out = Text$from_strn(buf, (size_t)len);
}
public const TypeInfo_t Text$info = {
.size=sizeof(Text_t),
.align=__alignof__(Text_t),

View File

@ -60,6 +60,8 @@ Text_t Text$join(Text_t glue, Array_t pieces);
Text_t Text$repeat(Text_t text, Int_t count);
int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index);
uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index);
void Text$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);
void Text$deserialize(FILE *in, void *out, Array_t *, const TypeInfo_t *);
MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index)
{
@ -75,6 +77,8 @@ extern const TypeInfo_t Text$info;
.compare=Text$compare, \
.equal=Text$equal, \
.is_none=Text$is_none, \
.serialize=Text$serialize, \
.deserialize=Text$deserialize, \
})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -14,6 +14,7 @@
#include "arrays.h"
#include "datatypes.h"
#include "metamethods.h"
#include "rng.h"
#include "text.h"
#include "threads.h"
@ -71,6 +72,8 @@ public const TypeInfo_t Thread$info = {
.metamethods={
.as_text=Thread$as_text,
.is_none=Thread$is_none,
.serialize=cannot_serialize,
.deserialize=cannot_deserialize,
},
};

View File

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "datatypes.h"
@ -15,6 +16,8 @@ typedef struct {
bool (*equal)(const void*, const void*, const TypeInfo_t*);
Text_t (*as_text)(const void*, bool, const TypeInfo_t*);
bool (*is_none)(const void*, const TypeInfo_t*);
void (*serialize)(const void*, FILE*, Table_t*, const TypeInfo_t*);
void (*deserialize)(FILE*, void*, Array_t*, const TypeInfo_t*);
} metamethods_t;
typedef struct {
@ -68,7 +71,8 @@ struct TypeInfo_s {
};
#define Type$info(typestr) &((TypeInfo_t){.size=sizeof(TypeInfo_t), .align=__alignof__(TypeInfo_t), \
.tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr})
.tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr, \
.metamethods={.serialize=cannot_serialize, .deserialize=cannot_deserialize}})
extern const TypeInfo_t Void$info;
extern const TypeInfo_t Abort$info;

View File

@ -71,7 +71,7 @@ CORD compile_struct_header(env_t *env, ast_t *ast)
full_name, "_t value;\n"
"struct {\n"
"char _padding[", heap_strf("%zu", unpadded_struct_size(t)), "];\n",
"Bool_t is_null;\n"
"Bool_t is_none:1;\n"
"};\n"
"};\n"
"} ", namespace_prefix(env, env->namespace), "$Optional", def->name, "_t;\n"

95
test/serialization.tm Normal file
View File

@ -0,0 +1,95 @@
struct Foo(name:Text, next=NONE:@Foo)
enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text))
func main():
do:
>> obj := now()
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Moment == obj
= yes
do:
>> obj := Int64(123)
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Int64 == obj
= yes
do:
>> obj := 5
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Int == obj
= yes
do:
>> obj := 9999999999999999999999999999999999999999999999999999
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Int == obj
= yes
do:
>> obj := "Héllo"
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Text
>> DESERIALIZE(bytes):Text == obj
= yes
do:
>> obj := [Int64(10), Int64(20), Int64(30)]:reversed()
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):[Int64] == obj
= yes
do:
>> obj := yes
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Bool == obj
= yes
do:
>> obj := @[10, 20]
>> bytes := obj:serialize()
>> roundtrip := DESERIALIZE(bytes):@[Int]
>> roundtrip == obj
= no
>> roundtrip[] == obj[]
= yes
do:
>> obj := {"A":10, "B":20; fallback={"C":30}}
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):{Text:Int} == obj
= yes
do:
>> obj := @Foo("root")
>> obj.next = @Foo("abcdef", next=obj)
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):@Foo
= @Foo(name="root", next=@Foo(name="abcdef", next=@~1))
do:
>> obj := MyEnum.Two(123, "OKAY")
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):MyEnum == obj
= yes
do:
>> obj := "Hello"?
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Text? == obj
= yes
do:
>> obj := {10, 20, 30}
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):{Int} == obj
= yes
do:
>> obj := NONE:Num
>> bytes := obj:serialize()
>> DESERIALIZE(bytes):Num? == obj
= yes

View File

@ -721,6 +721,11 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case FunctionCall: {
auto call = Match(ast, FunctionCall);
// HACK:
if (call->fn->tag == Var && streq(Match(call->fn, Var)->name, "serialize"))
return Type(ArrayType, Type(ByteType));
type_t *fn_type_t = get_type(env, call->fn);
if (!fn_type_t)
code_err(call->fn, "I couldn't find this function");
@ -741,6 +746,10 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case MethodCall: {
auto call = Match(ast, MethodCall);
if (streq(call->name, "serialize"))
return Type(ArrayType, Type(ByteType));
type_t *self_value_t = value_type(get_type(env, call->self));
switch (self_value_t->tag) {
case ArrayType: {
@ -1245,6 +1254,8 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case Moment: return Type(MomentType);
case Unknown: code_err(ast, "I can't figure out the type of: %W", ast);
case Serialize: return Type(ArrayType, Type(ByteType));
case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
}
#pragma GCC diagnostic pop
code_err(ast, "I can't figure out the type of: %W", ast);