diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-10-05 18:05:07 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-10-05 18:05:07 -0400 |
| commit | c74fba540448f1d4b1aec4de8f3d9ffc395fdde0 (patch) | |
| tree | 67f2f62e2ccf1889ebbeef725a6782b8ba9f5912 | |
| parent | 398d2cab6988e20c59e7037ff7ef551540339abb (diff) | |
Deprecate `deserialize` keyword and `.serialized()` method
| -rw-r--r-- | CHANGES.md | 2 | ||||
| -rw-r--r-- | docs/serialization.md | 52 | ||||
| -rw-r--r-- | src/ast.c | 9 | ||||
| -rw-r--r-- | src/ast.h | 5 | ||||
| -rw-r--r-- | src/compile/expressions.c | 13 | ||||
| -rw-r--r-- | src/compile/functions.c | 11 | ||||
| -rw-r--r-- | src/formatter/formatter.c | 11 | ||||
| -rw-r--r-- | src/parse/expressions.c | 26 | ||||
| -rw-r--r-- | src/parse/expressions.h | 1 | ||||
| -rw-r--r-- | src/parse/utils.c | 6 | ||||
| -rw-r--r-- | src/typecheck.c | 5 | ||||
| -rw-r--r-- | test/serialization.tm | 2 |
12 files changed, 43 insertions, 100 deletions
@@ -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. @@ -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; } } @@ -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" |
