aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md2
-rw-r--r--docs/serialization.md52
-rw-r--r--src/ast.c9
-rw-r--r--src/ast.h5
-rw-r--r--src/compile/expressions.c13
-rw-r--r--src/compile/functions.c11
-rw-r--r--src/formatter/formatter.c11
-rw-r--r--src/parse/expressions.c26
-rw-r--r--src/parse/expressions.h1
-rw-r--r--src/parse/utils.c6
-rw-r--r--src/typecheck.c5
-rw-r--r--test/serialization.tm2
12 files changed, 43 insertions, 100 deletions
diff --git a/CHANGES.md b/CHANGES.md
index 0c34e86c..776a1b07 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,8 @@
- `>> ... = ...` form of doctests. They are now called "debug logs" and you
can specify multiple values: `>> a, b, c`
- `extend` blocks
+ - `deserialize` operation and `.serialized()` method call
+ - Instead, convert to and from `[Byte]`
- Struct fields that start with underscores can be accessed again and function
arguments that start with underscore can be passed (but only as keyword
arguments).
diff --git a/docs/serialization.md b/docs/serialization.md
index a938e316..287cbda7 100644
--- a/docs/serialization.md
+++ b/docs/serialization.md
@@ -10,31 +10,42 @@ original value.
## Serializing
-To serialize data, simply call the method `.serialized()` on any value and it
-will return a list of bytes that encode the value's data:
+To serialize data, declare a variable with type `[Byte]` and assign any
+arbitrary type to that value.
```tomo
value := Int64(5)
->> serialized := value.serialized()
-= [0x0A] : [Byte]
+serialized : [Byte] = value
+assert serialized == [0x0A]
```
Serialization produces a fairly compact representation of data as a flat list
of bytes. In this case, a 64-bit integer can be represented in a single byte
because it's a small number.
+The same process works with more complicated data:
+
+```tomo
+struct Foo(x:Int, y:Text)
+
+foo := Foo(123, "Hello")
+serialized : [Byte] = foo
+assert serialized == [0x00, 0xf6, 0x01, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
+```
+
## Deserializing
-To deserialize data, you must provide its type explicitly using the syntax
-`deserialize(bytes -> Type)`:
+To deserialize data, you can assign a list of bytes to a variable with your
+target type:
```tomo
-i := 123
-bytes := i.serialized()
+value_bytes : [Byte] = [Byte(0x0A)]
+value : Int64 = value_bytes
+assert value == 5
-roundtripped := deserialize(bytes -> Int)
->> roundtripped
-= 123 :Int
+foo_bytes : [Byte] = [0x00, 0xf6, 0x01, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
+foo : Foo = foo_bytes
+assert foo == Foo(123, "Hello")
```
## Pointers
@@ -57,11 +68,12 @@ struct Cycle(name:Text, next:@Cycle?=none)
c := @Cycle("A")
c.next = @Cycle("B", next=c)
>> c
-= @Cycle(name="A", next=@Cycle(name="B", next=@~1))
->> serialized := c.serialized()
-= [0x02, 0x02, 0x41, 0x01, 0x04, 0x02, 0x42, 0x01, 0x02] : [Byte]
->> roundtrip := DESERIALIZE(serialized):@Cycle
-= @Cycle(name="A", next=@Cycle(name="B", next=@~1)) : @Cycle
+# @Cycle(name="A", next=@Cycle(name="B", next=@~1))
+>> bytes : [Byte] = c
+# [0x02, 0x02, 0x41, 0x01, 0x04, 0x02, 0x42, 0x01, 0x02] : [Byte]
+>> roundtrip : @Cycle = bytes
+# @Cycle(name="A", next=@Cycle(name="B", next=@~1)) : @Cycle
+assert roundtrip.next.next == roundtrip
```
The deserialized version of the data correctly preserves the cycle
@@ -70,6 +82,8 @@ only 9 bytes for the whole thing!
## Unserializable Types
-Unfortunately, not all types can be easily serialized. In particular, types and
-functions cannot be serialized because their data contents cannot be easily
-converted to portable byte lists. All other datatypes _can_ be serialized.
+Unfortunately, not all types can be easily serialized. In particular, functions
+(and closures) cannot be serialized because their data contents cannot be
+easily converted to portable byte lists. Type objects themselves (e.g. the
+variable `Text`) also cannot be serialized. All other datatypes _can_ be
+serialized.
diff --git a/src/ast.c b/src/ast.c
index dfc1213c..432ce2d4 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -274,7 +274,6 @@ Text_t ast_to_sexp(ast_t *ast) {
T(Assert, "(Assert ", ast_to_sexp(data.expr), " ", optional_sexp("message", data.message), ")");
T(Use, "(Use ", optional_sexp("var", data.var), " ", quoted_text(data.path), ")");
T(InlineCCode, "(InlineCCode ", ast_list_to_sexp(data.chunks), optional_type_sexp("type", data.type_ast), ")");
- T(Deserialize, "(Deserialize ", type_ast_to_sexp(data.type), " ", ast_to_sexp(data.value), ")");
default: errx(1, "S-expressions are not implemented for this AST");
#undef T
}
@@ -676,10 +675,6 @@ void ast_visit(ast_t *ast, void (*visitor)(ast_t *, void *), void *userdata) {
ast_visit_list(Match(ast, InlineCCode)->chunks, visitor, userdata);
return;
}
- case Deserialize: {
- ast_visit(Match(ast, Deserialize)->value, visitor, userdata);
- return;
- }
default: errx(1, "Visiting is not supported for this AST: %s", Text$as_c_string(ast_to_sexp(ast)));
#undef T
}
@@ -777,10 +772,6 @@ static void _type_ast_visit(ast_t *ast, void *userdata) {
_recursive_type_ast_visit(Match(ast, InlineCCode)->type_ast, userdata);
break;
}
- case Deserialize: {
- _recursive_type_ast_visit(Match(ast, Deserialize)->type, userdata);
- break;
- }
default: break;
}
}
diff --git a/src/ast.h b/src/ast.h
index 60cd9077..93723dbb 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -274,7 +274,6 @@ typedef enum {
Assert,
Use,
InlineCCode,
- Deserialize,
ExplicitlyTyped,
} ast_e;
#define NUM_AST_TAGS (ExplicitlyTyped + 1)
@@ -454,10 +453,6 @@ struct ast_s {
type_ast_t *type_ast;
} InlineCCode;
struct {
- ast_t *value;
- type_ast_t *type;
- } Deserialize;
- struct {
ast_t *ast;
struct type_s *type;
} ExplicitlyTyped;
diff --git a/src/compile/expressions.c b/src/compile/expressions.c
index 5ec90191..fcd1d136 100644
--- a/src/compile/expressions.c
+++ b/src/compile/expressions.c
@@ -205,19 +205,6 @@ Text_t compile(env_t *env, ast_t *ast) {
case Lambda: return compile_lambda(env, ast);
case MethodCall: return compile_method_call(env, ast);
case FunctionCall: return compile_function_call(env, ast);
- case Deserialize: {
- ast_t *value = Match(ast, Deserialize)->value;
- type_t *value_type = get_type(env, value);
- if (!type_eq(value_type, Type(ListType, Type(ByteType))))
- code_err(value, "This value should be a list of bytes, not a ", type_to_text(value_type));
- type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type);
- return Texts("({ ", compile_declaration(t, Text("deserialized")),
- ";\n"
- "generic_deserialize(",
- compile(env, value), ", &deserialized, ", compile_type_info(t),
- ");\n"
- "deserialized; })");
- }
case ExplicitlyTyped: {
return compile_to_type(env, Match(ast, ExplicitlyTyped)->ast, get_type(env, ast));
}
diff --git a/src/compile/functions.c b/src/compile/functions.c
index ab267e2c..e1b9c89a 100644
--- a/src/compile/functions.c
+++ b/src/compile/functions.c
@@ -567,10 +567,6 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t
add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Assert)->message);
break;
}
- case Deserialize: {
- add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Deserialize)->value);
- break;
- }
case ExplicitlyTyped: {
add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, ExplicitlyTyped)->ast);
break;
@@ -805,13 +801,6 @@ public
Text_t compile_method_call(env_t *env, ast_t *ast) {
DeclareMatch(call, ast, MethodCall);
type_t *self_t = get_type(env, call->self);
-
- if (streq(call->name, "serialized")) {
- if (call->args) code_err(ast, ".serialized() doesn't take any arguments");
- return Texts("generic_serialize((", compile_declaration(self_t, Text("[1]")), "){", compile(env, call->self),
- "}, ", compile_type_info(self_t), ")");
- }
-
type_t *self_value_t = value_type(self_t);
if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) {
return compile(env, WrapAST(ast, FunctionCall,
diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c
index 41b1c4aa..55fff920 100644
--- a/src/formatter/formatter.c
+++ b/src/formatter/formatter.c
@@ -366,11 +366,6 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" ");
return Texts(lhs, space, Text$from_str(binop_info[ast->tag].operator), space, rhs);
}
- /*inline*/ case Deserialize: {
- DeclareMatch(deserialize, ast, Deserialize);
- return Texts("deserialize(", fmt_inline(deserialize->value, comments), " -> ", format_type(deserialize->type),
- ")");
- }
/*inline*/ case Use: {
DeclareMatch(use, ast, Use);
// struct {
@@ -806,12 +801,6 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) {
Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" ");
return Texts(lhs, space, Text$from_str(binop_info[ast->tag].operator), space, rhs);
}
- /*multiline*/ case Deserialize: {
- if (inlined_fits) return inlined;
- DeclareMatch(deserialize, ast, Deserialize);
- return Texts("deserialize(", fmt(deserialize->value, comments, indent), " -> ", format_type(deserialize->type),
- ")");
- }
/*multiline*/ case Use: {
assert(inlined.length > 0);
return inlined;
diff --git a/src/parse/expressions.c b/src/parse/expressions.c
index 3cb47669..5be48b89 100644
--- a/src/parse/expressions.c
+++ b/src/parse/expressions.c
@@ -157,24 +157,6 @@ ast_t *parse_none(parse_ctx_t *ctx, const char *pos) {
return NewAST(ctx->file, start, pos, None);
}
-ast_t *parse_deserialize(parse_ctx_t *ctx, const char *pos) {
- const char *start = pos;
- if (!match_word(&pos, "deserialize")) return NULL;
-
- spaces(&pos);
- expect_str(ctx, start, &pos, "(", "I expected arguments for this `deserialize` call");
- whitespace(ctx, &pos);
- ast_t *value = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here");
- whitespace(ctx, &pos);
- expect_str(ctx, start, &pos, "->",
- "I expected a `-> Type` for this `deserialize` call so I know what it deserializes to");
- whitespace(ctx, &pos);
- type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this deserialization");
- whitespace(ctx, &pos);
- expect_closing(ctx, &pos, ")", "I expected a closing ')' for this `deserialize` call");
- return NewAST(ctx->file, start, pos, Deserialize, .value = value, .type = type);
-}
-
ast_t *parse_var(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
const char *name = get_id(&pos);
@@ -190,10 +172,10 @@ ast_t *parse_term_no_suffix(parse_ctx_t *ctx, const char *pos) {
|| (term = parse_heap_alloc(ctx, pos)) || (term = parse_stack_reference(ctx, pos))
|| (term = parse_bool(ctx, pos)) || (term = parse_text(ctx, pos)) || (term = parse_path(ctx, pos))
|| (term = parse_lambda(ctx, pos)) || (term = parse_parens(ctx, pos)) || (term = parse_table(ctx, pos))
- || (term = parse_deserialize(ctx, pos)) || (term = parse_var(ctx, pos)) || (term = parse_list(ctx, pos))
- || (term = parse_reduction(ctx, pos)) || (term = parse_pass(ctx, pos)) || (term = parse_defer(ctx, pos))
- || (term = parse_skip(ctx, pos)) || (term = parse_stop(ctx, pos)) || (term = parse_return(ctx, pos))
- || (term = parse_not(ctx, pos)) || (term = parse_inline_c(ctx, pos)));
+ || (term = parse_var(ctx, pos)) || (term = parse_list(ctx, pos)) || (term = parse_reduction(ctx, pos))
+ || (term = parse_pass(ctx, pos)) || (term = parse_defer(ctx, pos)) || (term = parse_skip(ctx, pos))
+ || (term = parse_stop(ctx, pos)) || (term = parse_return(ctx, pos)) || (term = parse_not(ctx, pos))
+ || (term = parse_inline_c(ctx, pos)));
return term;
}
diff --git a/src/parse/expressions.h b/src/parse/expressions.h
index c7c97f24..8da13b0e 100644
--- a/src/parse/expressions.h
+++ b/src/parse/expressions.h
@@ -19,4 +19,3 @@ ast_t *parse_stack_reference(parse_ctx_t *ctx, const char *pos);
ast_t *parse_term(parse_ctx_t *ctx, const char *pos);
ast_t *parse_term_no_suffix(parse_ctx_t *ctx, const char *pos);
ast_t *parse_var(parse_ctx_t *ctx, const char *pos);
-ast_t *parse_deserialize(parse_ctx_t *ctx, const char *pos);
diff --git a/src/parse/utils.c b/src/parse/utils.c
index 28cf0964..f1b518ca 100644
--- a/src/parse/utils.c
+++ b/src/parse/utils.c
@@ -12,9 +12,9 @@
#include "utils.h"
static const char *keywords[] = {
- "C_code", "_max_", "_min_", "and", "assert", "break", "continue", "defer", "deserialize", "do", "else", "enum",
- "for", "func", "if", "in", "lang", "mod", "mod1", "no", "none", "not", "or", "pass",
- "return", "skip", "skip", "stop", "struct", "then", "unless", "use", "when", "while", "xor", "yes",
+ "C_code", "_max_", "_min_", "and", "assert", "break", "continue", "defer", "do", "else", "enum", "for",
+ "func", "if", "in", "lang", "mod", "mod1", "no", "none", "not", "or", "pass", "return",
+ "skip", "skip", "stop", "struct", "then", "unless", "use", "when", "while", "xor", "yes",
};
CONSTFUNC bool is_keyword(const char *word) {
diff --git a/src/typecheck.c b/src/typecheck.c
index 906236c3..429a162f 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -929,10 +929,6 @@ type_t *get_type(env_t *env, ast_t *ast) {
}
case MethodCall: {
DeclareMatch(call, ast, MethodCall);
-
- if (streq(call->name, "serialized")) // Data serialization
- return Type(ListType, Type(ByteType));
-
type_t *self_value_t = get_type(env, call->self);
if (!self_value_t) code_err(call->self, "Couldn't get the type of this value");
self_value_t = value_type(self_value_t);
@@ -1509,7 +1505,6 @@ type_t *get_type(env_t *env, ast_t *ast) {
return type_ast ? parse_type_ast(env, type_ast) : Type(VoidType);
}
case Unknown: code_err(ast, "I can't figure out the type of: ", ast_to_sexp_str(ast));
- case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
case ExplicitlyTyped: return Match(ast, ExplicitlyTyped)->type;
}
#ifdef __GNUC__
diff --git a/test/serialization.tm b/test/serialization.tm
index 18858d0e..39c1624f 100644
--- a/test/serialization.tm
+++ b/test/serialization.tm
@@ -65,7 +65,7 @@ func main()
>> obj := MyEnum.Two(123, "OKAY")
>> bytes : [Byte] = obj
>> roundtrip : MyEnum = bytes
- assert deserialize(bytes -> MyEnum) == obj
+ assert roundtrip == obj
do
>> obj : Text? = "Hello"