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" #name ">", data.lhs, data.rhs)
+#define BINOP(name) T(name, "<" #name ">%r %r" #name ">", 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()