diff --git a/docs/arrays.md b/docs/arrays.md index 437ab76..8ea51f0 100644 --- a/docs/arrays.md +++ b/docs/arrays.md @@ -246,13 +246,13 @@ variable or dereference a heap pointer, it may trigger copy-on-write behavior. - [`func insert(arr: @[T], item: T, at: Int = 0 -> Void)`](#insert) - [`func insert_all(arr: @[T], items: [T], at: Int = 0 -> Void)`](#insert_all) - [`func pop(arr: &[T], index: Int = -1 -> T?)`](#pop) -- [`func random(arr: [T], random: func(min,max:Int64->Int64)? = none:func(min,max:Int64->Int64) -> T)`](#random) +- [`func random(arr: [T], random: func(min,max:Int64->Int64)? = none -> T)`](#random) - [`func remove_at(arr: @[T], at: Int = -1, count: Int = 1 -> Void)`](#remove_at) - [`func remove_item(arr: @[T], item: T, max_count: Int = -1 -> Void)`](#remove_item) - [`func reversed(arr: [T] -> [T])`](#reversed) -- [`func sample(arr: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none:func(->Num) -> [T])`](#sample) -- [`func shuffle(arr: @[T], random: func(min,max:Int64->Int64)? = none:func(min,max:Int64->Int64) -> Void)`](#shuffle) -- [`func shuffled(arr: [T], random: func(min,max:Int64->Int64)? = none:func(min,max:Int64->Int64) -> [T])`](#shuffled) +- [`func sample(arr: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none -> [T])`](#sample) +- [`func shuffle(arr: @[T], random: func(min,max:Int64->Int64)? = none -> Void)`](#shuffle) +- [`func shuffled(arr: [T], random: func(min,max:Int64->Int64)? = none -> [T])`](#shuffled) - [`func slice(arr: [T], from: Int, to: Int -> [T])`](#slice) - [`func sort(arr: @[T], by=T.compare -> Void)`](#sort) - [`sorted(arr: [T], by=T.compare -> [T])`](#sorted) @@ -607,7 +607,7 @@ otherwise the item at the given index. Selects a random element from the array. ```tomo -func random(arr: [T], random: func(min,max:Int64->Int64)? = none:func(min,max:Int64->Int64) -> T) +func random(arr: [T], random: func(min,max:Int64->Int64)? = none -> T) ``` - `arr`: The array from which to select a random element. @@ -707,7 +707,7 @@ Selects a sample of elements from the array, optionally with weighted probabilities. ```tomo -func sample(arr: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none:func(->Num) -> [T]) +func sample(arr: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none -> [T]) ``` - `arr`: The array to sample from. @@ -744,7 +744,7 @@ A list of sampled elements from the array. Shuffles the elements of the array in place. ```tomo -func shuffle(arr: @[T], random: func(min,max:Int64->Int64)? = none:func(min,max:Int64->Int64) -> Void) +func shuffle(arr: @[T], random: func(min,max:Int64->Int64)? = none -> Void) ``` - `arr`: The mutable reference to the array to be shuffled. @@ -766,7 +766,7 @@ Nothing. Creates a new array with elements shuffled. ```tomo -func shuffled(arr: [T], random: func(min,max:Int64->Int64)? = none:func(min,max:Int64->Int64) -> [T]) +func shuffled(arr: [T], random: func(min,max:Int64->Int64)? = none -> [T]) ``` - `arr`: The array to be shuffled. diff --git a/docs/integers.md b/docs/integers.md index 6e45f1b..5681388 100644 --- a/docs/integers.md +++ b/docs/integers.md @@ -135,7 +135,7 @@ can be called either on the type itself: `Int.sqrt(x)` or as a method call: - [`func parse(text: Text -> Int?)`](#parse) - [`func prev_prime(x: Int -> Int)`](#prev_prime) - [`func sqrt(x: Int -> Int)`](#sqrt) -- [`func to(first: Int, last: Int, step : Int? = none:Int -> func(->Int?))`](#to) +- [`func to(first: Int, last: Int, step : Int? = none -> func(->Int?))`](#to) ### `abs` Calculates the absolute value of an integer. @@ -458,7 +458,7 @@ Returns an iterator function that iterates over the range of numbers specified. Iteration is assumed to be nonempty and ```tomo -func to(first: Int, last: Int, step : Int? = none:Int -> func(->Int?)) +func to(first: Int, last: Int, step : Int? = none -> func(->Int?)) ``` - `first`: The starting value of the range. diff --git a/docs/optionals.md b/docs/optionals.md index 84f886b..ff4252d 100644 --- a/docs/optionals.md +++ b/docs/optionals.md @@ -40,7 +40,7 @@ example, if you wanted to declare a variable that could be either an integer value or `none` and initialize it as none, you would write it as: ```tomo -x := none:Int +x : Int = none ``` Similarly, if you wanted to declare a variable that could be an array of texts @@ -57,7 +57,7 @@ keep open the possibility of assigning `none` later, you can use the postfix ```tomo x := 5? # Later on, assign none: -x = !Int +x = none ``` ## Type Inference @@ -86,7 +86,7 @@ Non-none values can also be automatically promoted to optional values without the need for an explicit `?` operator in the cases listed above: ```tomo -x := !Int +x : Int? = none x = 5 func doop(arg:Int?)->Text?: @@ -109,7 +109,7 @@ maybe_x := 5? >> maybe_x or fail("No value!") = 5 : Int -maybe_x = !Int +maybe_x = none >> maybe_x or -1 = -1 : Int >> maybe_x or fail("No value!") diff --git a/docs/paths.md b/docs/paths.md index 234ad44..2bfc9b0 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -68,7 +68,7 @@ intended. Paths can be created from text with slashes using - [`func relative_to(path: Path, relative_to=(./) -> Path)`](#relative_to) - [`func remove(path: Path, ignore_missing=no -> Void)`](#remove) - [`func resolved(path: Path, relative_to=(./) -> Path)`](#resolved) -- [`func set_owner(path:Path, owner=none:Text, group=none:Text, follow_symlinks=yes)`](#set_owner) +- [`func set_owner(path:Path, owner:Text?=none, group:Text?=none, follow_symlinks=yes)`](#set_owner) - [`func subdirectories(path: Path, include_hidden=no -> [Path])`](#subdirectories) - [`func unique_directory(path: Path -> Path)`](#unique_directory) - [`func write(path: Path, text: Text, permissions=0o644[32] -> Void)`](#write) @@ -95,7 +95,7 @@ accessed, or `none` if no such file or directory exists. >> (./file.txt):accessed() = 1704221100? >> (./not-a-file):accessed() -= none:Int64? += none ``` --- @@ -289,7 +289,7 @@ changed, or `none` if no such file or directory exists. >> (./file.txt):changed() = 1704221100? >> (./not-a-file):changed() -= none:Int64 += none ``` --- @@ -535,7 +535,7 @@ The name of the group which owns the file or directory, or `none` if the path do >> (/bin):group() = "root" >> (/non/existent/file):group() -= none:Text += none ``` --- @@ -648,7 +648,7 @@ modified, or `none` if no such file or directory exists. >> (./file.txt):modified() = 1704221100? >> (./not-a-file):modified() -= none:Int64 += none ``` --- @@ -671,7 +671,7 @@ The name of the user who owns the file or directory, or `none` if the path does >> (/bin):owner() = "root" >> (/non/existent/file):owner() -= none:Text += none ``` --- @@ -717,7 +717,7 @@ raised. = "Hello"? >> (./nosuchfile.xxx):read() -= none:Text += none ``` --- @@ -741,7 +741,7 @@ returned. = [72[B], 101[B], 108[B], 108[B], 111[B]]? >> (./nosuchfile.xxx):read() -= none:[Byte] += none ``` --- @@ -815,7 +815,7 @@ The resolved absolute path. Set the owning user and/or group for a path. ```tomo -func set_owner(path:Path, owner: Text? = none:Text, group: Text? = none:Text, follow_symlinks: Bool = yes) +func set_owner(path:Path, owner: Text? = none, group: Text? = none, follow_symlinks: Bool = yes) ``` - `path`: The path to change the permissions for. diff --git a/docs/serialization.md b/docs/serialization.md index 0f158be..a1a38fd 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -52,7 +52,7 @@ cyclic datastructures correctly, enabling you to serialize cyclic structures like circularly linked lists or graphs: ```tomo -struct Cycle(name:Text, next=none:@Cycle) +struct Cycle(name:Text, next:@Cycle?=none) c := @Cycle("A") c.next = @Cycle("B", next=c) diff --git a/docs/tables.md b/docs/tables.md index 83c80b2..93ee3eb 100644 --- a/docs/tables.md +++ b/docs/tables.md @@ -43,7 +43,7 @@ table := {"A"=1, "B"=2} >> table["A"] = 1? >> table["missing"] -= none:Int += none ``` As with all optional values, you can use the `!` postfix operator to assert @@ -77,7 +77,7 @@ table value: >> t2.fallback = {"A"=10}? >> t.fallback -= none:{Text=Int} += none ``` ### Default Values @@ -225,7 +225,7 @@ The value associated with the key or `none` if the key is not found. = 1? >> t:get("????") -= none:Int += none >> t:get("A")! = 1 diff --git a/examples/commands/commands.tm b/examples/commands/commands.tm index ddd04d7..170e45b 100644 --- a/examples/commands/commands.tm +++ b/examples/commands/commands.tm @@ -11,12 +11,12 @@ enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed): when e is Exited(status): return (status == 0) else: return no - func or_fail(e:ExitType, message=none:Text): + func or_fail(e:ExitType, message:Text?=none): if not e:succeeded(): fail(message or "Program failed: $e") struct ProgramResult(stdout:[Byte], stderr:[Byte], exit_type:ExitType): - func or_fail(r:ProgramResult, message=none:Text -> ProgramResult): + func or_fail(r:ProgramResult, message:Text?=none -> ProgramResult): when r.exit_type is Exited(status): if status == 0: return r diff --git a/examples/http-server/connection-queue.tm b/examples/http-server/connection-queue.tm index 362dab7..84f9236 100644 --- a/examples/http-server/connection-queue.tm +++ b/examples/http-server/connection-queue.tm @@ -12,7 +12,7 @@ struct ConnectionQueue(_connections:@[Int32]=@[], _mutex=pthread_mutex_t.new(), func dequeue(queue:ConnectionQueue -> Int32): - conn := none:Int32 + conn : Int32? = none queue._mutex:lock() diff --git a/examples/http/http.tm b/examples/http/http.tm index ee1b8c7..ce74d01 100644 --- a/examples/http/http.tm +++ b/examples/http/http.tm @@ -93,7 +93,7 @@ func put(url:Text, data="", headers=["Content-Type: application/json", "Accept: func patch(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): return _send(PATCH, url, data, headers) -func delete(url:Text, data=none:Text, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): +func delete(url:Text, data:Text?=none, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): return _send(DELETE, url, data, headers) func main(): diff --git a/examples/patterns/README.md b/examples/patterns/README.md index 2fb1739..9e9d860 100644 --- a/examples/patterns/README.md +++ b/examples/patterns/README.md @@ -332,7 +332,7 @@ not match the pattern. >> "123 boxes":pattern_captures($Pat"{int} {id}") = ["123", "boxes"]? >> "xxx":pattern_captures($Pat"{int} {id}") -= none:[Text] += none ``` --- diff --git a/examples/random/random.tm b/examples/random/random.tm index 0a0167a..95d2f9f 100644 --- a/examples/random/random.tm +++ b/examples/random/random.tm @@ -25,7 +25,7 @@ func _os_random_bytes(count:Int64 -> [Byte]): } struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret): - func new(seed=none:[Byte], -> @RandomNumberGenerator): + func new(seed:[Byte]?=none, -> @RandomNumberGenerator): ctx := chacha_ctx.from_seed(seed or _os_random_bytes(40)) return @RandomNumberGenerator(ctx, []) diff --git a/examples/tomo-install/tomo-install.tm b/examples/tomo-install/tomo-install.tm index c705af1..0be0561 100644 --- a/examples/tomo-install/tomo-install.tm +++ b/examples/tomo-install/tomo-install.tm @@ -40,7 +40,7 @@ func main(paths:[Path]): say("Already installed: $url") skip - alias := none:Text + alias : Text? = none curl_flags := ["-L"] if github := url_without_protocol:pattern_captures($Pat"github.com/{!/}/{!/}#{..}"): user := github[1] diff --git a/examples/vectors/vectors.tm b/examples/vectors/vectors.tm index 3f07e50..f978d3d 100644 --- a/examples/vectors/vectors.tm +++ b/examples/vectors/vectors.tm @@ -19,7 +19,7 @@ struct Vec2(x,y:Num): func divided_by(v:Vec2, divisor:Num->Vec2; inline): return Vec2(v.x/divisor, v.y/divisor) func length(v:Vec2->Num; inline): - return (v.x*v.x + v.y*v.y)!:sqrt() + return (v.x*v.x + v.y*v.y):sqrt() func dist(a,b:Vec2->Num; inline): return a:minus(b):length() func angle(v:Vec2->Num; inline): @@ -58,7 +58,7 @@ struct Vec3(x,y,z:Num): func divided_by(v:Vec3, divisor:Num->Vec3; inline): return Vec3(v.x/divisor, v.y/divisor, v.z/divisor) func length(v:Vec3->Num; inline): - return (v.x*v.x + v.y*v.y + v.z*v.z)!:sqrt() + return (v.x*v.x + v.y*v.y + v.z*v.z):sqrt() func dist(a,b:Vec3->Num; inline): return a:minus(b):length() func norm(v:Vec3->Vec3; inline): diff --git a/src/ast.c b/src/ast.c index 67b54f9..8544eba 100644 --- a/src/ast.c +++ b/src/ast.c @@ -134,12 +134,13 @@ CORD ast_to_xml(ast_t *ast) T(Path, "%s", data.path) T(Declare, "%r%r", ast_to_xml(data.var), type_ast_to_xml(data.type), ast_to_xml(data.value)) T(Assign, "%r%r", ast_list_to_xml(data.targets), ast_list_to_xml(data.values)) -#define BINOP(name) T(name, "<" #name ">%r %r", data.lhs, data.rhs) +#define BINOP(name) T(name, "<" #name ">%r %r", ast_to_xml(data.lhs), ast_to_xml(data.rhs)) BINOP(Power) BINOP(PowerUpdate) BINOP(Multiply) BINOP(MultiplyUpdate) BINOP(Divide) BINOP(DivideUpdate) BINOP(Mod) BINOP(ModUpdate) BINOP(Mod1) BINOP(Mod1Update) BINOP(Plus) BINOP(PlusUpdate) BINOP(Minus) BINOP(MinusUpdate) BINOP(Concat) BINOP(ConcatUpdate) BINOP(LeftShift) BINOP(LeftShiftUpdate) BINOP(RightShift) BINOP(RightShiftUpdate) BINOP(UnsignedLeftShift) BINOP(UnsignedLeftShiftUpdate) BINOP(UnsignedRightShift) BINOP(UnsignedRightShiftUpdate) BINOP(And) BINOP(AndUpdate) BINOP(Or) BINOP(OrUpdate) - BINOP(Xor) BINOP(XorUpdate) + BINOP(Xor) BINOP(XorUpdate) BINOP(Compare) + BINOP(Equals) BINOP(NotEquals) BINOP(LessThan) BINOP(LessThanOrEquals) BINOP(GreaterThan) BINOP(GreaterThanOrEquals) #undef BINOP T(Negative, "%r", ast_to_xml(data.value)) T(Not, "%r", ast_to_xml(data.value)) diff --git a/src/compile.c b/src/compile.c index d893c67..d618de7 100644 --- a/src/compile.c +++ b/src/compile.c @@ -552,7 +552,7 @@ static CORD compile_update_assignment(env_t *env, ast_t *ast) binop->tag = binop_tag(binop->tag); if (needs_idemotency_fix) binop->__data.Plus.lhs = WrapAST(update.lhs, InlineCCode, .code="*lhs", .type=lhs_t); - update_assignment = CORD_all(lhs, " = ", compile_to_type(env, binop, lhs_t)); + update_assignment = CORD_all(lhs, " = ", compile_to_type(env, binop, lhs_t), ";"); } if (needs_idemotency_fix) @@ -1154,7 +1154,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) } ast_t *update_var = new(ast_t); - *update_var = *ast; + *update_var = *test->expr; update_var->__data.PlusUpdate.lhs = WrapAST(update.lhs, InlineCCode, .code="(*expr)", .type=lhs_t); // UNSAFE test_code = CORD_all("({", compile_declaration(Type(PointerType, lhs_t), "expr"), " = &(", compile_lvalue(env, update.lhs), "); ", @@ -1855,6 +1855,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) } } default: + // print("Is discardable: ", ast_to_xml_str(ast), " ==> ", is_discardable(env, ast)); if (!is_discardable(env, ast)) code_err(ast, "The ", type_to_str(get_type(env, ast)), " result of this statement cannot be discarded"); return CORD_asprintf("(void)%r;", compile(env, ast)); @@ -1952,6 +1953,10 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) default: code_err(ast, "This is not a valid number bit width"); } } else if (ast->tag == None) { + if (t->tag != OptionalType) + code_err(ast, "This is not supposed to be an optional type"); + else if (Match(t, OptionalType)->type == NULL) + code_err(ast, "I don't know what kind of `none` this is supposed to be!\nPlease tell me by declaring a variable like `foo : Type = none`"); return compile_none(t); } else if (t->tag == PointerType && (ast->tag == HeapAllocate || ast->tag == StackReference)) { return compile_typed_allocation(env, ast, t); @@ -2377,9 +2382,15 @@ static bool string_literal_is_all_ascii(CORD literal) CORD compile_none(type_t *t) { + if (t == NULL) + compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); + if (t->tag == OptionalType) t = Match(t, OptionalType)->type; + if (t == NULL) + compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); + if (t == PATH_TYPE) return "NONE_PATH"; else if (t == PATH_TYPE_TYPE) return "((OptionalPathType_t){})"; @@ -2435,7 +2446,7 @@ CORD compile(env_t *env, ast_t *ast) { switch (ast->tag) { case None: { - code_err(ast, "This 'none' needs to specify what type it is using `none:Type` syntax"); + code_err(ast, "I can't figure out what this `none`'s type is!"); } case Bool: return Match(ast, Bool)->b ? "yes" : "no"; case Var: { @@ -2535,30 +2546,43 @@ CORD compile(env_t *env, ast_t *ast) type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); type_t *operand_t; - CORD lhs, rhs; - if (can_compile_to_type(env, binop.rhs, lhs_t)) { - lhs = compile(env, binop.lhs); - rhs = compile_to_type(env, binop.rhs, lhs_t); + if (is_numeric_type(lhs_t) && binop.rhs->tag == Int) { operand_t = lhs_t; - } else if (can_compile_to_type(env, binop.lhs, rhs_t)) { - rhs = compile(env, binop.rhs); - lhs = compile_to_type(env, binop.lhs, rhs_t); + } else if (is_numeric_type(rhs_t) && binop.lhs->tag == Int) { operand_t = rhs_t; } else { - code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + switch (compare_precision(lhs_t, rhs_t)) { + case NUM_PRECISION_LESS: operand_t = rhs_t; break; + case NUM_PRECISION_MORE: operand_t = lhs_t; break; + case NUM_PRECISION_EQUAL: operand_t = lhs_t; break; + default: { + if (can_compile_to_type(env, binop.rhs, lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, binop.lhs, rhs_t)) { + operand_t = rhs_t; + } else { + code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + } + break; + } + } } + CORD lhs, rhs; + lhs = compile_to_type(env, binop.lhs, operand_t); + rhs = compile_to_type(env, binop.rhs, operand_t); + switch (operand_t->tag) { case BigIntType: return CORD_all(ast->tag == Equals ? CORD_EMPTY : "!", "Int$equal_value(", lhs, ", ", rhs, ")"); case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType: return CORD_all("(", lhs, ast->tag == Equals ? " == " : " != ", rhs, ")"); default: - return CORD_asprintf(ast->tag == Equals ? CORD_EMPTY : "!", - "generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(operand_t)); + return CORD_all(ast->tag == Equals ? CORD_EMPTY : "!", + "generic_equal(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ")"); } } - case LessThan: case LessThanOrEquals: case GreaterThan: case GreaterThanOrEquals: { + case LessThan: case LessThanOrEquals: case GreaterThan: case GreaterThanOrEquals: case Compare: { binary_operands_t cmp = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, cmp.lhs); @@ -2577,6 +2601,10 @@ CORD compile(env_t *env, ast_t *ast) code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); } + if (ast->tag == Compare) + return CORD_all("generic_compare(stack(", lhs, "), stack(", rhs, "), ", + compile_type_info(operand_t), ")"); + const char *op = binop_operator(ast->tag); switch (operand_t->tag) { case BigIntType: @@ -2584,7 +2612,8 @@ CORD compile(env_t *env, ast_t *ast) case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType: return CORD_all("(", lhs, " ", op, " ", rhs, ")"); default: - return CORD_all("(generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(Type(OptionalType, operand_t)), ") ", op, " 0)"); + return CORD_all("(generic_compare(stack(", lhs, "), stack(", rhs, "), ", + compile_type_info(operand_t), ") ", op, " 0)"); } } case TextLiteral: { @@ -2896,31 +2925,27 @@ CORD compile(env_t *env, ast_t *ast) compile_type_info(self_value_t), ")"); } else if (streq(call->name, "sample")) { type_t *random_num_type = parse_type_string(env, "func(->Num)?"); - ast_t *none_rng = parse_expression("none:func(->Num)"); self = compile_to_pointer_depth(env, call->self, 0, false); arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE, - .next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType)), + .next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType, .bits=TYPE_NBITS64)), .default_val=FakeAST(None), - .next=new(arg_t, .name="random", .type=random_num_type, .default_val=none_rng))); + .next=new(arg_t, .name="random", .type=random_num_type, .default_val=FakeAST(None)))); return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "shuffle")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); - ast_t *none_rng = parse_expression("none:func(min,max:Int64->Int64)"); EXPECT_POINTER("an", "array"); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=none_rng); + arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); return CORD_all("Array$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "shuffled")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); - ast_t *none_rng = parse_expression("none:func(min,max:Int64->Int64)"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=none_rng); + arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); return CORD_all("Array$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "random")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); - ast_t *none_rng = parse_expression("none:func(min,max:Int64->Int64)"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=none_rng); + arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); return CORD_all("Array$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")"); } else if (streq(call->name, "sort") || streq(call->name, "sorted")) { if (streq(call->name, "sort")) @@ -3290,10 +3315,16 @@ CORD compile(env_t *env, ast_t *ast) CORD condition_code; if (condition->tag == Declare) { - type_t *condition_type = get_type(env, Match(condition, Declare)->value); + auto decl = Match(condition, Declare); + type_t *condition_type = + decl->type ? parse_type_ast(env, decl->type) + : get_type(env, Match(condition, Declare)->value); if (condition_type->tag != OptionalType) code_err(condition, "This `if var := ...:` declaration should be an optional type, not ", type_to_str(condition_type)); + if (is_incomplete_type(condition_type)) + code_err(condition, "This type is incomplete!"); + decl_code = compile_statement(env, condition); ast_t *var = Match(condition, Declare)->var; truthy_scope = fresh_scope(env); @@ -3632,7 +3663,7 @@ CORD compile(env_t *env, ast_t *ast) case Declare: case Assign: case UPDATE_CASES: case For: case While: case Repeat: case StructDef: case LangDef: case Extend: case EnumDef: case FunctionDef: case ConvertDef: case Skip: case Stop: case Pass: case Return: case DocTest: case PrintStatement: code_err(ast, "This is not a valid expression"); - default: case Unknown: code_err(ast, "Unknown AST"); + default: case Unknown: code_err(ast, "Unknown AST: ", ast_to_xml_str(ast)); } } diff --git a/src/parse.c b/src/parse.c index 0aa2600..9d4d35e 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1685,19 +1685,19 @@ PARSER(parse_update) { if (!lhs) return NULL; spaces(&pos); ast_e op; - if (match(&pos, "+=")) op = Plus; - else if (match(&pos, "++=")) op = Concat; - else if (match(&pos, "-=")) op = Minus; - else if (match(&pos, "*=")) op = Multiply; - else if (match(&pos, "/=")) op = Divide; - else if (match(&pos, "^=")) op = Power; - else if (match(&pos, "<<=")) op = LeftShift; - else if (match(&pos, "<<<=")) op = UnsignedLeftShift; - else if (match(&pos, ">>=")) op = RightShift; - else if (match(&pos, ">>>=")) op = UnsignedRightShift; - else if (match(&pos, "and=")) op = And; - else if (match(&pos, "or=")) op = Or; - else if (match(&pos, "xor=")) op = Xor; + if (match(&pos, "+=")) op = PlusUpdate; + else if (match(&pos, "++=")) op = ConcatUpdate; + else if (match(&pos, "-=")) op = MinusUpdate; + else if (match(&pos, "*=")) op = MultiplyUpdate; + else if (match(&pos, "/=")) op = DivideUpdate; + else if (match(&pos, "^=")) op = PowerUpdate; + else if (match(&pos, "<<=")) op = LeftShiftUpdate; + else if (match(&pos, "<<<=")) op = UnsignedLeftShiftUpdate; + else if (match(&pos, ">>=")) op = RightShiftUpdate; + else if (match(&pos, ">>>=")) op = UnsignedRightShiftUpdate; + else if (match(&pos, "and=")) op = AndUpdate; + else if (match(&pos, "or=")) op = OrUpdate; + else if (match(&pos, "xor=")) op = XorUpdate; else return NULL; ast_t *rhs = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here"); return new(ast_t, .file=ctx->file, .start=start, .end=pos, .tag=op, .__data.PlusUpdate.lhs=lhs, .__data.PlusUpdate.rhs=rhs); diff --git a/src/typecheck.c b/src/typecheck.c index cd6ff1c..8d4cc94 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -1163,6 +1163,26 @@ type_t *get_type(env_t *env, ast_t *ast) code_err(binop.rhs, "I only know how to do bit shifting by integer amounts, not ", type_to_str(rhs_t)); } + if (is_numeric_type(lhs_t) && binop.rhs->tag == Int) { + return lhs_t; + } else if (is_numeric_type(rhs_t) && binop.lhs->tag == Int) { + return rhs_t; + } else { + switch (compare_precision(lhs_t, rhs_t)) { + case NUM_PRECISION_LESS: return rhs_t; + case NUM_PRECISION_MORE: return lhs_t; + case NUM_PRECISION_EQUAL: return lhs_t; + default: { + if (can_compile_to_type(env, binop.rhs, lhs_t)) { + return lhs_t; + } else if (can_compile_to_type(env, binop.lhs, rhs_t)) { + return rhs_t; + } + break; + } + } + } + type_t *overall_t = (can_promote(rhs_t, lhs_t) ? lhs_t : (can_promote(lhs_t, rhs_t) ? rhs_t : NULL)); if (ast->tag == Multiply || ast->tag == Divide) { binding_t *b = is_numeric_type(lhs_t) ? get_metamethod_binding(env, ast->tag, binop.lhs, binop.rhs, lhs_t) @@ -1588,9 +1608,11 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) { - if (needed->tag == OptionalType && ast->tag == None) { + if (is_incomplete_type(needed)) + return false; + + if (needed->tag == OptionalType && ast->tag == None) return true; - } needed = non_optional(needed); if (needed->tag == ArrayType && ast->tag == Array) { diff --git a/src/types.c b/src/types.c index 0b9bc72..93326b1 100644 --- a/src/types.c +++ b/src/types.c @@ -111,6 +111,8 @@ PUREFUNC const char *get_type_name(type_t *t) bool type_eq(type_t *a, type_t *b) { if (a == b) return true; + if (!a && !b) return true; + if (!a || !b) return false; if (a->tag != b->tag) return false; return (CORD_cmp(type_to_cord(a), type_to_cord(b)) == 0); } @@ -208,10 +210,8 @@ static PUREFUNC INLINE double type_max_magnitude(type_t *t) PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b) { - if (a->tag == OptionalType && Match(a, OptionalType)->type->tag == NumType) - a = Match(a, OptionalType)->type; - if (b->tag == OptionalType && Match(b, OptionalType)->type->tag == NumType) - b = Match(b, OptionalType)->type; + if (a == NULL || b == NULL) + return NUM_PRECISION_INCOMPARABLE; if (is_int_type(a) && b->tag == NumType) return NUM_PRECISION_LESS; diff --git a/test/arrays.tm b/test/arrays.tm index 5816c35..5aaaa15 100644 --- a/test/arrays.tm +++ b/test/arrays.tm @@ -107,7 +107,7 @@ func main(): sorted : @[Int] = @[] repeat: sorted:insert(heap:heap_pop() or stop) - >> sorted == sorted:sorted() + >> sorted[] == sorted:sorted() = yes for i in 10: heap:heap_push((i*13337) mod 37) @@ -115,7 +115,7 @@ func main(): sorted = @[] repeat: sorted:insert(heap:heap_pop() or stop) - >> sorted == sorted:sorted() + >> sorted[] == sorted:sorted() = yes do: @@ -162,10 +162,10 @@ func main(): >> ["a", "b", "c"]:find("b") = 2? >> ["a", "b", "c"]:find("XXX") - = none:Int + = none >> [10, 20]:first(func(i:&Int): i:is_prime()) - = none:Int + = none >> [4, 5, 6]:first(func(i:&Int): i:is_prime()) = 2? @@ -183,4 +183,4 @@ func main(): >> nums = &[] >> nums:pop() - = none:Int + = none diff --git a/test/iterators.tm b/test/iterators.tm index 0b6c2a8..a8316ab 100644 --- a/test/iterators.tm +++ b/test/iterators.tm @@ -4,7 +4,7 @@ struct Pair(x:Text, y:Text) func pairwise(strs:[Text] -> func(->Pair?)): i := 1 return func(): - if i + 1 > strs.length: return none:Pair + if i + 1 > strs.length: return none i += 1 return Pair(strs[i-1], strs[i])? @@ -12,7 +12,7 @@ func range(first:Int, last:Int -> func(->Int?)): i := first return func(): if i > last: - return none:Int + return none i += 1 return (i-1)? diff --git a/test/nums.tm b/test/nums.tm index 1dc83ce..e36ae57 100644 --- a/test/nums.tm +++ b/test/nums.tm @@ -22,8 +22,8 @@ func main(): >> Num.INF:isinf() = yes - >> nan := none : Num - = none:Num + >> nan : Num = none + = none >> nan == nan = yes >> nan < nan @@ -46,10 +46,10 @@ func main(): = Int32(-1) >> nan + 1 - = none:Num + = none >> 0./0. - = none:Num + = none >> Num.PI:cos()!:near(-1) = yes diff --git a/test/optionals.tm b/test/optionals.tm index a1b0dcd..703e74e 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -66,7 +66,8 @@ func main(): = 5? >> if no: - none:Int + x : Int? = none + x else: 5 = 5? @@ -80,7 +81,8 @@ func main(): >> 5? or exit("Non-null is falsey") = 5 - >> (none:Int) or -1 + >> none_int : Int? = none + >> none_int or -1 = -1 do: @@ -88,7 +90,7 @@ func main(): >> yep := maybe_int(yes) = 123? >> nope := maybe_int(no) - = none:Int + = none >> if yep: >> yep = 123 @@ -103,7 +105,7 @@ func main(): >> yep := maybe_int64(yes) = Int64(123)? >> nope := maybe_int64(no) - = none:Int64 + = none >> if yep: >> yep = Int64(123) @@ -118,7 +120,7 @@ func main(): >> yep := maybe_array(yes) = [10, 20, 30]? >> nope := maybe_array(no) - = none:[Int] + = none >> if yep: >> yep = [10, 20, 30] @@ -133,7 +135,7 @@ func main(): >> yep := maybe_bool(yes) = no? >> nope := maybe_bool(no) - = none:Bool + = none >> if yep: >> yep = no @@ -148,7 +150,7 @@ func main(): >> yep := maybe_text(yes) = "Hello"? >> nope := maybe_text(no) - = none:Text + = none >> if yep: >> yep = "Hello" @@ -163,7 +165,7 @@ func main(): >> yep := maybe_num(yes) = 12.3? >> nope := maybe_num(no) - = none:Num + = none >> if yep: >> yep = 12.3 @@ -178,7 +180,7 @@ func main(): # >> yep := maybe_lambda(yes) # = func() [optionals.tm:54] : func()? >> nope := maybe_lambda(no) - = none : func() + = none # >> if yep: # >> yep # = func() [optionals.tm:54] @@ -193,7 +195,7 @@ func main(): >> yep := Struct.maybe(yes) = Struct(x=123, y="hello")? >> nope := Struct.maybe(no) - = none:Struct + = none >> if yep: >> yep = Struct(x=123, y="hello") @@ -208,7 +210,7 @@ func main(): >> yep := Enum.maybe(yes) = Enum.Y(123)? >> nope := Enum.maybe(no) - = none : Enum + = none >> if yep: >> yep = Enum.Y(123) @@ -223,7 +225,7 @@ func main(): >> yep := maybe_c_string(yes) = CString("hi")? >> nope := maybe_c_string(no) - = none : CString + = none >> if yep: >> yep = CString("hi") @@ -241,16 +243,15 @@ func main(): = 123 # Test comparisons, hashing, equality: - >> (none:Int == 5?) + >> (none == 5?) = no >> (5? == 5?) = yes - >> {none:Int, none:Int} - = {none:Int} >> nones : {Int?} = {none, none} - = {none} - >> [5?, none:Int, none:Int, 6?]:sorted() - = [none:Int, none:Int, 5, 6] + >> also_nones : {Int?} = {none} + >> nones == also_nones + >> [5?, none, none, 6?]:sorted() + = [none, none, 5, 6] do: >> value := if var := 5?: @@ -260,7 +261,7 @@ func main(): = 5 do: - >> value := if var := none:Int: + >> value := if var : Int? = none: var else: 0 @@ -274,7 +275,7 @@ func main(): >> opt do: - >> opt := none:Int + >> opt : Int? = none >> if opt: >> opt else: @@ -283,7 +284,8 @@ func main(): >> not 5? = no - >> not none:Int + >> nah : Int? = none + >> not nah = yes >> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")] diff --git a/test/serialization.tm b/test/serialization.tm index 2027cb9..9e3ac36 100644 --- a/test/serialization.tm +++ b/test/serialization.tm @@ -1,5 +1,5 @@ -struct Foo(name:Text, next=none:@Foo) +struct Foo(name:Text, next:@Foo?=none) enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text)) @@ -82,7 +82,7 @@ func main(): = yes do: - >> obj := none:Num + >> obj : Num? = none >> bytes := obj:serialized() >> deserialize(bytes -> Num?) == obj = yes diff --git a/test/structs.tm b/test/structs.tm index b546488..c1d2f7b 100644 --- a/test/structs.tm +++ b/test/structs.tm @@ -2,11 +2,11 @@ struct Single(x:Int) struct Pair(x,y:Int) struct Mixed(x:Int, text:Text) -struct LinkedList(x:Int, next=none:@LinkedList) +struct LinkedList(x:Int, next:@LinkedList?=none) struct Password(text:Text; secret) struct CorecursiveA(other:@CorecursiveB?) -struct CorecursiveB(other=none:@CorecursiveA) +struct CorecursiveB(other:@CorecursiveA?=none) func test_literals(): >> Single(123) diff --git a/test/tables.tm b/test/tables.tm index 144b93e..e75788b 100644 --- a/test/tables.tm +++ b/test/tables.tm @@ -7,7 +7,7 @@ func main(): >> t["two"] = 2? >> t["???"] - = none:Int + = none >> t["one"]! = 1 >> t["???"] or -1 @@ -37,7 +37,7 @@ func main(): >> t2["three"] = 3? >> t2["???"] - = none:Int + = none >> t2.length = 1 diff --git a/test/text.tm b/test/text.tm index ae91050..0316a96 100644 --- a/test/text.tm +++ b/test/text.tm @@ -59,7 +59,7 @@ func main(): >> Text.from_bytes([0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65])! = "Amélie" >> Text.from_bytes([Byte(0xFF)]) - = none:Text + = none amelie2 := "Am$(\U65\U301)lie" >> amelie2:split()