aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-10-28 13:53:15 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-10-28 13:53:15 -0400
commit9c302fdc34403f46572d9524309617888ba816bb (patch)
tree58ea7faf390536503de114cf2889ed85ba60df7b
parentc632a72486d347e7ef30c0b7890e2045ed42b903 (diff)
parentce2aebe91085f987aab31bd2a49820fb605cf386 (diff)
Merge branch 'main' into internal-textsinternal-texts
-rw-r--r--ast.c5
-rw-r--r--ast.h6
-rw-r--r--compile.c63
-rw-r--r--docs/arrays.md20
-rw-r--r--docs/booleans.md14
-rw-r--r--docs/datetime.md237
-rw-r--r--docs/integers.md29
-rw-r--r--docs/metamethods.md2
-rw-r--r--docs/nums.md17
-rw-r--r--docs/pointers.md66
-rw-r--r--docs/sets.md8
-rw-r--r--docs/structs.md4
-rw-r--r--docs/tables.md4
-rw-r--r--docs/text.md12
-rw-r--r--environment.c12
-rw-r--r--examples/game/player.tm2
-rw-r--r--examples/game/world.tm20
-rw-r--r--examples/tomodeps/tomodeps.tm12
-rw-r--r--parse.c41
-rw-r--r--repl.c2
-rw-r--r--stdlib/datetime.c62
-rw-r--r--stdlib/datetime.h10
-rw-r--r--stdlib/paths.c8
-rw-r--r--stdlib/patterns.c30
-rw-r--r--stdlib/patterns.h2
-rw-r--r--stdlib/shell.c1
-rw-r--r--stdlib/stdlib.c2
-rw-r--r--stdlib/text.c3
-rw-r--r--stdlib/text.h3
-rw-r--r--stdlib/threads.c1
-rw-r--r--stdlib/tomo.h2
-rw-r--r--test/datetime.tm4
-rw-r--r--test/text.tm6
-rw-r--r--typecheck.c61
-rw-r--r--types.c12
-rw-r--r--types.h4
36 files changed, 467 insertions, 320 deletions
diff --git a/ast.c b/ast.c
index 170fd12a..b6ba6e00 100644
--- a/ast.c
+++ b/ast.c
@@ -130,7 +130,6 @@ Text_t ast_to_xml(ast_t *ast)
T(Negative, "<Negative>%k</Negative>", ast_to_xml_ptr(data.value))
T(Not, "<Not>%k</Not>", ast_to_xml_ptr(data.value))
T(HeapAllocate, "<HeapAllocate>%k</HeapAllocate>", ast_to_xml_ptr(data.value))
- T(StackReference, "<StackReference>%k</StackReference>", ast_to_xml_ptr(data.value))
T(Min, "<Min>%k%k%k</Min>", ast_to_xml_ptr(data.lhs), ast_to_xml_ptr(data.rhs), optional_tagged("key", data.key))
T(Max, "<Max>%k%k%k</Max>", ast_to_xml_ptr(data.lhs), ast_to_xml_ptr(data.rhs), optional_tagged("key", data.key))
T(Array, "<Array>%k%k</Array>", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items))
@@ -188,8 +187,8 @@ Text_t type_ast_to_xml(type_ast_t *t)
#define T(type, ...) case type: { auto data = t->__data.type; (void)data; return Text$format(__VA_ARGS__); }
T(UnknownTypeAST, "<UnknownType/>")
T(VarTypeAST, "%s", data.name)
- T(PointerTypeAST, "<PointerType is_stack=\"%s\">%k</PointerType>",
- data.is_stack ? "yes" : "no", type_ast_to_xml_ptr(data.pointed))
+ T(PointerTypeAST, "<PointerType is_view=\"%s\">%k</PointerType>",
+ data.is_view ? "yes" : "no", type_ast_to_xml_ptr(data.pointed))
T(ArrayTypeAST, "<ArrayType>%k</ArrayType>", type_ast_to_xml_ptr(data.item))
T(SetTypeAST, "<TableType>%k</TableType>", type_ast_to_xml_ptr(data.item))
T(ChannelTypeAST, "<ChannelType>%k</ChannelType>", type_ast_to_xml_ptr(data.item))
diff --git a/ast.h b/ast.h
index 2fb2c328..737b73eb 100644
--- a/ast.h
+++ b/ast.h
@@ -99,7 +99,7 @@ struct type_ast_s {
} VarTypeAST;
struct {
type_ast_t *pointed;
- bool is_stack:1;
+ bool is_view:1;
} PointerTypeAST;
struct {
type_ast_t *item;
@@ -127,7 +127,7 @@ typedef enum {
TextLiteral, TextJoin, PrintStatement,
Declare, Assign,
BinaryOp, UpdateAssign,
- Not, Negative, HeapAllocate, StackReference,
+ Not, Negative, HeapAllocate,
Min, Max,
Array, Channel, Set, Table, TableEntry, Comprehension,
FunctionDef, Lambda,
@@ -194,7 +194,7 @@ struct ast_s {
} BinaryOp, UpdateAssign;
struct {
ast_t *value;
- } Not, Negative, HeapAllocate, StackReference;
+ } Not, Negative, HeapAllocate;
struct {
ast_t *lhs, *rhs, *key;
} Min, Max;
diff --git a/compile.c b/compile.c
index b02e2ffe..cde052c6 100644
--- a/compile.c
+++ b/compile.c
@@ -280,9 +280,20 @@ CORD compile_type(type_t *t)
static CORD compile_lvalue(env_t *env, ast_t *ast)
{
if (!can_be_mutated(env, ast)) {
- if (ast->tag == Index || ast->tag == FieldAccess) {
- ast_t *subject = ast->tag == Index ? Match(ast, Index)->indexed : Match(ast, FieldAccess)->fielded;
- code_err(subject, "This is an immutable value, you can't assign to it");
+ if (ast->tag == Index) {
+ ast_t *subject = Match(ast, Index)->indexed;
+ type_t *t = get_type(env, subject);
+ if (t->tag == PointerType && Match(t, PointerType)->is_view)
+ code_err(ast, "This is a read-only view and you can't mutate its contents");
+ else
+ code_err(subject, "This is an immutable value, you can't mutate its contents");
+ } else if (ast->tag == FieldAccess) {
+ ast_t *subject = Match(ast, FieldAccess)->fielded;
+ type_t *t = get_type(env, subject);
+ if (t->tag == PointerType && Match(t, PointerType)->is_view)
+ code_err(subject, "This is a read-only view and you can't mutate its fields");
+ else
+ code_err(subject, "This is an immutable value, you can't assign to its fields");
} else {
code_err(ast, "This is a value of type %T and can't be used as an assignment target", get_type(env, ast));
}
@@ -508,8 +519,6 @@ CORD compile_statement(env_t *env, ast_t *ast)
if (!assign->targets->next && assign->targets->ast->tag == Var && is_idempotent(assign->targets->ast)) {
// Common case: assigning to one variable:
type_t *lhs_t = get_type(env, assign->targets->ast);
- if (lhs_t->tag == PointerType && Match(lhs_t, PointerType)->is_stack)
- code_err(test->expr, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory.");
env_t *val_scope = with_enum_scope(env, lhs_t);
type_t *rhs_t = get_type(val_scope, assign->values->ast);
CORD value;
@@ -537,8 +546,6 @@ CORD compile_statement(env_t *env, ast_t *ast)
int64_t i = 1;
for (ast_list_t *target = assign->targets, *value = assign->values; target && value; target = target->next, value = value->next) {
type_t *target_type = get_type(env, target->ast);
- if (target_type->tag == PointerType && Match(target_type, PointerType)->is_stack)
- code_err(ast, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory.");
env_t *val_scope = with_enum_scope(env, target_type);
type_t *value_type = get_type(val_scope, value->ast);
CORD val_code;
@@ -611,8 +618,6 @@ CORD compile_statement(env_t *env, ast_t *ast)
// Single assignment, no temp vars needed:
if (assign->targets && !assign->targets->next && is_idempotent(assign->targets->ast)) {
type_t *lhs_t = get_type(env, assign->targets->ast);
- if (lhs_t->tag == PointerType && Match(lhs_t, PointerType)->is_stack)
- code_err(ast, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory.");
env_t *val_env = with_enum_scope(env, lhs_t);
type_t *rhs_t = get_type(val_env, assign->values->ast);
CORD val;
@@ -630,8 +635,6 @@ CORD compile_statement(env_t *env, ast_t *ast)
int64_t i = 1;
for (ast_list_t *value = assign->values, *target = assign->targets; value && target; value = value->next, target = target->next) {
type_t *lhs_t = get_type(env, target->ast);
- if (lhs_t->tag == PointerType && Match(lhs_t, PointerType)->is_stack)
- code_err(ast, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory.");
env_t *val_env = with_enum_scope(env, lhs_t);
type_t *rhs_t = get_type(val_env, value->ast);
CORD val;
@@ -1520,7 +1523,7 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool
val = CORD_all("(&", val, ")");
else
code_err(ast, "This should be a pointer, not %T", get_type(env, ast));
- t = Type(PointerType, .pointed=t, .is_stack=true);
+ t = Type(PointerType, .pointed=t, .is_view=true);
++depth;
} else {
auto ptr = Match(t, PointerType);
@@ -1980,13 +1983,6 @@ CORD compile(env_t *env, ast_t *ast)
}
// TODO: for constructors, do new(T, ...) instead of heap((T){...})
case HeapAllocate: return heap_strf("heap(%r)", compile(env, Match(ast, HeapAllocate)->value));
- case StackReference: {
- ast_t *subject = Match(ast, StackReference)->value;
- if (can_be_mutated(env, subject))
- return CORD_all("(&", compile_lvalue(env, subject), ")");
- else
- code_err(subject, "This subject can't be mutated!");
- }
case Optional: {
ast_t *value = Match(ast, Optional)->value;
CORD value_code = compile(env, value);
@@ -2354,7 +2350,7 @@ CORD compile(env_t *env, ast_t *ast)
static int64_t comp_num = 1;
const char *comprehension_name = heap_strf("arr$%ld", comp_num++);
ast_t *comprehension_var = FakeAST(InlineCCode, .code=Text$format("&%s", comprehension_name),
- .type=Type(PointerType, .pointed=array_type, .is_stack=true));
+ .type=Type(PointerType, .pointed=array_type, .is_view=true));
Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var};
scope->comprehension_action = &comp_action;
CORD code = CORD_all("({ Array_t ", comprehension_name, " = {};");
@@ -2433,7 +2429,7 @@ CORD compile(env_t *env, ast_t *ast)
env_t *scope = fresh_scope(env);
const char *comprehension_name = heap_strf("table$%ld", comp_num++);
ast_t *comprehension_var = FakeAST(InlineCCode, .code=Text$format("&%s", comprehension_name),
- .type=Type(PointerType, .pointed=table_type, .is_stack=true));
+ .type=Type(PointerType, .pointed=table_type, .is_view=true));
CORD code = CORD_all("({ Table_t ", comprehension_name, " = {");
if (table->fallback)
@@ -2487,7 +2483,7 @@ CORD compile(env_t *env, ast_t *ast)
env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env);
const char *comprehension_name = heap_strf("set$%ld", comp_num++);
ast_t *comprehension_var = FakeAST(InlineCCode, .code=Text$format("&%s", comprehension_name),
- .type=Type(PointerType, .pointed=set_type, .is_stack=true));
+ .type=Type(PointerType, .pointed=set_type, .is_view=true));
CORD code = CORD_all("({ Table_t ", comprehension_name, " = {};");
Closure_t comp_action = {.fn=add_to_set_comprehension, .userdata=comprehension_var};
scope->comprehension_action = &comp_action;
@@ -2671,10 +2667,13 @@ CORD compile(env_t *env, ast_t *ast)
(void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Array$shuffled(", self, ", ", padded_item_size, ")");
} else if (streq(call->name, "sort") || streq(call->name, "sorted")) {
- CORD self = compile_to_pointer_depth(env, call->self, streq(call->name, "sort") ? 1 : 0, false);
+ CORD self = streq(call->name, "sort")
+ ? compile_to_pointer_depth(env, call->self, 1, false)
+ // No need to do an ARRAY_COPY() here because it happens inside Array$sorted():
+ : compile_to_pointer_depth(env, call->self, 0, false);
CORD comparison;
if (call->args) {
- type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
+ type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32));
arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t));
@@ -2687,7 +2686,7 @@ CORD compile(env_t *env, ast_t *ast)
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
CORD comparison;
if (call->args) {
- type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
+ type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32));
arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t));
@@ -2698,7 +2697,7 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("Array$heapify(", self, ", ", comparison, ", ", padded_item_size, ")");
} else if (streq(call->name, "heap_push")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
- type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
+ type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32));
ast_t *default_cmp = FakeAST(InlineCCode,
@@ -2711,7 +2710,7 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("Array$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")");
} else if (streq(call->name, "heap_pop")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
- type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
+ type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32));
ast_t *default_cmp = FakeAST(InlineCCode,
@@ -2722,8 +2721,8 @@ CORD compile(env_t *env, ast_t *ast)
CORD arg_code = compile_arguments(env, ast, arg_spec, call->args);
return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", padded_item_size, ", ", compile_type(item_t), ")");
} else if (streq(call->name, "binary_search")) {
- CORD self = compile_to_pointer_depth(env, call->self, 0, false);
- type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
+ CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
+ type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32));
ast_t *default_cmp = FakeAST(InlineCCode,
@@ -2744,8 +2743,8 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("Array$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "first")) {
- CORD self = compile_to_pointer_depth(env, call->self, 0, false);
- type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true);
+ CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
+ type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *predicate_type = Type(
ClosureType, .fn=Type(FunctionType, .args=new(arg_t, .name="item", .type=item_ptr), .ret=Type(BoolType)));
arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type);
@@ -3490,7 +3489,7 @@ CORD compile_type_info(env_t *env, type_t *t)
}
case PointerType: {
auto ptr = Match(t, PointerType);
- CORD sigil = ptr->is_stack ? "&" : "@";
+ CORD sigil = ptr->is_view ? "&" : "@";
return heap_strf("Pointer$info(%r, %r)",
CORD_quoted(sigil),
compile_type_info(env, ptr->pointed));
diff --git a/docs/arrays.md b/docs/arrays.md
index 45b0e8a6..ee28cf3b 100644
--- a/docs/arrays.md
+++ b/docs/arrays.md
@@ -301,7 +301,7 @@ Clears all elements from the array.
**Signature:**
```tomo
-func clear(arr: &[T] -> Void)
+func clear(arr: @[T] -> Void)
```
**Parameters:**
@@ -460,7 +460,7 @@ Removes and returns the top element of a heap. By default, this is the
**Signature:**
```tomo
-func heap_pop(arr: &[T], by: func(x,y:&T->Int32) = T.compare -> T)
+func heap_pop(arr: @[T], by: func(x,y:&T->Int32) = T.compare -> T)
```
**Parameters:**
@@ -490,7 +490,7 @@ is a *minimum* heap.
**Signature:**
```tomo
-func heap_push(arr: &[T], item: T, by=T.compare -> Void)
+func heap_push(arr: @[T], item: T, by=T.compare -> Void)
```
**Parameters:**
@@ -517,7 +517,7 @@ Converts an array into a heap.
**Signature:**
```tomo
-func heapify(arr: &[T], by: func(x,y:&T->Int32) = T.compare -> Void)
+func heapify(arr: @[T], by: func(x,y:&T->Int32) = T.compare -> Void)
```
**Parameters:**
@@ -544,7 +544,7 @@ Inserts an element at a specified position in the array.
**Signature:**
```tomo
-func insert(arr: &[T], item: T, at: Int = 0 -> Void)
+func insert(arr: @[T], item: T, at: Int = 0 -> Void)
```
**Parameters:**
@@ -579,7 +579,7 @@ Inserts an array of items at a specified position in the array.
**Signature:**
```tomo
-func insert_all(arr: &[T], items: [T], at: Int = 0 -> Void)
+func insert_all(arr: @[T], items: [T], at: Int = 0 -> Void)
```
**Parameters:**
@@ -639,7 +639,7 @@ Removes elements from the array starting at a specified index.
**Signature:**
```tomo
-func remove_at(arr: &[T], at: Int = -1, count: Int = 1 -> Void)
+func remove_at(arr: @[T], at: Int = -1, count: Int = 1 -> Void)
```
**Parameters:**
@@ -672,7 +672,7 @@ Removes all occurrences of a specified item from the array.
**Signature:**
```tomo
-func remove_item(arr: &[T], item: T, max_count: Int = -1 -> Void)
+func remove_item(arr: @[T], item: T, max_count: Int = -1 -> Void)
```
**Parameters:**
@@ -769,7 +769,7 @@ Shuffles the elements of the array in place.
**Signature:**
```tomo
-func shuffle(arr: &[T] -> Void)
+func shuffle(arr: @[T] -> Void)
```
**Parameters:**
@@ -818,7 +818,7 @@ Sorts the elements of the array in place in ascending order (small to large).
**Signature:**
```tomo
-func sort(arr: &[T], by=T.compare -> Void)
+func sort(arr: @[T], by=T.compare -> Void)
```
**Parameters:**
diff --git a/docs/booleans.md b/docs/booleans.md
index a08faa9f..5610d863 100644
--- a/docs/booleans.md
+++ b/docs/booleans.md
@@ -16,13 +16,12 @@ boolean values are case-insensitive variations of `yes`/`no`, `y`/`n`,
**Signature:**
```tomo
-func from_text(text: Text, success: Bool = !&Bool -> Bool)
+func from_text(text: Text -> Bool?)
```
**Parameters:**
- `text`: The string containing the boolean value.
-- `success`: If provided, this boolean value reference will be set to `yes` if the given text is a recognizable boolean value or `no` otherwise.
**Returns:**
`yes` if the string matches a recognized truthy boolean value; otherwise return `no`.
@@ -30,14 +29,11 @@ func from_text(text: Text, success: Bool = !&Bool -> Bool)
**Example:**
```tomo
>> Bool.from_text("yes")
-= yes
+= yes?
>> Bool.from_text("no")
-= no
->> success := yes
->> Bool.from_text("???", &success)
-= no
->> success
-= no
+= no?
+>> Bool.from_text("???")
+= !Bool
```
---
diff --git a/docs/datetime.md b/docs/datetime.md
index 96eda14a..334c615b 100644
--- a/docs/datetime.md
+++ b/docs/datetime.md
@@ -133,6 +133,85 @@ The date in `YYYY-MM-DD` format.
---
+### `day_of_month`
+
+**Description:**
+Return the integer day of the month (1-31).
+
+**Signature:**
+```tomo
+func day_of_month(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the day of the month from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The day of the month as an integer (1-31).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29):day_of_month()
+= 29
+```
+
+---
+
+### `day_of_week`
+
+**Description:**
+Return the integer day of the week (1-7), where 1 = Sunday, 2 = Monday,
+3 = Tuesday, 4 = Wednesday, 5 = Thursday, 6 = Friday, 7 = Saturday.
+
+**Signature:**
+```tomo
+func day_of_week(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the day of the week from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The day of the week as an integer (1-7).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29):day_of_week()
+= 1
+```
+
+---
+
+### `day_of_year`
+
+**Description:**
+Return the integer day of the year (1-366, including leap years).
+
+**Signature:**
+```tomo
+func day_of_year(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the day of the year from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The day of the year as an integer (1-366).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29):day_of_year()
+= 272
+```
+
+---
+
### `format`
**Description:**
@@ -188,69 +267,55 @@ A `DateTime` object representing the same moment as the given UNIX timestamp.
= Wed Dec 31 19:00:00 1969
```
----
-
-### `get`
+### `get_local_timezone`
-**Description:**
-Get various components of the given datetime object and store them in the
-provided optional fields.
+**Description:**
+Get the local timezone's name (e.g. `America/New_York` or `UTC`. By default,
+this value is read from `/etc/localtime`, however, this can be overridden by
+calling `DateTime.set_local_timezone(...)`.
**Signature:**
```tomo
-func get(datetime: DateTime, year : &Int? = !&Int, month : &Int? = !&Int, day : &Int? = !&Int, hour : &Int? = !&Int, minute : &Int? = !&Int, second : &Int? = !&Int, nanosecond : &Int? = !&Int, weekday : &Int? = !&Int, timezone : Text? = !Text -> Void)
+func get_local_timezone(->Text)
```
**Parameters:**
-- `datetime`: The datetime from which to extract information.
-- `year`: If non-null, store the year here.
-- `month`: If non-null, store the month here (1-12).
-- `day`: If non-null, store the day of the month here (1-31).
-- `hour`: If non-null, store the hour of the day here (0-23).
-- `minute`: If non-null, store the minute of the hour here (0-59).
-- `second`: If non-null, store the second of the minute here (0-59).
-- `nanosecond`: If non-null, store the nanosecond of the second here (0-1,000,000,000).
-- `weekday`: If non-null, store the day of the week here (sunday=1, saturday=7)
-- `timezone` (optional): If specified, give values in the given timezone (otherwise, use the current local timezone).
+None.
**Returns:**
-Nothing.
+The name of the current local timezone.
**Example:**
```tomo
-dt := DateTime(2024, 9, 29)
-month := 0
-dt:get(month=&month)
->> month
-= 9
+>> DateTime.get_local_timezone()
+= "America/New_York"
```
---
-### `get_local_timezone`
+### `hour`
-**Description:**
-Get the local timezone's name (e.g. `America/New_York` or `UTC`. By default,
-this value is read from `/etc/localtime`, however, this can be overridden by
-calling `DateTime.set_local_timezone(...)`.
+**Description:**
+Return the hour of the day as an integer (1-24).
**Signature:**
```tomo
-func get_local_timezone(->Text)
+func hour(datetime: DateTime, timezone : Text? = !Text -> Int)
```
**Parameters:**
-None.
+- `datetime`: The datetime to get the hour from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
-**Returns:**
-The name of the current local timezone.
+**Returns:**
+The hour of the day as an integer (1-24).
**Example:**
```tomo
->> DateTime.get_local_timezone()
-= "America/New_York"
+>> DateTime(2024, 9, 29, 11, 59):hour()
+= 11
```
---
@@ -282,6 +347,32 @@ the_future := now():after(hours=1, minutes=30)
---
+### `minute`
+
+**Description:**
+Return the minute of the day as an integer (0-59).
+
+**Signature:**
+```tomo
+func minute(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the minute from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The minute of the hour as an integer (0-59).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29, 11, 59):minute()
+= 59
+```
+
+---
+
### `minutes_till`
**Description:**
@@ -309,6 +400,58 @@ the_future := now():after(minutes=1, seconds=30)
---
+### `month`
+
+**Description:**
+Return the month of the year as an integer (1-12).
+
+**Signature:**
+```tomo
+func month(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the month from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The month of the year as an integer (1-12).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29, 11, 59):month()
+= 9
+```
+
+---
+
+### `nanosecond`
+
+**Description:**
+Return the nanosecond of the second as an integer (0-999,999,999).
+
+**Signature:**
+```tomo
+func nanosecond(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the nanosecond from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The nanosecond of the second as an integer (0-999,999,999).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29, 11, 59):month()
+= 9
+```
+
+---
+
### `new`
**Description:**
@@ -445,6 +588,32 @@ ago"`, while datetimes in the future will have the suffix `" later"`.
---
+### `second`
+
+**Description:**
+Return the second of the minute as an integer (0-59).
+
+**Signature:**
+```tomo
+func second(datetime: DateTime, timezone : Text? = !Text -> Int)
+```
+
+**Parameters:**
+
+- `datetime`: The datetime to get the second from.
+- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
+
+**Returns:**
+The second of the hour as an integer (0-59).
+
+**Example:**
+```tomo
+>> DateTime(2024, 9, 29, 11, 30, 59):second()
+= 59
+```
+
+---
+
### `seconds_till`
**Description:**
diff --git a/docs/integers.md b/docs/integers.md
index 60ccd71b..ef30b74d 100644
--- a/docs/integers.md
+++ b/docs/integers.md
@@ -144,39 +144,32 @@ Converts a text representation of an integer into an integer.
**Signature:**
```tomo
-func from_text(text: Text, success: Bool = !&Bool? -> Int)
+func from_text(text: Text -> Int?)
```
**Parameters:**
- `text`: The text containing the integer.
-- `success`: If non-null, this pointer will be set to `yes` if the whole text
- is a valid integer that fits within the representable range of the integer
- type, otherwise `no`.
**Returns:**
The integer represented by the text. If the given text contains a value outside
-of the representable range, the number will be truncated to the minimum or
-maximum representable value. Other failures to parse the number will return
-zero.
+of the representable range or if the entire text can't be parsed as an integer,
+a null value will be returned.
**Example:**
```tomo
>> Int.from_text("123")
-= 123
+= 123?
>> Int.from_text("0xFF")
-= 255
+= 255?
-success := no
->> Int.from_text("asdf", &success)
-= 0
->> success
-= no
+# Can't parse:
+>> Int.from_text("asdf")
+= !Int
->> Int8.from_text("9999999", &success)
-= 127
->> success
-= no
+# Outside valid range:
+>> Int8.from_text("9999999")
+= !Int
```
---
diff --git a/docs/metamethods.md b/docs/metamethods.md
index dbd11069..ca2ecccd 100644
--- a/docs/metamethods.md
+++ b/docs/metamethods.md
@@ -3,7 +3,7 @@
This language relies on a small set of "metamethods" which define special
behavior that is required for all types:
-- `func as_text(obj:&(optional)T, colorize=no, type:&TypeInfo_t -> Text)`: a method to
+- `func as_text(obj:&T?, colorize=no, type:&TypeInfo_t -> Text)`: a method to
convert the type to a string. If `colorize` is `yes`, then the method should
include ANSI escape codes for syntax highlighting. If the `obj` pointer is
`NULL`, a string representation of the type will be returned instead.
diff --git a/docs/nums.md b/docs/nums.md
index fc683fe7..9072f86a 100644
--- a/docs/nums.md
+++ b/docs/nums.md
@@ -543,7 +543,7 @@ The largest integer less than or equal to `x`.
### `format`
**Description:**
-Formats a number as a string with a specified precision.
+Formats a number as a text with a specified precision.
**Signature:**
```tomo
@@ -556,7 +556,7 @@ func format(n: Num, precision: Int = 0 -> Text)
- `precision`: The number of decimal places. Default is `0`.
**Returns:**
-A string representation of the number with the specified precision.
+A text representation of the number with the specified precision.
**Example:**
```tomo
@@ -569,20 +569,19 @@ A string representation of the number with the specified precision.
### `from_text`
**Description:**
-Converts a string representation of a number into a floating-point number.
+Converts a text representation of a number into a floating-point number.
**Signature:**
```tomo
-func from_text(text: Text, the_rest: Text = "!&Text" -> Num)
+func from_text(text: Text -> Num?)
```
**Parameters:**
-- `text`: The string containing the number.
-- `the_rest`: A string indicating what to return if the conversion fails. Default is `"!&Text"`.
+- `text`: The text containing the number.
**Returns:**
-The number represented by the string.
+The number represented by the text or a null value if the entire text can't be parsed as a number.
**Example:**
```tomo
@@ -917,7 +916,7 @@ func nan(tag: Text = "" -> Num)
**Parameters:**
-- `tag`: An optional tag to describe the NaN. Default is an empty string.
+- `tag`: An optional tag to describe the NaN. Default is an empty text.
**Returns:**
A NaN value.
@@ -1086,7 +1085,7 @@ func scientific(n: Num, precision: Int = 0 -> Text)
- `precision`: The number of decimal places. Default is `0`.
**Returns:**
-A string representation of the number in scientific notation with the specified precision.
+A text representation of the number in scientific notation with the specified precision.
**Example:**
```tomo
diff --git a/docs/pointers.md b/docs/pointers.md
index b1d0fc19..174c4296 100644
--- a/docs/pointers.md
+++ b/docs/pointers.md
@@ -1,12 +1,8 @@
# Pointers
Pointers are numeric values that represent a location in memory where some type
-of data lives. Pointers are created using either the `@` prefix operator to
-**a**llocate heap memory or the `&` prefix operator to get the address of a
-variable. Stack pointers (`&`) are more limited than heap pointers (`@`) and
-cannot be stored inside an array, set, table, struct, enum, or channel.
-However, stack pointers are useful for methods that mutate local variables and
-don't need to save the pointer anywhere.
+of data lives. Pointers are created using the `@` prefix operator to
+**a**llocate heap memory.
Pointers are the way in Tomo that you can create mutable data. All
datastructures are by default, immutable, but using pointers, you can create
@@ -34,25 +30,6 @@ do_mutation(my_nums)
= @[10, 1, 2]
```
-In general, heap pointers can be used as stack pointers if necessary, since
-the usage of stack pointers is restricted, but heap pointers don't have the
-same restrictions, so it's good practice to define functions that don't need
-to store pointers to use stack references. This lets you pass references to
-local variables or pointers to heap data depending on your needs.
-
-```tomo
-func swap_first_two(data:&[Int]):
- data[1], data[2] = data[2], data[1]
-
-...
-
-heap_nums := @[10, 20, 30]
-swap_first_two(heap_nums)
-
-local_nums := [10, 20, 30]
-swap_first_two(&local_nums)
-```
-
## Dereferencing
Pointers can be dereferenced to access the value that's stored at the pointer's
@@ -89,15 +66,14 @@ consistent.
## Null Safety
Tomo pointers are, by default, guaranteed to be non-null. If you write a
-function that takes either a `&T` or `@T`, the value that will be given
-is always non-null. However, optional pointers can be used by adding a
-question mark to the type: `&T?` or `@T?`. A null value can be created
-using the syntax `!@T` or `!&T`. You can also append a question mark to
-a pointer value so the type checker knows it's supposed to be optional:
+function that takes a `@T`, the value that will be given is always non-null.
+However, optional pointers can be used by adding a question mark to the type:
+`@T?`. A null value can be created using the syntax `!@T`. You can also append
+a question mark to a pointer value so the type checker knows it's supposed to
+be optional:
```
optional := @[10, 20]?
-optional := &foo?
```
The compiler will not allow you to dereference an optionally null pointer
@@ -120,15 +96,25 @@ you can use `ptr.foo` on a pointer to that struct type as well, without needing
to use `ptr[].foo`. The same is true for array accesses like `ptr[i]` and method
calls like `ptr:reversed()`.
-As a matter of convenience, local variables can also be automatically promoted
-to stack references when invoking methods that require a stack reference as the
-first argument. For example:
+# Read-Only Views
+
+In a small number of API methods (`array:first()`, `array:binary_search()`,
+`array:sort()`, `array:sorted()`, and `array:heapify()`), the methods allow you
+to provide custom comparison functions. However, for safety, we don't actually
+want the comparison methods to be able mutate the values inside of immutable
+array values. For implementation reasons, we can't pass the values themselves
+to the comparison functions, but need to pass pointers to the array members.
+So, to work around this, Tomo allows you to define functions that take
+immutable view pointers as arguments. These behave similarly to `@` pointers,
+but their type signature uses `&` instead of `@` and read-only view pointers
+cannot be used to mutate the contents that they point to and cannot be stored
+inside of any datastructures as elements or members.
```tomo
-func swap_first_two(arr:&[Int]):
- arr[1], arr[2] = arr[2], arr[1]
-...
-my_arr := [10, 20, 30] // not a pointer
-swap_first_two(my_arr) // ok, automatically converted to &my_arr
-my_arr:shuffle() // ok, automatically converted to &my_arr
+nums := @[10, 20, 30]
+>> nums:first(func(x:&Int): x / 2 == 10)
+= 2?
```
+
+Normal `@` pointers can be promoted to immutable view pointers automatically,
+but not vice versa.
diff --git a/docs/sets.md b/docs/sets.md
index bc1b3aca..e9c44b29 100644
--- a/docs/sets.md
+++ b/docs/sets.md
@@ -133,7 +133,7 @@ Adds multiple items to the set.
**Signature:**
```tomo
-func add_all(set:&{T}, items: [T] -> Void)
+func add_all(set:@{T}, items: [T] -> Void)
```
**Parameters:**
@@ -158,7 +158,7 @@ Removes an item from the set.
**Signature:**
```tomo
-func remove(set:&{T}, item: T -> Void)
+func remove(set:@{T}, item: T -> Void)
```
**Parameters:**
@@ -183,7 +183,7 @@ Removes multiple items from the set.
**Signature:**
```tomo
-func remove_all(set:&{T}, items: [T] -> Void)
+func remove_all(set:@{T}, items: [T] -> Void)
```
**Parameters:**
@@ -208,7 +208,7 @@ Removes all items from the set.
**Signature:**
```tomo
-func clear(set:&{T} -> Void)
+func clear(set:@{T} -> Void)
```
**Parameters:**
diff --git a/docs/structs.md b/docs/structs.md
index 842f815b..ce4f6ff5 100644
--- a/docs/structs.md
+++ b/docs/structs.md
@@ -27,10 +27,10 @@ struct Foo(name:Text, age:Int):
func greet(f:Foo):
say("Hi my name is $f.name and I am $f.age years old!")
- func get_older(f:&Foo):
+ func get_older(f:@Foo):
f.age += 1
...
-my_foo := Foo("Alice", 28)
+my_foo := @Foo("Alice", 28)
my_foo:greet()
my_foo:get_older()
```
diff --git a/docs/tables.md b/docs/tables.md
index eb727d7e..1de58118 100644
--- a/docs/tables.md
+++ b/docs/tables.md
@@ -118,7 +118,7 @@ not already in the table, its value will be assumed to be zero.
**Signature:**
```tomo
-func bump(t:&{K:V}, key: K, amount: Int = 1 -> Void)
+func bump(t:@{K:V}, key: K, amount: Int = 1 -> Void)
```
**Parameters:**
@@ -148,7 +148,7 @@ Removes all key-value pairs from the table.
**Signature:**
```tomo
-func clear(t:&{K:V})
+func clear(t:@{K:V})
```
**Parameters:**
diff --git a/docs/text.md b/docs/text.md
index e8392b8f..7941e6a5 100644
--- a/docs/text.md
+++ b/docs/text.md
@@ -274,7 +274,7 @@ functions that would normally be handled by a more extensive API:
```
Text.has(pattern:Pattern)->Bool
-Text.find(pattern:Pattern, start=1, length=!&Int64?)->Int
+Text.find(pattern:Pattern, start=1)->Int
Text.find_all(pattern:Pattern)->[Text]
Text.matches(pattern:Pattern)->[Text]?
Text.map(pattern:Pattern, fn:func(t:Text)->Text)->Text
@@ -642,7 +642,7 @@ See: [Patterns](#Patterns) for more information on patterns.
**Signature:**
```tomo
-func find(text: Text, pattern: Pattern, start: Int = 1, length: &Int64? = !&Int64 -> Int)
+func find(text: Text, pattern: Pattern, start: Int = 1)
```
**Parameters:**
@@ -650,8 +650,6 @@ func find(text: Text, pattern: Pattern, start: Int = 1, length: &Int64? = !&Int6
- `text`: The text to be searched.
- `pattern`: The pattern to search for.
- `start`: The index to start the search.
-- `length`: If non-null, this pointer's value will be set to the length of the
- match, or `-1` if there is no match.
**Returns:**
`0` if the target pattern is not found, otherwise the index where the match was
@@ -667,12 +665,6 @@ found.
= 2
>> " one two three ":find("{id}", start=5)
= 8
-
->> len := 0[64]
->> " one ":find("{id}", length=&len)
-= 4
->> len
-= 3[64]
```
---
diff --git a/environment.c b/environment.c
index 2607ab95..c980bd16 100644
--- a/environment.c
+++ b/environment.c
@@ -267,19 +267,27 @@ env_t *new_compilation_unit(CORD libname)
{"after", "DateTime$after", "func(dt:DateTime,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=!Text -> DateTime)"},
{"date", "DateTime$date", "func(dt:DateTime,timezone=!Text -> Text)"},
+ {"day_of_month", "DateTime$day_of_month", "func(dt:DateTime,timezone=!Text -> Int)"},
+ {"day_of_week", "DateTime$day_of_week", "func(dt:DateTime,timezone=!Text -> Int)"},
+ {"day_of_year", "DateTime$day_of_year", "func(dt:DateTime,timezone=!Text -> Int)"},
{"format", "DateTime$format", "func(dt:DateTime,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=!Text -> Text)"},
{"from_unix_timestamp", "DateTime$from_unix_timestamp", "func(timestamp:Int64 -> DateTime)"},
- {"get", "DateTime$get", "func(dt:DateTime,year,month,day,hour,minute,second,nanosecond,weekday=!&Int,timezone=!Text)"},
{"get_local_timezone", "DateTime$get_local_timezone", "func(->Text)"},
+ {"hour", "DateTime$hour", "func(dt:DateTime,timezone=!Text -> Int)"},
{"hours_till", "DateTime$hours_till", "func(now,then:DateTime -> Num)"},
+ {"minute", "DateTime$minute", "func(dt:DateTime,timezone=!Text -> Int)"},
{"minutes_till", "DateTime$minutes_till", "func(now,then:DateTime -> Num)"},
+ {"month", "DateTime$month", "func(dt:DateTime,timezone=!Text -> Int)"},
+ {"nanosecond", "DateTime$nanosecond", "func(dt:DateTime,timezone=!Text -> Int)"},
{"new", "DateTime$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=!Text -> DateTime)"},
{"parse", "DateTime$parse", "func(text:Text, format=\"%Y-%m-%dT%H:%M:%S%z\" -> DateTime?)"},
{"relative", "DateTime$relative", "func(dt:DateTime,relative_to=DateTime.now(),timezone=!Text -> Text)"},
+ {"second", "DateTime$second", "func(dt:DateTime,timezone=!Text -> Int)"},
{"seconds_till", "DateTime$seconds_till", "func(now:DateTime,then:DateTime -> Num)"},
{"set_local_timezone", "DateTime$set_local_timezone", "func(timezone=!Text)"},
{"time", "DateTime$time", "func(dt:DateTime,seconds=no,am_pm=yes,timezone=!Text -> Text)"},
{"unix_timestamp", "DateTime$unix_timestamp", "func(dt:DateTime -> Int64)"},
+ {"year", "DateTime$year", "func(dt:DateTime,timezone=!Text -> Int)"},
)},
{"Path", Type(TextType, .lang="Path", .env=namespace_env(env, "Path")), "Text_t", "Text$info", TypedArray(ns_entry_t,
{"append", "Path$append", "func(path:Path, text:Text, permissions=0o644[32])"},
@@ -336,7 +344,7 @@ env_t *new_compilation_unit(CORD libname)
{"as_c_string", "Text$as_c_string", "func(text:Text -> CString)"},
{"codepoint_names", "Text$codepoint_names", "func(text:Text -> [Text])"},
{"ends_with", "Text$ends_with", "func(text,suffix:Text -> Bool)"},
- {"find", "Text$find", "func(text:Text, pattern:Pattern, start=1, length=!&Int64 -> Int)"},
+ {"find", "Text$find", "func(text:Text, pattern:Pattern, start=1 -> Int)"},
{"find_all", "Text$find_all", "func(text:Text, pattern:Pattern -> [Text])"},
{"from_bytes", "Text$from_bytes", "func(bytes:[Byte] -> Text)"},
{"from_c_string", "Text$from_str", "func(str:CString -> Text)"},
diff --git a/examples/game/player.tm b/examples/game/player.tm
index 5909ae63..91f7a173 100644
--- a/examples/game/player.tm
+++ b/examples/game/player.tm
@@ -12,7 +12,7 @@ struct Player(pos,prev_pos:Vec2):
FRICTION := 0.99
SIZE := Vec2(30, 30)
- func update(p:&Player):
+ func update(p:@Player):
target_x := inline C:Num {
(Num_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0))
}
diff --git a/examples/game/world.tm b/examples/game/world.tm
index 71c14bfd..4df7f156 100644
--- a/examples/game/world.tm
+++ b/examples/game/world.tm
@@ -38,18 +38,18 @@ func solve_overlap(a_pos:Vec2, a_size:Vec2, b_pos:Vec2, b_size:Vec2 -> Vec2):
return Vec2(0, 0)
-struct World(player:@Player, goal:@Box, boxes:[@Box], dt_accum=0.0, won=no):
+struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no):
DT := 1./60.
- CURRENT := @World(@Player(Vec2(0,0), Vec2(0,0)), @Box(Vec2(0,0), Vec2(0,0), Color.GOAL), [:@Box])
+ CURRENT := @World(@Player(Vec2(0,0), Vec2(0,0)), @Box(Vec2(0,0), Vec2(0,0), Color.GOAL), @[:@Box])
STIFFNESS := 0.3
- func update(w:&World, dt:Num):
+ func update(w:@World, dt:Num):
w.dt_accum += dt
while w.dt_accum > 0:
w:update_once()
w.dt_accum -= World.DT
- func update_once(w:&World):
+ func update_once(w:@World):
w.player:update()
if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vec2(0,0):
@@ -57,11 +57,11 @@ struct World(player:@Player, goal:@Box, boxes:[@Box], dt_accum=0.0, won=no):
# Resolve player overlapping with any boxes:
for i in 3:
- for b in w.boxes:
+ for b in w.boxes[]:
w.player.pos += STIFFNESS * solve_overlap(w.player.pos, Player.SIZE, b.pos, b.size)
- func draw(w:&World):
- for b in w.boxes:
+ func draw(w:@World):
+ for b in w.boxes[]:
b:draw()
w.goal:draw()
w.player:draw()
@@ -71,17 +71,17 @@ struct World(player:@Player, goal:@Box, boxes:[@Box], dt_accum=0.0, won=no):
DrawText("WINNER", GetScreenWidth()/2-48*3, GetScreenHeight()/2-24, 48, (Color){0,0,0,0xFF});
}
- func load_map(w:&World, map:Text):
+ func load_map(w:@World, map:Text):
if map:has($/[]/):
map = map:replace_all({$/[]/: "#", $/@{1..}/: "@", $/ /: " "})
- w.boxes = [:@Box]
+ w.boxes = @[:@Box]
box_size := Vec2(50., 50.)
for y,line in map:lines():
for x,cell in line:split():
if cell == "#":
pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y)
box := @Box(pos, size=box_size, color=Color.GRAY)
- (&w.boxes):insert(box)
+ w.boxes:insert(box)
else if cell == "@":
pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y)
pos += box_size/2. - Player.SIZE/2.
diff --git a/examples/tomodeps/tomodeps.tm b/examples/tomodeps/tomodeps.tm
index 907734a0..15655756 100644
--- a/examples/tomodeps/tomodeps.tm
+++ b/examples/tomodeps/tomodeps.tm
@@ -25,7 +25,7 @@ func _get_file_dependencies(file:Path -> {Dependency}):
deps:add(Dependency.Module(module_name))
return deps
-func _build_dependency_graph(dep:Dependency, dependencies:&{Dependency:{Dependency}}):
+func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependency}}):
return if dependencies:has(dep)
dependencies:set(dep, {:Dependency}) # Placeholder
@@ -56,8 +56,8 @@ func _build_dependency_graph(dep:Dependency, dependencies:&{Dependency:{Dependen
_build_dependency_graph(dep2, dependencies)
func get_dependency_graph(dep:Dependency -> {Dependency:{Dependency}}):
- graph := {:Dependency:{Dependency}}
- _build_dependency_graph(dep, &graph)
+ graph := @{:Dependency:{Dependency}}
+ _build_dependency_graph(dep, graph)
return graph
func _printable_name(dep:Dependency -> Text):
@@ -70,7 +70,7 @@ func _printable_name(dep:Dependency -> Text):
else:
return "$(\x1b)[31;1m$(f.text_content) (not found)$(\x1b)[m"
-func _draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}, already_printed:&{Dependency}, prefix="", is_last=yes):
+func _draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}, already_printed:@{Dependency}, prefix="", is_last=yes):
if already_printed:has(dep):
say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep) ++ " $\x1b[2m(recursive)$\x1b[m")
return
@@ -86,13 +86,13 @@ func _draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}, already_
_draw_tree(child, dependencies, already_printed, child_prefix, is_child_last)
func draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}):
- printed := {:Dependency}
+ printed := @{:Dependency}
say(_printable_name(dep))
printed:add(dep)
deps := dependencies:get(dep) or {:Dependency}
for i,child in deps.items:
is_child_last := (i == deps.length)
- _draw_tree(child, dependencies, already_printed=&printed, is_last=is_child_last)
+ _draw_tree(child, dependencies, already_printed=printed, is_last=is_child_last)
func main(files:[Text]):
if files.length == 0:
diff --git a/parse.c b/parse.c
index d889ba6b..0f32e021 100644
--- a/parse.c
+++ b/parse.c
@@ -127,7 +127,6 @@ static PARSER(parse_return);
static PARSER(parse_say);
static PARSER(parse_set);
static PARSER(parse_skip);
-static PARSER(parse_stack_reference);
static PARSER(parse_statement);
static PARSER(parse_stop);
static PARSER(parse_struct_def);
@@ -629,18 +628,18 @@ type_ast_t *parse_channel_type(parse_ctx_t *ctx, const char *pos) {
type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
- bool is_stack;
+ bool is_view;
if (match(&pos, "@"))
- is_stack = false;
+ is_view = false;
else if (match(&pos, "&"))
- is_stack = true;
+ is_view = true;
else
return NULL;
spaces(&pos);
type_ast_t *type = expect(ctx, start, &pos, parse_non_optional_type,
"I couldn't parse a pointer type after this point");
- type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_stack=is_stack);
+ type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_view=is_view);
spaces(&pos);
if (match(&pos, "?"))
return NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type=ptr_type);
@@ -1251,32 +1250,6 @@ PARSER(parse_heap_alloc) {
return ast;
}
-PARSER(parse_stack_reference) {
- const char *start = pos;
- if (!match(&pos, "&")) return NULL;
- spaces(&pos);
- ast_t *val = expect(ctx, start, &pos, parse_term_no_suffix, "I expected an expression for this '&'");
-
- for (;;) {
- ast_t *new_term;
- if ((new_term=parse_index_suffix(ctx, val))
- || (new_term=parse_fncall_suffix(ctx, val))
- || (new_term=parse_field_suffix(ctx, val))) {
- val = new_term;
- } else break;
- }
- pos = val->end;
-
- 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);
- if (!next) break;
- ast = next;
- }
- return ast;
-}
-
PARSER(parse_not) {
const char *start = pos;
if (!match_word(&pos, "not")) return NULL;
@@ -1586,7 +1559,6 @@ PARSER(parse_term_no_suffix) {
|| (term=parse_int(ctx, pos))
|| (term=parse_negative(ctx, pos)) // Must come after num/int
|| (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))
@@ -2323,6 +2295,11 @@ PARSER(parse_doctest) {
if (!match(&pos, ">>")) return NULL;
spaces(&pos);
ast_t *expr = expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this doctest");
+ spaces(&pos);
+ comment(&pos);
+ // We need at least one newline before looking for "= <expected value>"
+ if (strspn(pos, "\r\n") < 1 && strcspn(pos, "\r\n") > 0)
+ parser_err(ctx, pos, pos + strcspn(pos, "\r\n"), "I couldn't parse this code as part of the doctest expression");
whitespace(&pos);
const char* output = NULL;
if (match(&pos, "=")) {
diff --git a/repl.c b/repl.c
index c8fbc94b..e88efcbd 100644
--- a/repl.c
+++ b/repl.c
@@ -135,7 +135,7 @@ const TypeInfo_t *type_to_type_info(type_t *t)
}
case PointerType: {
auto ptr = Match(t, PointerType);
- const char *sigil = ptr->is_stack ? "&" : "@";
+ const char *sigil = ptr->is_view ? "&" : "@";
const TypeInfo_t *pointed_info = type_to_type_info(ptr->pointed);
const TypeInfo_t pointer_info = {.size=sizeof(void*), .align=__alignof__(void*),
.tag=PointerInfo, .PointerInfo={.sigil=sigil, .pointed=pointed_info}};
diff --git a/stdlib/datetime.c b/stdlib/datetime.c
index 9fa4f8cd..98c23a8e 100644
--- a/stdlib/datetime.c
+++ b/stdlib/datetime.c
@@ -124,6 +124,68 @@ public void DateTime$get(
if (weekday) *weekday = I(info.tm_wday + 1);
}
+public Int_t DateTime$year(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_year + 1900);
+}
+
+public Int_t DateTime$month(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_mon + 1);
+}
+
+public Int_t DateTime$day_of_week(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_wday + 1);
+}
+
+public Int_t DateTime$day_of_month(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_mday);
+}
+
+public Int_t DateTime$day_of_year(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_yday);
+}
+
+public Int_t DateTime$hour(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_hour);
+}
+
+public Int_t DateTime$minute(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_min);
+}
+
+public Int_t DateTime$second(DateTime_t dt, OptionalText_t timezone)
+{
+ struct tm info = {};
+ WITH_TIMEZONE(timezone, localtime_r(&dt.tv_sec, &info));
+ return I(info.tm_sec);
+}
+
+public Int_t DateTime$nanosecond(DateTime_t dt, OptionalText_t timezone)
+{
+ (void)timezone;
+ return I(dt.tv_usec);
+}
+
public Text_t DateTime$format(DateTime_t dt, Text_t fmt, OptionalText_t timezone)
{
struct tm info;
diff --git a/stdlib/datetime.h b/stdlib/datetime.h
index 96669aa3..ccbc5190 100644
--- a/stdlib/datetime.h
+++ b/stdlib/datetime.h
@@ -18,7 +18,15 @@ DateTime_t DateTime$after(DateTime_t dt, double seconds, double minutes, double
CONSTFUNC double DateTime$seconds_till(DateTime_t now, DateTime_t then);
CONSTFUNC double DateTime$minutes_till(DateTime_t now, DateTime_t then);
CONSTFUNC double DateTime$hours_till(DateTime_t now, DateTime_t then);
-void DateTime$get(DateTime_t dt, Int_t *year, Int_t *month, Int_t *day, Int_t *hour, Int_t *minute, Int_t *second, Int_t *nanosecond, Int_t *weekday, OptionalText_t timezone);
+Int_t DateTime$year(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$month(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$day_of_week(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$day_of_month(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$day_of_year(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$hour(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$minute(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$second(DateTime_t dt, OptionalText_t timezone);
+Int_t DateTime$nanosecond(DateTime_t dt, OptionalText_t timezone);
Text_t DateTime$format(DateTime_t dt, Text_t fmt, OptionalText_t timezone);
Text_t DateTime$date(DateTime_t dt, OptionalText_t timezone);
Text_t DateTime$time(DateTime_t dt, bool seconds, bool am_pm, OptionalText_t timezone);
diff --git a/stdlib/paths.c b/stdlib/paths.c
index 8acc34ff..e8a1e1f6 100644
--- a/stdlib/paths.c
+++ b/stdlib/paths.c
@@ -439,9 +439,11 @@ public Text_t Path$write_unique_bytes(Path_t path, Array_t bytes)
char buf[PATH_MAX] = {};
strcpy(buf, path_str);
- int64_t suffixlen = 0;
- (void)Text$find(path, Pattern("{0+!X}{end}"), I(1), &suffixlen);
- if (suffixlen < 0) suffixlen = 0;
+ // Count the number of trailing characters leading up to the last "X"
+ // (e.g. "foo_XXXXXX.tmp" would yield suffixlen = 4)
+ size_t suffixlen = 0;
+ while (suffixlen < len && buf[len - 1 - suffixlen] != 'X')
+ ++suffixlen;
int fd = mkstemps(buf, suffixlen);
if (fd == -1)
diff --git a/stdlib/patterns.c b/stdlib/patterns.c
index 701aff9c..6acb58a2 100644
--- a/stdlib/patterns.c
+++ b/stdlib/patterns.c
@@ -67,7 +67,7 @@ static inline bool match_str(TextIter_t *state, int64_t *i, const char *str)
static inline bool match_property(TextIter_t *state, int64_t *i, uc_property_t prop)
{
if (*i >= state->text.length) return false;
- ucs4_t grapheme = Text$get_main_grapheme_fast(state, *i);
+ uint32_t grapheme = Text$get_main_grapheme_fast(state, *i);
// TODO: check every codepoint in the cluster?
if (uc_is_property(grapheme, prop)) {
*i += 1;
@@ -80,8 +80,8 @@ static int64_t parse_int(TextIter_t *state, int64_t *i)
{
int64_t value = 0;
for (;; *i += 1) {
- ucs4_t grapheme = Text$get_main_grapheme_fast(state, *i);
- int digit = uc_digit_value((ucs4_t)grapheme);
+ uint32_t grapheme = Text$get_main_grapheme_fast(state, *i);
+ int digit = uc_digit_value(grapheme);
if (digit < 0) break;
if (value >= INT64_MAX/10) break;
value = 10*value + digit;
@@ -143,8 +143,8 @@ static int64_t match_email(TextIter_t *state, int64_t index)
// dns-label = 1-63 ([a-zA-Z0-9-] | non-ascii)
if (index > 0) {
- ucs4_t prev_codepoint = Text$get_main_grapheme_fast(state, index - 1);
- if (uc_is_property_alphabetic((ucs4_t)prev_codepoint))
+ uint32_t prev_codepoint = Text$get_main_grapheme_fast(state, index - 1);
+ if (uc_is_property_alphabetic(prev_codepoint))
return -1;
}
@@ -310,7 +310,7 @@ static int64_t match_uri(TextIter_t *state, int64_t index)
if (index > 0) {
// Don't match if we're not at a word edge:
- ucs4_t prev_codepoint = Text$get_main_grapheme_fast(state, index - 1);
+ uint32_t prev_codepoint = Text$get_main_grapheme_fast(state, index - 1);
if (uc_is_property_alphabetic(prev_codepoint))
return -1;
}
@@ -407,7 +407,7 @@ static int64_t match_newline(TextIter_t *state, int64_t index)
if (index >= state->text.length)
return -1;
- ucs4_t grapheme = index >= state->text.length ? 0 : Text$get_main_grapheme_fast(state, index);
+ uint32_t grapheme = index >= state->text.length ? 0 : Text$get_main_grapheme_fast(state, index);
if (grapheme == '\n')
return 1;
if (grapheme == '\r' && Text$get_grapheme_fast(state, index + 1) == '\n')
@@ -796,14 +796,14 @@ static int64_t _find(Text_t text, Pattern_t pattern, int64_t first, int64_t last
return -1;
}
-public Int_t Text$find(Text_t text, Pattern_t pattern, Int_t from_index, int64_t *match_length)
+public Int_t Text$find(Text_t text, Pattern_t pattern, Int_t from_index)
{
int64_t first = Int_to_Int64(from_index, false);
if (first == 0) fail("Invalid index: 0");
if (first < 0) first = text.length + first + 1;
if (first > text.length || first < 1)
return I(0);
- int64_t found = _find(text, pattern, first-1, text.length-1, match_length);
+ int64_t found = _find(text, pattern, first-1, text.length-1, NULL);
return I(found+1);
}
@@ -1081,17 +1081,17 @@ public Array_t Text$split(Text_t text, Pattern_t pattern)
Array_t chunks = {};
- Int_t i = I_small(1);
+ int64_t i = 0;
for (;;) {
int64_t len = 0;
- Int_t found = Text$find(text, pattern, i, &len);
- if (I_is_zero(found)) break;
- Text_t chunk = Text$slice(text, i, Int$minus(found, I_small(1)));
+ int64_t found = _find(text, pattern, i, text.length-1, &len);
+ if (found < 0) break;
+ Text_t chunk = Text$slice(text, I(i+1), I(found));
Array$insert(&chunks, &chunk, I_small(0), sizeof(Text_t));
- i = Int$plus(found, I(MAX(len, 1)));
+ i = found + MAX(len, 1);
}
- Text_t last_chunk = Text$slice(text, i, I(text.length));
+ Text_t last_chunk = Text$slice(text, I(i+1), I(text.length));
Array$insert(&chunks, &last_chunk, I_small(0), sizeof(Text_t));
return chunks;
diff --git a/stdlib/patterns.h b/stdlib/patterns.h
index c1246b1e..9cfbcd6b 100644
--- a/stdlib/patterns.h
+++ b/stdlib/patterns.h
@@ -18,7 +18,7 @@ Pattern_t Pattern$escape_text(Text_t text);
Text_t Text$replace_all(Text_t text, Table_t replacements, Pattern_t backref_pat, bool recursive);
Array_t Text$split(Text_t text, Pattern_t pattern);
Text_t Text$trim(Text_t text, Pattern_t pattern, bool trim_left, bool trim_right);
-Int_t Text$find(Text_t text, Pattern_t pattern, Int_t i, int64_t *match_length);
+Int_t Text$find(Text_t text, Pattern_t pattern, Int_t i);
Array_t Text$find_all(Text_t text, Pattern_t pattern);
PUREFUNC bool Text$has(Text_t text, Pattern_t pattern);
Array_t Text$matches(Text_t text, Pattern_t pattern);
diff --git a/stdlib/shell.c b/stdlib/shell.c
index d2d0f78a..68c61115 100644
--- a/stdlib/shell.c
+++ b/stdlib/shell.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
+#include <unistr.h>
#include "arrays.h"
#include "integers.h"
diff --git a/stdlib/stdlib.c b/stdlib/stdlib.c
index 34acc828..d90f5748 100644
--- a/stdlib/stdlib.c
+++ b/stdlib/stdlib.c
@@ -441,7 +441,7 @@ public void end_test(const void *expr, const TypeInfo_t *type, const char *expec
Text_t expr_plain = USE_COLOR ? generic_as_text(expr, false, type) : expr_text;
bool success = Text$equal(&expr_plain, &expected_text);
if (!success) {
- Int_t colon = Text$find(expected_text, Text(":"), I_small(1), NULL);
+ Int_t colon = Text$find(expected_text, Text(":"), I_small(1));
if (colon.small != I_small(0).small) {
Text_t with_type = Text$concat(expr_plain, Text(" : "), type_name);
success = Text$equal(&with_type, &expected_text);
diff --git a/stdlib/text.c b/stdlib/text.c
index 0af95413..deca024a 100644
--- a/stdlib/text.c
+++ b/stdlib/text.c
@@ -56,6 +56,7 @@
#include <stdlib.h>
#include <sys/param.h>
+#include <unistr.h>
#include <unicase.h>
#include <unictype.h>
#include <unigbrk.h>
@@ -846,7 +847,7 @@ public int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index)
return 0;
}
-public ucs4_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index)
+public uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index)
{
return MAIN_GRAPHEME_CODEPOINT(Text$get_grapheme_fast(state, index));
}
diff --git a/stdlib/text.h b/stdlib/text.h
index 43eee1cc..e915eefd 100644
--- a/stdlib/text.h
+++ b/stdlib/text.h
@@ -6,7 +6,6 @@
#include <stdbool.h>
#include <printf.h>
#include <stdint.h>
-#include <unistr.h>
#include "datatypes.h"
#include "integers.h"
@@ -55,7 +54,7 @@ Array_t Text$lines(Text_t text);
Text_t Text$join(Text_t glue, Array_t pieces);
Text_t Text$repeat(Text_t text, Int_t count);
int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index);
-ucs4_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index);
+uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index);
static inline int32_t Text$get_grapheme(Text_t text, int64_t index)
{
diff --git a/stdlib/threads.c b/stdlib/threads.c
index bd9f017e..0cb47e1b 100644
--- a/stdlib/threads.c
+++ b/stdlib/threads.c
@@ -11,6 +11,7 @@
#include <sys/param.h>
#include "arrays.h"
+#include "datatypes.h"
#include "text.h"
#include "threads.h"
#include "types.h"
diff --git a/stdlib/tomo.h b/stdlib/tomo.h
index 515bb8da..64a9979b 100644
--- a/stdlib/tomo.h
+++ b/stdlib/tomo.h
@@ -3,8 +3,6 @@
// All of the different builtin modules can be included by including this one
// import
-#include <gc.h>
-#include <gmp.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/param.h>
diff --git a/test/datetime.tm b/test/datetime.tm
index 37559996..19ca74b0 100644
--- a/test/datetime.tm
+++ b/test/datetime.tm
@@ -29,9 +29,7 @@ func main():
>> t:hours_till(t:after(minutes=60))
= 1
- weekday := 0
- >> t:get(weekday=&weekday)
- >> weekday # 1 = Sun, 2 = Mon, 3 = Tue
+ >> t:day_of_week() # 1 = Sun, 2 = Mon, 3 = Tue
= 3
>> t:format("%A")
diff --git a/test/text.tm b/test/text.tm
index f87cedfb..3a364917 100644
--- a/test/text.tm
+++ b/test/text.tm
@@ -195,12 +195,6 @@ func main():
>> " one two three ":find($/{id}/, start=5)
= 8
- >> len := 0[64]
- >> " one ":find($/{id}/, length=&len)
- = 4
- >> len
- = 3[64]
-
!! Test text slicing:
>> "abcdef":slice()
= "abcdef"
diff --git a/typecheck.c b/typecheck.c
index 6d01359f..43dec880 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -46,13 +46,13 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
type_t *pointed_t = parse_type_ast(env, ptr->pointed);
if (pointed_t->tag == VoidType)
code_err(ast, "Void pointers are not supported. You probably meant 'Memory' instead of 'Void'");
- return Type(PointerType, .pointed=pointed_t, .is_stack=ptr->is_stack);
+ return Type(PointerType, .pointed=pointed_t, .is_view=ptr->is_view);
}
case ArrayTypeAST: {
type_ast_t *item_type = Match(ast, ArrayTypeAST)->item;
type_t *item_t = parse_type_ast(env, item_type);
if (!item_t) code_err(item_type, "I can't figure out what this type is.");
- if (has_stack_memory(item_t))
+ if (has_view_memory(item_t))
code_err(item_type, "Arrays can't have stack references because the array may outlive the stack frame.");
if (type_size(item_t) > ARRAY_MAX_STRIDE)
code_err(ast, "This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead.",
@@ -63,7 +63,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
type_ast_t *item_type = Match(ast, SetTypeAST)->item;
type_t *item_t = parse_type_ast(env, item_type);
if (!item_t) code_err(item_type, "I can't figure out what this type is.");
- if (has_stack_memory(item_t))
+ if (has_view_memory(item_t))
code_err(item_type, "Sets can't have stack references because the array may outlive the stack frame.");
if (type_size(item_t) > ARRAY_MAX_STRIDE)
code_err(ast, "This set holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an set of pointers instead.",
@@ -85,19 +85,19 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
type_ast_t *key_type_ast = Match(ast, TableTypeAST)->key;
type_t *key_type = parse_type_ast(env, key_type_ast);
if (!key_type) code_err(key_type_ast, "I can't figure out what type this is.");
- if (has_stack_memory(key_type))
+ if (has_view_memory(key_type))
code_err(key_type_ast, "Tables can't have stack references because the array may outlive the stack frame.");
type_ast_t *val_type_ast = Match(ast, TableTypeAST)->value;
type_t *val_type = parse_type_ast(env, val_type_ast);
if (!val_type) code_err(val_type_ast, "I can't figure out what type this is.");
- if (has_stack_memory(val_type))
+ if (has_view_memory(val_type))
code_err(val_type_ast, "Tables can't have stack references because the array may outlive the stack frame.");
return Type(TableType, .key_type=key_type, .value_type=val_type);
}
case FunctionTypeAST: {
auto fn = Match(ast, FunctionTypeAST);
type_t *ret_t = fn->ret ? parse_type_ast(env, fn->ret) : Type(VoidType);
- if (has_stack_memory(ret_t))
+ if (has_view_memory(ret_t))
code_err(fn->ret, "Functions are not allowed to return stack references, because the reference may no longer exist on the stack.");
arg_t *type_args = NULL;
for (arg_ast_t *arg = fn->args; arg; arg = arg->next) {
@@ -421,7 +421,7 @@ type_t *get_function_def_type(env_t *env, ast_t *ast)
REVERSE_LIST(args);
type_t *ret = fn->ret_type ? parse_type_ast(scope, fn->ret_type) : Type(VoidType);
- if (has_stack_memory(ret))
+ if (has_view_memory(ret))
code_err(ast, "Functions can't return stack references because the reference may outlive its stack frame.");
return Type(FunctionType, .args=args, .ret=ret);
}
@@ -510,45 +510,10 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case HeapAllocate: {
type_t *pointed = get_type(env, Match(ast, HeapAllocate)->value);
- if (has_stack_memory(pointed))
+ if (has_view_memory(pointed))
code_err(ast, "Stack references cannot be moved to the heap because they may outlive the stack frame they were created in.");
return Type(PointerType, .pointed=pointed);
}
- case StackReference: {
- // Supported:
- // &variable
- // &struct_variable.field.(...)
- // &struct_ptr.field.(...)
- // Not supported:
- // &ptr[]
- // &list[index]
- // &table[key]
- // &(expression).field
- // &(expression)
- // &optional_struct_ptr.field
- ast_t *value = Match(ast, StackReference)->value;
- if (value->tag == Var)
- return Type(PointerType, .pointed=get_type(env, value), .is_stack=true);
-
- if (value->tag == FieldAccess) {
- ast_t *base = value;
- while (base->tag == FieldAccess)
- base = Match(base, FieldAccess)->fielded;
-
- type_t *ref_type = get_type(env, value);
- type_t *base_type = get_type(env, base);
- if (base_type->tag == OptionalType) {
- code_err(base, "This value might be null, so it can't be safely dereferenced");
- } else if (base_type->tag == PointerType) {
- auto ptr = Match(base_type, PointerType);
- return Type(PointerType, .pointed=ref_type, .is_stack=ptr->is_stack);
- } else if (base->tag == Var) {
- return Type(PointerType, .pointed=ref_type, .is_stack=true);
- }
- }
-
- code_err(ast, "'&' stack references can only be used on variables or fields of variables");
- }
case Optional: {
ast_t *value = Match(ast, Optional)->value;
type_t *t = get_type(env, value);
@@ -607,7 +572,7 @@ type_t *get_type(env_t *env, ast_t *ast)
} else {
code_err(ast, "I can't figure out what type this array has because it has no members or explicit type");
}
- if (has_stack_memory(item_type))
+ if (has_view_memory(item_type))
code_err(ast, "Arrays cannot hold stack references, because the array may outlive the stack frame the reference was created in.");
return Type(ArrayType, .item_type=item_type);
}
@@ -636,7 +601,7 @@ type_t *get_type(env_t *env, ast_t *ast)
item_type = item_merged;
}
}
- if (has_stack_memory(item_type))
+ if (has_view_memory(item_type))
code_err(ast, "Sets cannot hold stack references because the set may outlive the reference's stack frame.");
return Type(SetType, .item_type=item_type);
}
@@ -681,7 +646,7 @@ type_t *get_type(env_t *env, ast_t *ast)
value_type = val_merged;
}
}
- if (has_stack_memory(key_type) || has_stack_memory(value_type))
+ if (has_view_memory(key_type) || has_view_memory(value_type))
code_err(ast, "Tables cannot hold stack references because the table may outlive the reference's stack frame.");
return Type(TableType, .key_type=key_type, .value_type=value_type);
}
@@ -1143,7 +1108,7 @@ type_t *get_type(env_t *env, ast_t *ast)
declared, ret);
}
- if (has_stack_memory(ret))
+ if (has_view_memory(ret))
code_err(ast, "Functions can't return stack references because the reference may outlive its stack frame.");
return Type(ClosureType, Type(FunctionType, .args=args, .ret=ret));
}
@@ -1328,7 +1293,7 @@ PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast)
auto index = Match(ast, Index);
type_t *indexed_type = get_type(env, index->indexed);
if (indexed_type->tag == PointerType)
- return true;
+ return !Match(indexed_type, PointerType)->is_view;
return can_be_mutated(env, index->indexed);
}
default: return false;
diff --git a/types.c b/types.c
index 5392c559..6a007c3e 100644
--- a/types.c
+++ b/types.c
@@ -67,7 +67,7 @@ Text_t type_to_text(type_t *t) {
}
case PointerType: {
auto ptr = Match(t, PointerType);
- Text_t sigil = ptr->is_stack ? Text("&") : Text("@");
+ Text_t sigil = ptr->is_view ? Text("&") : Text("@");
return Texts(sigil, type_to_text(ptr->pointed));
}
case EnumType: {
@@ -123,7 +123,7 @@ bool type_is_a(type_t *t, type_t *req)
auto t_ptr = Match(t, PointerType);
auto req_ptr = Match(req, PointerType);
if (type_eq(t_ptr->pointed, req_ptr->pointed))
- return (!t_ptr->is_stack && req_ptr->is_stack) || (!t_ptr->is_stack);
+ return (!t_ptr->is_view && req_ptr->is_view) || (!t_ptr->is_view);
}
return false;
}
@@ -275,11 +275,11 @@ PUREFUNC bool can_send_over_channel(type_t *t)
}
}
-PUREFUNC bool has_stack_memory(type_t *t)
+PUREFUNC bool has_view_memory(type_t *t)
{
switch (t->tag) {
- case PointerType: return Match(t, PointerType)->is_stack;
- case OptionalType: return has_stack_memory(Match(t, OptionalType)->type);
+ case PointerType: return Match(t, PointerType)->is_view;
+ case OptionalType: return has_view_memory(Match(t, OptionalType)->type);
default: return false;
}
}
@@ -345,7 +345,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed)
// Can't use @Foo for a function that wants @Baz
// But you *can* use @Foo for a function that wants @Memory
return false;
- else if (actual_ptr->is_stack && !needed_ptr->is_stack)
+ else if (actual_ptr->is_view && !needed_ptr->is_view)
// Can't use &x for a function that wants a @Foo or ?Foo
return false;
else
diff --git a/types.h b/types.h
index a0128285..f9e050e6 100644
--- a/types.h
+++ b/types.h
@@ -102,7 +102,7 @@ struct type_s {
} ClosureType;
struct {
type_t *pointed;
- bool is_stack:1;
+ bool is_view:1;
} PointerType;
struct {
const char *name;
@@ -144,7 +144,7 @@ type_t *value_type(type_t *a);
typedef enum {NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, NUM_PRECISION_MORE, NUM_PRECISION_INCOMPARABLE} precision_cmp_e;
PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b);
PUREFUNC bool has_heap_memory(type_t *t);
-PUREFUNC bool has_stack_memory(type_t *t);
+PUREFUNC bool has_view_memory(type_t *t);
PUREFUNC bool can_send_over_channel(type_t *t);
PUREFUNC bool can_promote(type_t *actual, type_t *needed);
PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t);