diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-10-28 13:53:15 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-10-28 13:53:15 -0400 |
| commit | 9c302fdc34403f46572d9524309617888ba816bb (patch) | |
| tree | 58ea7faf390536503de114cf2889ed85ba60df7b | |
| parent | c632a72486d347e7ef30c0b7890e2045ed42b903 (diff) | |
| parent | ce2aebe91085f987aab31bd2a49820fb605cf386 (diff) | |
Merge branch 'main' into internal-textsinternal-texts
| -rw-r--r-- | ast.c | 5 | ||||
| -rw-r--r-- | ast.h | 6 | ||||
| -rw-r--r-- | compile.c | 63 | ||||
| -rw-r--r-- | docs/arrays.md | 20 | ||||
| -rw-r--r-- | docs/booleans.md | 14 | ||||
| -rw-r--r-- | docs/datetime.md | 237 | ||||
| -rw-r--r-- | docs/integers.md | 29 | ||||
| -rw-r--r-- | docs/metamethods.md | 2 | ||||
| -rw-r--r-- | docs/nums.md | 17 | ||||
| -rw-r--r-- | docs/pointers.md | 66 | ||||
| -rw-r--r-- | docs/sets.md | 8 | ||||
| -rw-r--r-- | docs/structs.md | 4 | ||||
| -rw-r--r-- | docs/tables.md | 4 | ||||
| -rw-r--r-- | docs/text.md | 12 | ||||
| -rw-r--r-- | environment.c | 12 | ||||
| -rw-r--r-- | examples/game/player.tm | 2 | ||||
| -rw-r--r-- | examples/game/world.tm | 20 | ||||
| -rw-r--r-- | examples/tomodeps/tomodeps.tm | 12 | ||||
| -rw-r--r-- | parse.c | 41 | ||||
| -rw-r--r-- | repl.c | 2 | ||||
| -rw-r--r-- | stdlib/datetime.c | 62 | ||||
| -rw-r--r-- | stdlib/datetime.h | 10 | ||||
| -rw-r--r-- | stdlib/paths.c | 8 | ||||
| -rw-r--r-- | stdlib/patterns.c | 30 | ||||
| -rw-r--r-- | stdlib/patterns.h | 2 | ||||
| -rw-r--r-- | stdlib/shell.c | 1 | ||||
| -rw-r--r-- | stdlib/stdlib.c | 2 | ||||
| -rw-r--r-- | stdlib/text.c | 3 | ||||
| -rw-r--r-- | stdlib/text.h | 3 | ||||
| -rw-r--r-- | stdlib/threads.c | 1 | ||||
| -rw-r--r-- | stdlib/tomo.h | 2 | ||||
| -rw-r--r-- | test/datetime.tm | 4 | ||||
| -rw-r--r-- | test/text.tm | 6 | ||||
| -rw-r--r-- | typecheck.c | 61 | ||||
| -rw-r--r-- | types.c | 12 | ||||
| -rw-r--r-- | types.h | 4 |
36 files changed, 467 insertions, 320 deletions
@@ -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)) @@ -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; @@ -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: @@ -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, "=")) { @@ -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; @@ -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 @@ -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); |
