diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-09-21 16:28:57 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-09-21 16:28:57 -0400 |
| commit | fd74479a2bf2e4ccc35d1c2fa206de8f28be1e54 (patch) | |
| tree | 0cdf5c4659264b612b5f3fbb4ce6a3b11dd736f5 | |
| parent | 92185a002a2e8b669add454a945c0ecdc0904993 (diff) | |
Deprecate optional '?' postfix operator
| -rw-r--r-- | CHANGES.md | 4 | ||||
| -rw-r--r-- | src/ast.c | 5 | ||||
| -rw-r--r-- | src/ast.h | 3 | ||||
| -rw-r--r-- | src/compile/expressions.c | 1 | ||||
| -rw-r--r-- | src/compile/functions.c | 4 | ||||
| -rw-r--r-- | src/compile/optionals.c | 7 | ||||
| -rw-r--r-- | src/compile/optionals.h | 1 | ||||
| -rw-r--r-- | src/formatter/formatter.c | 9 | ||||
| -rw-r--r-- | src/parse/binops.c | 3 | ||||
| -rw-r--r-- | src/parse/expressions.c | 15 | ||||
| -rw-r--r-- | src/parse/suffixes.c | 7 | ||||
| -rw-r--r-- | src/parse/suffixes.h | 1 | ||||
| -rw-r--r-- | src/typecheck.c | 6 | ||||
| -rw-r--r-- | test/iterators.tm | 8 | ||||
| -rw-r--r-- | test/lists.tm | 8 | ||||
| -rw-r--r-- | test/nums.tm | 4 | ||||
| -rw-r--r-- | test/optionals.tm | 50 | ||||
| -rw-r--r-- | test/paths.tm | 2 | ||||
| -rw-r--r-- | test/reductions.tm | 6 | ||||
| -rw-r--r-- | test/serialization.tm | 2 | ||||
| -rw-r--r-- | test/tables.tm | 10 | ||||
| -rw-r--r-- | test/when.tm | 2 |
22 files changed, 58 insertions, 100 deletions
@@ -1,6 +1,6 @@ # Version History -## v0.4 +## v1.0 - Tomo libraries are now installed to `$TOMO_PATH/lib/tomo_vX.Y/module_vZ.W` instead of `$TOMO_PATH/share/tomo_vX.Y/installed/module_vZ.W` - Core libraries are no longer shipped with the compiler, they have moved to @@ -14,6 +14,8 @@ - Tables now have `and`, `or`, `xor`, and `-` (minus) metamethods. - Deprecated `extern` keyword for declaring external symbols from C. - Use `C_code` instead. +- Deprecated the postfix `?` to make values optional. + - Explicitly optional values can be declared as `my_var : T? = value`. - Added a `--format` flag to the `tomo` binary that autoformats your code (currently unstable, do not rely on it just yet). - Standardized text methods for Unicode encodings: @@ -269,7 +269,6 @@ Text_t ast_to_sexp(ast_t *ast) { T(LangDef, "(LangDef \"", data.name, "\" ", ast_to_sexp(data.namespace), ")"); T(Index, "(Index ", ast_to_sexp(data.indexed), " ", ast_to_sexp(data.index), ")"); T(FieldAccess, "(FieldAccess ", ast_to_sexp(data.fielded), " \"", data.field, "\")"); - T(Optional, "(Optional ", ast_to_sexp(data.value), ")"); T(NonOptional, "(NonOptional ", ast_to_sexp(data.value), ")"); T(DocTest, "(DocTest ", ast_to_sexp(data.expr), optional_sexp("expected", data.expected), ")"); T(Assert, "(Assert ", ast_to_sexp(data.expr), " ", optional_sexp("message", data.message), ")"); @@ -655,10 +654,6 @@ void ast_visit(ast_t *ast, void (*visitor)(ast_t *, void *), void *userdata) { ast_visit(Match(ast, FieldAccess)->fielded, visitor, userdata); return; } - case Optional: { - ast_visit(Match(ast, Optional)->value, visitor, userdata); - return; - } case NonOptional: { ast_visit(Match(ast, NonOptional)->value, visitor, userdata); return; @@ -269,7 +269,6 @@ typedef enum { LangDef, Index, FieldAccess, - Optional, NonOptional, DocTest, Assert, @@ -438,7 +437,7 @@ struct ast_s { } FieldAccess; struct { ast_t *value; - } Optional, NonOptional; + } NonOptional; struct { ast_t *expr, *expected; bool skip_source : 1; diff --git a/src/compile/expressions.c b/src/compile/expressions.c index cd244fec..f4326b00 100644 --- a/src/compile/expressions.c +++ b/src/compile/expressions.c @@ -113,7 +113,6 @@ Text_t compile(env_t *env, ast_t *ast) { } case HeapAllocate: case StackReference: return compile_typed_allocation(env, ast, get_type(env, ast)); - case Optional: return compile_optional(env, ast); case NonOptional: return compile_non_optional(env, ast); case Power: case Multiply: diff --git a/src/compile/functions.c b/src/compile/functions.c index e2fa8a11..5f6f7e91 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -552,10 +552,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, FieldAccess)->fielded); break; } - case Optional: { - add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Optional)->value); - break; - } case NonOptional: { add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, NonOptional)->value); break; diff --git a/src/compile/optionals.c b/src/compile/optionals.c index 86b4f771..d74f0f31 100644 --- a/src/compile/optionals.c +++ b/src/compile/optionals.c @@ -113,13 +113,6 @@ Text_t check_none(type_t *t, Text_t value) { } public -Text_t compile_optional(env_t *env, ast_t *ast) { - ast_t *value = Match(ast, Optional)->value; - Text_t value_code = compile(env, value); - return promote_to_optional(get_type(env, value), value_code); -} - -public Text_t compile_non_optional(env_t *env, ast_t *ast) { ast_t *value = Match(ast, NonOptional)->value; if (value->tag == Index && Match(value, Index)->index != NULL) return compile_indexing(env, value, true); diff --git a/src/compile/optionals.h b/src/compile/optionals.h index d30aaefb..28ee25b9 100644 --- a/src/compile/optionals.h +++ b/src/compile/optionals.h @@ -11,5 +11,4 @@ Text_t optional_into_nonnone(type_t *t, Text_t value); Text_t promote_to_optional(type_t *t, Text_t code); Text_t compile_none(type_t *t); Text_t check_none(type_t *t, Text_t value); -Text_t compile_optional(env_t *env, ast_t *ast); Text_t compile_non_optional(env_t *env, ast_t *ast); diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index a07b903d..69c1a213 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -269,10 +269,6 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { ast_t *val = Match(ast, StackReference)->value; return Texts("&", must(termify_inline(val, comments))); } - /*inline*/ case Optional: { - ast_t *val = Match(ast, Optional)->value; - return Texts(must(termify_inline(val, comments)), "?"); - } /*inline*/ case NonOptional: { ast_t *val = Match(ast, NonOptional)->value; return Texts(must(termify_inline(val, comments)), "!"); @@ -698,11 +694,6 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) { ast_t *val = Match(ast, StackReference)->value; return Texts("&(", termify(val, comments, indent), ")"); } - /*multiline*/ case Optional: { - if (inlined_fits) return inlined; - ast_t *val = Match(ast, Optional)->value; - return Texts(termify(val, comments, indent), "?"); - } /*multiline*/ case NonOptional: { if (inlined_fits) return inlined; ast_t *val = Match(ast, NonOptional)->value; diff --git a/src/parse/binops.c b/src/parse/binops.c index 4676b249..dd1ac3c2 100644 --- a/src/parse/binops.c +++ b/src/parse/binops.c @@ -73,8 +73,7 @@ ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightness) { progress = (false || (new_term = parse_index_suffix(ctx, key)) || (new_term = parse_method_call_suffix(ctx, key)) || (new_term = parse_field_suffix(ctx, key)) - || (new_term = parse_fncall_suffix(ctx, key)) || (new_term = parse_optional_suffix(ctx, key)) - || (new_term = parse_non_optional_suffix(ctx, key))); + || (new_term = parse_fncall_suffix(ctx, key)) || (new_term = parse_non_optional_suffix(ctx, key))); if (progress) key = new_term; } if (key && key->tag == Var) key = NULL; diff --git a/src/parse/expressions.c b/src/parse/expressions.c index 7410f678..3cb47669 100644 --- a/src/parse/expressions.c +++ b/src/parse/expressions.c @@ -54,10 +54,9 @@ ast_t *parse_reduction(parse_ctx_t *ctx, const char *pos) { ast_t *key = NewAST(ctx->file, pos, pos, Var, .name = op_str); for (bool progress = true; progress;) { ast_t *new_term; - progress = - (false || (new_term = parse_index_suffix(ctx, key)) || (new_term = parse_method_call_suffix(ctx, key)) - || (new_term = parse_field_suffix(ctx, key)) || (new_term = parse_fncall_suffix(ctx, key)) - || (new_term = parse_optional_suffix(ctx, key)) || (new_term = parse_non_optional_suffix(ctx, key))); + progress = (false || (new_term = parse_index_suffix(ctx, key)) + || (new_term = parse_method_call_suffix(ctx, key)) || (new_term = parse_field_suffix(ctx, key)) + || (new_term = parse_fncall_suffix(ctx, key)) || (new_term = parse_non_optional_suffix(ctx, key))); if (progress) key = new_term; } if (key && key->tag == Var) key = NULL; @@ -98,8 +97,7 @@ ast_t *parse_heap_alloc(parse_ctx_t *ctx, const char *pos) { ast_t *ast = NewAST(ctx->file, start, pos, HeapAllocate, .value = val); for (;;) { - ast_t *next = parse_optional_suffix(ctx, ast); - if (!next) next = parse_non_optional_suffix(ctx, ast); + ast_t *next = parse_non_optional_suffix(ctx, ast); if (!next) break; ast = next; } @@ -123,8 +121,7 @@ ast_t *parse_stack_reference(parse_ctx_t *ctx, const char *pos) { ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value = val); for (;;) { - ast_t *next = parse_optional_suffix(ctx, ast); - if (!next) next = parse_non_optional_suffix(ctx, ast); + ast_t *next = parse_non_optional_suffix(ctx, ast); if (!next) break; ast = next; } @@ -212,7 +209,7 @@ ast_t *parse_term(parse_ctx_t *ctx, const char *pos) { progress = (false || (new_term = parse_index_suffix(ctx, term)) || (new_term = parse_method_call_suffix(ctx, term)) || (new_term = parse_field_suffix(ctx, term)) || (new_term = parse_fncall_suffix(ctx, term)) - || (new_term = parse_optional_suffix(ctx, term)) || (new_term = parse_non_optional_suffix(ctx, term))); + || (new_term = parse_non_optional_suffix(ctx, term))); if (progress) term = new_term; } return term; diff --git a/src/parse/suffixes.c b/src/parse/suffixes.c index 312f958f..7493a65d 100644 --- a/src/parse/suffixes.c +++ b/src/parse/suffixes.c @@ -25,13 +25,6 @@ ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded = lhs, .field = field); } -ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { - if (!lhs) return NULL; - const char *pos = lhs->end; - if (match(&pos, "?")) return NewAST(ctx->file, lhs->start, pos, Optional, .value = lhs); - else return NULL; -} - ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (!lhs) return NULL; const char *pos = lhs->end; diff --git a/src/parse/suffixes.h b/src/parse/suffixes.h index da2f23a2..23ca7182 100644 --- a/src/parse/suffixes.h +++ b/src/parse/suffixes.h @@ -11,4 +11,3 @@ ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs); ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self); ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt); -ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); diff --git a/src/typecheck.c b/src/typecheck.c index 64bf0fd8..b4798768 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -798,12 +798,6 @@ type_t *get_type(env_t *env, ast_t *ast) { default: return Type(PointerType, .pointed = get_type(env, value), .is_stack = true); } } - case Optional: { - ast_t *value = Match(ast, Optional)->value; - type_t *t = get_type(env, value); - if (t->tag == OptionalType) code_err(ast, "This value is already optional, it can't be converted to optional"); - return Type(OptionalType, .type = t); - } case NonOptional: { ast_t *value = Match(ast, NonOptional)->value; type_t *t = get_type(env, value); diff --git a/test/iterators.tm b/test/iterators.tm index c48e572b..ed08f73d 100644 --- a/test/iterators.tm +++ b/test/iterators.tm @@ -3,17 +3,17 @@ struct Pair(x:Text, y:Text) func pairwise(strs:[Text] -> func(->Pair?)) i := 1 - return func() + return func(-> Pair?) i += 1 - return Pair(strs[i-1] or return none, strs[i] or return none)? + return Pair(strs[i-1] or return none, strs[i] or return none) func range(first:Int, last:Int -> func(->Int?)) i := first - return func() + return func(->Int?) if i > last return none i += 1 - return (i-1)? + return (i-1) func main() values := ["A", "B", "C", "D"] diff --git a/test/lists.tm b/test/lists.tm index 05001f19..2342c570 100644 --- a/test/lists.tm +++ b/test/lists.tm @@ -162,23 +162,23 @@ func main() = [1, 2, 3, 4, 5] >> ["a", "b", "c"].find("b") - = 2? + = 2 >> ["a", "b", "c"].find("XXX") = none >> [10, 20].where(func(i:&Int) i.is_prime()) = none >> [4, 5, 6].where(func(i:&Int) i.is_prime()) - = 2? + = 2 do >> nums := &[10, 20, 30, 40, 50] >> nums.pop() - = 50? + = 50 >> nums[] = [10, 20, 30, 40] >> nums.pop(2) - = 20? + = 20 >> nums[] = [10, 30, 40] >> nums.clear() diff --git a/test/nums.tm b/test/nums.tm index 362f590f..1ca09d7b 100644 --- a/test/nums.tm +++ b/test/nums.tm @@ -51,7 +51,9 @@ func main() = no >> Num32.sqrt(16) - = Num32(4)? + = Num32(4) + >> Num32.sqrt(-1) + = none >> (0.25).mix(10, 20) = 12.5 diff --git a/test/optionals.tm b/test/optionals.tm index 1f7d640e..dcec904d 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -57,28 +57,28 @@ func maybe_lambda(should_i:Bool-> func()?) func maybe_c_string(should_i:Bool->CString?) if should_i - return ("hi".as_c_string())? + return "hi".as_c_string() else return none func main() - >> 5? - = 5? + >> optional : Int? = 5 + = 5 >> if no x : Int? = none x else 5 - = 5? + = 5 - >> 5? or -1 + >> optional or -1 = 5 - >> 5? or fail("Non-none is falsey") + >> optional or fail("Non-none is falsey") = 5 - >> 5? or exit("Non-none is falsey") + >> optional or exit("Non-none is falsey") = 5 >> none_int : Int? = none @@ -88,7 +88,7 @@ func main() do say("Ints:") >> yep := maybe_int(yes) - = 123? + = 123 >> nope := maybe_int(no) = none >> if yep @@ -102,7 +102,7 @@ func main() do say("Int64s:") >> yep := maybe_int64(yes) - = Int64(123)? + = Int64(123) >> nope := maybe_int64(no) = none >> if yep @@ -116,7 +116,7 @@ func main() do say("Lists:") >> yep := maybe_list(yes) - = [10, 20, 30]? + = [10, 20, 30] >> nope := maybe_list(no) = none >> if yep @@ -131,7 +131,7 @@ func main() say("...") say("Bools:") >> yep := maybe_bool(yes) - = no? + = no >> nope := maybe_bool(no) = none >> if yep @@ -146,7 +146,7 @@ func main() say("...") say("Text:") >> yep := maybe_text(yes) - = "Hello"? + = "Hello" >> nope := maybe_text(no) = none >> if yep @@ -161,7 +161,7 @@ func main() say("...") say("Nums:") >> yep := maybe_num(yes) - = 12.3? + = 12.3 >> nope := maybe_num(no) = none >> if yep @@ -191,7 +191,7 @@ func main() say("...") say("Structs:") >> yep := Struct.maybe(yes) - = Struct(x=123, y="hello")? + = Struct(x=123, y="hello") >> nope := Struct.maybe(no) = none >> if yep @@ -206,7 +206,7 @@ func main() say("...") say("Enums:") >> yep := Enum.maybe(yes) - = Enum.Y(123)? + = Enum.Y(123) >> nope := Enum.maybe(no) = none >> if yep @@ -221,7 +221,7 @@ func main() say("...") say("C Strings:") >> yep := maybe_c_string(yes) - = CString("hi")? + = CString("hi") >> nope := maybe_c_string(no) = none >> if yep @@ -241,16 +241,16 @@ func main() = 123 # Test comparisons, hashing, equality: - assert none != 5? - assert 5? == 5? + assert none != optional + assert optional == 5 >> nones : {Int?=Bool} = {none=yes, none=yes} >> nones.keys = [none] - >> [5?, none, none, 6?].sorted() + >> [5, none, none, 6].sorted() = [none, none, 5, 6] do - >> value := if var := 5? + >> value := if var := optional var else 0 @@ -264,7 +264,7 @@ func main() = 0 do - >> opt := 5? + >> opt : Int? = 5 >> if opt >> opt else @@ -277,17 +277,17 @@ func main() else >> opt - >> not 5? + >> not optional = no >> nah : Int? = none >> not nah = yes - >> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")] - = [Struct(x=5, y="A")?, Struct(x=6, y="B")?, Struct(x=7, y="C")?] + >> [none, Struct(5,"A"), Struct(6,"B"), Struct(7,"C")] + = [none, Struct(x=5, y="A"), Struct(x=6, y="B"), Struct(x=7, y="C")] - if 5? or no + if optional or no say("Binary op 'or' works with optionals") else fail("Failed to do binary op 'or' on optional") diff --git a/test/paths.tm b/test/paths.tm index d92d4623..22f79c7b 100644 --- a/test/paths.tm +++ b/test/paths.tm @@ -20,7 +20,7 @@ func main() >> tmpfile.write("Hello world") >> tmpfile.append("!") >> tmpfile.read() - = "Hello world!"? + = "Hello world!" >> tmpfile.read_bytes()! = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21] assert tmpdir.files().has(tmpfile) diff --git a/test/reductions.tm b/test/reductions.tm index 3ec2ef5e..15c3d454 100644 --- a/test/reductions.tm +++ b/test/reductions.tm @@ -2,7 +2,7 @@ struct Foo(x,y:Int) func main() >> (+: [10, 20, 30]) - = 60? + = 60 >> empty_ints : [Int] >> (+: empty_ints) @@ -15,10 +15,10 @@ func main() = 0 >> (_max_: [3, 5, 2, 1, 4]) - = 5? + = 5 >> (_max_.abs(): [1, -10, 5]) - = -10? + = -10 >> (_max_: [Foo(0, 0), Foo(1, 0), Foo(0, 10)])! = Foo(x=1, y=0) diff --git a/test/serialization.tm b/test/serialization.tm index e2fa38a3..0e5be1ae 100644 --- a/test/serialization.tm +++ b/test/serialization.tm @@ -62,7 +62,7 @@ func main() assert deserialize(bytes -> MyEnum) == obj do - >> obj := "Hello"? + >> obj : Text? = "Hello" >> bytes := obj.serialized() assert deserialize(bytes -> Text?) == obj diff --git a/test/tables.tm b/test/tables.tm index 587a02c8..66318f11 100644 --- a/test/tables.tm +++ b/test/tables.tm @@ -3,9 +3,9 @@ func main() = {"one"=1, "two"=2} >> t["one"] - = 1? + = 1 >> t["two"] - = 2? + = 2 >> t["???"] = none >> t["one"]! @@ -33,16 +33,16 @@ func main() = {"three"=3; fallback={"one"=1, "two"=2}} >> t2["one"] - = 1? + = 1 >> t2["three"] - = 3? + = 3 >> t2["???"] = none >> t2.length = 1 >> t2.fallback - = {"one"=1, "two"=2}? + = {"one"=1, "two"=2} t2_str := "" for k,v in t2 diff --git a/test/when.tm b/test/when.tm index d18f5276..57de4c2c 100644 --- a/test/when.tm +++ b/test/when.tm @@ -15,4 +15,4 @@ func main() >> when n is 1 Int64(1) is 2 Int64(2) is 21 + 2 Int64(23) - = Int64(23)? + = Int64(23) |
