diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/compile/assertions.c | 14 | ||||
| -rw-r--r-- | src/compile/cli.c | 4 | ||||
| -rw-r--r-- | src/compile/functions.c | 2 | ||||
| -rw-r--r-- | src/compile/indexing.c | 2 | ||||
| -rw-r--r-- | src/compile/optionals.c | 8 | ||||
| -rw-r--r-- | src/compile/promotions.c | 12 | ||||
| -rw-r--r-- | src/compile/text.c | 15 | ||||
| -rw-r--r-- | src/environment.c | 4 | ||||
| -rw-r--r-- | src/modules.c | 3 | ||||
| -rw-r--r-- | src/stdlib/bytes.c | 2 | ||||
| -rw-r--r-- | src/stdlib/c_strings.c | 4 | ||||
| -rw-r--r-- | src/stdlib/enums.c | 2 | ||||
| -rw-r--r-- | src/stdlib/intX.c.h | 2 | ||||
| -rw-r--r-- | src/stdlib/lists.h | 10 | ||||
| -rw-r--r-- | src/stdlib/mapmacro.h | 4 | ||||
| -rw-r--r-- | src/stdlib/memory.c | 2 | ||||
| -rw-r--r-- | src/stdlib/numX.c.h | 6 | ||||
| -rw-r--r-- | src/stdlib/paths.c | 87 | ||||
| -rw-r--r-- | src/stdlib/paths.h | 2 | ||||
| -rw-r--r-- | src/stdlib/pointers.c | 8 | ||||
| -rw-r--r-- | src/stdlib/stdlib.h | 4 | ||||
| -rw-r--r-- | src/stdlib/structs.c | 4 | ||||
| -rw-r--r-- | src/stdlib/tables.c | 55 | ||||
| -rw-r--r-- | src/stdlib/tables.h | 4 | ||||
| -rw-r--r-- | src/stdlib/text.c | 16 | ||||
| -rw-r--r-- | src/stdlib/text.h | 5 | ||||
| -rw-r--r-- | src/tomo.c | 106 | ||||
| -rw-r--r-- | src/typecheck.c | 6 | ||||
| -rw-r--r-- | src/types.c | 9 | ||||
| -rw-r--r-- | src/types.h | 1 |
30 files changed, 231 insertions, 172 deletions
diff --git a/src/compile/assertions.c b/src/compile/assertions.c index 34055998..18531fd9 100644 --- a/src/compile/assertions.c +++ b/src/compile/assertions.c @@ -60,13 +60,10 @@ Text_t compile_assertion(env_t *env, ast_t *ast) { compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t), ";\n", "\n#line ", line, "\n", "if (!(", compile_condition(env, var_comparison), "))\n", "#line ", line, "\n", Texts("fail_source(", quoted_str(ast->file->filename), ", ", (int64_t)(expr->start - expr->file->text), - ", ", (int64_t)(expr->end - expr->file->text), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), - ", ", "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")), - ", " - "\" ", - failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"), + ", ", (int64_t)(expr->end - expr->file->text), ", Text$concat(", + message ? compile_to_type(env, message, Type(TextType)) : Text("Text(\"This assertion failed!\")"), + ", Text(\" (\"), ", expr_as_text(Text("_lhs"), operand_t, Text("no")), ", Text(\" ", failure, + " \"), ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", Text(\")\")));\n"), "}\n"); } default: { @@ -74,8 +71,7 @@ Text_t compile_assertion(env_t *env, ast_t *ast) { return Texts("if (!(", compile_condition(env, expr), "))\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->file->filename), ", ", (int64_t)(expr->start - expr->file->text), ", ", (int64_t)(expr->end - expr->file->text), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), + message ? compile_to_type(env, message, Type(TextType)) : Text("Text(\"This assertion failed!\")"), ");\n"); } } diff --git a/src/compile/cli.c b/src/compile/cli.c index 63a467ca..ade6caa7 100644 --- a/src/compile/cli.c +++ b/src/compile/cli.c @@ -83,10 +83,8 @@ static Text_t generate_usage(env_t *env, type_t *fn_type) { else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); else if (t->tag == EnumType) usage = Texts(usage, "[", flags, " val]"); else usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); - } else if (t->tag == EnumType) { - usage = Texts(usage, "\x1b[1m", flag, "\x1b[m"); } else { - usage = Texts(usage, "\x1b[1m", get_flag_options(t, Text("|")), "\x1b[m"); + usage = Texts(usage, "\x1b[1m", flag, "\x1b[m"); } } return usage; diff --git a/src/compile/functions.c b/src/compile/functions.c index ec37c0ad..01377a89 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -630,6 +630,8 @@ static void check_unused_vars(env_t *env, arg_ast_t *args, ast_t *body) { const char *name; } *entry = unused.entries.data + i * unused.entries.stride; if (streq(entry->name, "_")) continue; + // Global/file scoped vars are okay to mutate without reading + if (get_binding(env, entry->name) != NULL) continue; ast_t *var = Table$str_get(assigned_vars, entry->name); code_err(var, "This variable was assigned to, but never read from."); } diff --git a/src/compile/indexing.c b/src/compile/indexing.c index 13062641..031ef9a0 100644 --- a/src/compile/indexing.c +++ b/src/compile/indexing.c @@ -50,7 +50,7 @@ Text_t compile_indexing(env_t *env, ast_t *ast, bool checked) { return Texts("({ ", compile_declaration(item_type, Text("opt")), " = ", code, "; ", "if unlikely (", check_none(item_type, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->file->filename), ", ", start, ", ", end, ", ", - "\"This was expected to be a value, but it's `none`\\n\");\n", + "Text(\"This was expected to be a value, but it's `none`\\n\"));\n", optional_into_nonnone(item_type, Text("opt")), "; })"); } return code; diff --git a/src/compile/optionals.c b/src/compile/optionals.c index 83f387e2..75dff935 100644 --- a/src/compile/optionals.c +++ b/src/compile/optionals.c @@ -134,9 +134,9 @@ Text_t compile_non_optional(env_t *env, ast_t *ast) { compile_to_pointer_depth(env, f->fielded, 0, true), ";", "if unlikely (_test_enum.$tag != ", tag_name, ") {\n", "#line ", line, "\n", "fail_source(", quoted_str(f->fielded->file->filename), ", ", (int64_t)(f->fielded->start - f->fielded->file->text), - ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ", "\"This was expected to be ", - tag->name, ", but it was: \", ", expr_as_text(Text("_test_enum"), enum_t, Text("false")), - ", \"\\n\");\n}\n", + ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ", + "Text$concat(Text(\"This was expected to be ", tag->name, ", but it was: \"), ", + expr_as_text(Text("_test_enum"), enum_t, Text("false")), ", Text(\"\\n\")));\n}\n", compile_maybe_incref( env, WrapLiteralCode(value, Texts("_test_enum.", tag->name), .type = tag->type), tag->type), "; })"); @@ -149,7 +149,7 @@ Text_t compile_non_optional(env_t *env, ast_t *ast) { check_none(value_t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(value->file->filename), ", ", (int64_t)(value->start - value->file->text), ", ", (int64_t)(value->end - value->file->text), ", ", - "\"This was expected to be a value, but it's `none`\\n\");\n", + "Text(\"This was expected to be a value, but it's `none`\\n\"));\n", optional_into_nonnone(value_t, Text("opt")), "; })"); } } diff --git a/src/compile/promotions.c b/src/compile/promotions.c index 5b0ccb95..482e5ed0 100644 --- a/src/compile/promotions.c +++ b/src/compile/promotions.c @@ -61,12 +61,12 @@ bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *neede // Automatic optional checking for nums: if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) { int64_t line = get_line_number(ast->file, ast->start); - *code = - Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", "if unlikely (", - check_none(actual, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", - quoted_str(ast->file->filename), ", ", (int64_t)(ast->start - ast->file->text), ", ", - (int64_t)(ast->end - ast->file->text), ", ", "\"This was expected to be a value, but it's none\");\n", - optional_into_nonnone(actual, Text("opt")), "; })"); + *code = Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", "if unlikely (", + check_none(actual, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", (int64_t)(ast->start - ast->file->text), ", ", + (int64_t)(ast->end - ast->file->text), ", ", + "Text(\"This was expected to be a value, but it's none\"));\n", + optional_into_nonnone(actual, Text("opt")), "; })"); return true; } diff --git a/src/compile/text.c b/src/compile/text.c index 3a8a227c..2a64ca45 100644 --- a/src/compile/text.c +++ b/src/compile/text.c @@ -4,7 +4,6 @@ #include "../ast.h" #include "../environment.h" -#include "../naming.h" #include "../stdlib/datatypes.h" #include "../stdlib/tables.h" #include "../stdlib/text.h" @@ -102,19 +101,12 @@ Text_t compile_text_ast(env_t *env, ast_t *ast) { type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE; if (!text_t || text_t->tag != TextType) code_err(ast, quoted(lang), " is not a valid text language name"); - Text_t lang_constructor; - if (!lang || streq(lang, "Text")) lang_constructor = Text("Text"); - else - lang_constructor = namespace_name(Match(text_t, TextType)->env, Match(text_t, TextType)->env->namespace->parent, - Text$from_str(lang)); - ast_list_t *chunks = Match(ast, TextJoin)->children; if (!chunks) { - return Texts(lang_constructor, "(\"\")"); + return Text("EMPTY_TEXT"); } else if (!chunks->next && chunks->ast->tag == TextLiteral) { Text_t literal = Match(chunks->ast, TextLiteral)->text; - if (string_literal_is_all_ascii(literal)) - return Texts(lang_constructor, "(", compile_text_literal(literal), ")"); + if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")"); return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); } else { Text_t code = EMPTY_TEXT; @@ -142,7 +134,6 @@ Text_t compile_text_ast(env_t *env, ast_t *ast) { code = Texts(code, chunk_code); if (chunk->next) code = Texts(code, ", "); } - if (chunks->next) return Texts(lang_constructor, "s(", code, ")"); - else return code; + return Texts("Text$concat(", code, ")"); } } diff --git a/src/environment.c b/src/environment.c index cf662749..a82274e7 100644 --- a/src/environment.c +++ b/src/environment.c @@ -341,6 +341,10 @@ env_t *global_env(bool source_mapping) { {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, // {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, // {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644) -> Result)"}, // + {"writer", "Path$writer", + "func(path:Path, append=no, permissions=Int32(0o644) -> func(text:Text, close=no -> Result))"}, // + {"byte_writer", "Path$byte_writer", + "func(path:Path, append=no, permissions=Int32(0o644) -> func(bytes:[Byte], close=no -> Result))"}, // {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644) -> Result)"}, // {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path?)"}, // {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path?)"}), diff --git a/src/modules.c b/src/modules.c index 056cd5cc..c7c29d24 100644 --- a/src/modules.c +++ b/src/modules.c @@ -131,6 +131,9 @@ bool try_install_module(module_info_t mod, bool ask_confirmation) { if (mod.revision) xsystem("git clone --depth=1 --revision ", mod.revision, " ", mod.git, " ", dest); else if (mod.version) xsystem("git clone --depth=1 --branch ", mod.version, " ", mod.git, " ", dest); else xsystem("git clone --depth=1 ", mod.git, " ", dest); + // Clean up .git/ folder after cloning: + xsystem("rm -rf ", dest, "/.git"); + // Build library: xsystem("tomo -L ", dest); return true; } else if (mod.url) { diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index 4416d804..a1e953a0 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -25,7 +25,7 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo '\0', }; Text_t text = Text$from_str(digits); - if (colorize) text = Texts(Text("\x1b[35m"), text, Text("\x1b[m")); + if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m")); return text; } diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c index 57960577..cbe46b68 100644 --- a/src/stdlib/c_strings.c +++ b/src/stdlib/c_strings.c @@ -70,8 +70,8 @@ const char *CString$join(const char *glue, List_t strings) { Text_t ret = EMPTY_TEXT; Text_t glue_text = Text$from_str(glue); for (int64_t i = 0; i < (int64_t)strings.length; i++) { - if (i > 0) ret = Texts(ret, glue_text); - ret = Texts(ret, Text$from_str(*(const char **)(strings.data + i * strings.stride))); + if (i > 0) ret = Text$concat(ret, glue_text); + ret = Text$concat(ret, Text$from_str(*(const char **)(strings.data + i * strings.stride))); } return Text$as_c_string(ret); } diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c index b9b970fa..9cc16c5d 100644 --- a/src/stdlib/enums.c +++ b/src/stdlib/enums.c @@ -65,7 +65,7 @@ Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { NamedType_t value = type->EnumInfo.tags[tag - 1]; if (!value.type || value.type->size == 0) { Text_t text = Text$from_str(value.name); - return colorize ? Texts(Text("\x1b[1m"), text, Text("\x1b[m")) : text; + return colorize ? Text$concat(Text("\x1b[1m"), text, Text("\x1b[m")) : text; } return generic_as_text(obj + value_offset(type), colorize, value.type); diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 72dfe6ed..03322e5b 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -98,7 +98,7 @@ Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *info) (void)info; if (!i) return Text(NAME_STR); Text_t text = _int64_to_text((int64_t)(*(INT_T *)i)); - return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; + return colorize ? Text$concat(Text("\033[35m"), text, Text("\033[m")) : text; } public Text_t NAMESPACED(value_as_text)(INT_T i) { return _int64_to_text((int64_t)i); } diff --git a/src/stdlib/lists.h b/src/stdlib/lists.h index 6a0a1d43..9ac8bf1b 100644 --- a/src/stdlib/lists.h +++ b/src/stdlib/lists.h @@ -20,8 +20,9 @@ extern char _EMPTY_LIST_SENTINEL; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (list.length + 1) - 1; \ if (unlikely(off < 0 || off >= list.length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ - (int64_t)list.length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid list index: "), convert_to_text(index), Text(" (list has length "), \ + convert_to_text((int64_t)list.length), Text(")\n"))); \ *(item_type *)(list.data + list.stride * off); \ }) #define List_get(list_expr, index_expr, item_type, var, optional_expr, none_expr) \ @@ -40,8 +41,9 @@ extern char _EMPTY_LIST_SENTINEL; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (list->length + 1) - 1; \ if (unlikely(off < 0 || off >= list->length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ - (int64_t)list->length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid list index: "), convert_to_text(index), Text(" (list has length "), \ + convert_to_text((int64_t)list->length), Text(")\n"))); \ if (list->data_refcount > 0) List$compact(list, sizeof(item_type)); \ (item_type *)(list->data + list->stride * off); \ }) diff --git a/src/stdlib/mapmacro.h b/src/stdlib/mapmacro.h index 7b0e3c4e..5e9eaa36 100644 --- a/src/stdlib/mapmacro.h +++ b/src/stdlib/mapmacro.h @@ -7,9 +7,7 @@ #define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) -#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) -#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) +#define EVAL(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define MAP_END(...) #define MAP_OUT diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c index fd396463..53a180fb 100644 --- a/src/stdlib/memory.c +++ b/src/stdlib/memory.c @@ -18,7 +18,7 @@ Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *info) { (void)info; if (!p) return Text("Memory"); Text_t text = Text$from_str(String("Memory<", (void *)p, ">")); - return colorize ? Texts(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; + return colorize ? Text$concat(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; } public diff --git a/src/stdlib/numX.c.h b/src/stdlib/numX.c.h index 0e84708f..2fde8c45 100644 --- a/src/stdlib/numX.c.h +++ b/src/stdlib/numX.c.h @@ -50,7 +50,7 @@ PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo if (!x) return Text(TYPE_STR); static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); Text_t text = NAMESPACED(value_as_text)(*(NUM_T *)x); - return colorize ? Texts(color_prefix, text, color_suffix) : text; + return colorize ? Text$concat(color_prefix, text, color_suffix) : text; } public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { @@ -73,7 +73,7 @@ PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo if (!x) return Text(TYPE_STR); static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); Text_t text = Num$value_as_text((double)*(NUM_T *)x); - return colorize ? Texts(color_prefix, text, color_suffix) : text; + return colorize ? Text$concat(color_prefix, text, color_suffix) : text; } public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { @@ -115,7 +115,7 @@ public Text_t NAMESPACED(percent)(NUM_T x, NUM_T precision) { NUM_T d = SUFFIXED(100.) * x; d = NAMESPACED(with_precision)(d, precision); - return Texts(NAMESPACED(value_as_text)(d), Text("%")); + return Text$concat(NAMESPACED(value_as_text)(d), Text("%")); } public diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index ed8383fd..e3028cce 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -324,6 +324,77 @@ Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions) { return _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } +typedef struct { + const char *path_str; + int fd; + int mode; + int permissions; +} writer_data_t; + +static Result_t _write_bytes_to_fd(List_t bytes, bool close_file, void *userdata) { + writer_data_t *data = userdata; + if (bytes.length > 0) { + int fd = open(data->path_str, data->mode, data->permissions); + if (fd == -1) { + if (errno == EMFILE || errno == ENFILE) { + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. + GC_gcollect(); + fd = open(data->path_str, data->mode, data->permissions); + } + if (fd == -1) return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); + } + data->fd = fd; + + if (bytes.stride != 1) List$compact(&bytes, 1); + ssize_t written = write(data->fd, bytes.data, (size_t)bytes.length); + if (written != (ssize_t)bytes.length) + return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); + } + // After first successful write, all writes are appends + data->mode = (O_WRONLY | O_CREAT | O_APPEND); + + if (close_file && data->fd != -1) { + if (close(data->fd) == -1) + return FailureResult("Failed to close file: ", data->path_str, " (", strerror(errno), ")"); + data->fd = -1; + } + return SuccessResult; +} + +static Result_t _write_text_to_fd(Text_t text, bool close_file, void *userdata) { + return _write_bytes_to_fd(Text$utf8(text), close_file, userdata); +} + +static void _writer_cleanup(writer_data_t *data) { + if (data && data->fd != -1) { + close(data->fd); + data->fd = -1; + } +} + +public +Closure_t Path$byte_writer(Path_t path, bool append, int permissions) { + path = Path$expand_home(path); + const char *path_str = Path$as_c_string(path); + int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC); + writer_data_t *userdata = + new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions); + GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = _write_bytes_to_fd, .userdata = userdata}; +} + +public +Closure_t Path$writer(Path_t path, bool append, int permissions) { + path = Path$expand_home(path); + const char *path_str = Path$as_c_string(path); + int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC); + writer_data_t *userdata = + new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions); + GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = _write_text_to_fd, .userdata = userdata}; +} + public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { path = Path$expand_home(path); @@ -331,8 +402,8 @@ OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { int fd = open(path_str, O_RDONLY); if (fd == -1) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); fd = open(path_str, O_RDONLY); } @@ -619,7 +690,7 @@ bool Path$has_extension(Path_t path, Text_t extension) { if (extension.length == 0) return !Text$has(Text$from(last, I(2)), Text(".")) || Text$equal_values(last, Text("..")); - if (!Text$starts_with(extension, Text("."), NULL)) extension = Texts(Text("."), extension); + if (!Text$starts_with(extension, Text("."), NULL)) extension = Text$concat(Text("."), extension); return Text$ends_with(Text$from(last, I(2)), extension, NULL); } @@ -705,8 +776,8 @@ OptionalClosure_t Path$by_line(Path_t path) { FILE *f = fopen(path_str, "r"); if (f == NULL) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); f = fopen(path_str, "r"); } @@ -726,8 +797,8 @@ OptionalList_t Path$lines(Path_t path) { FILE *f = fopen(path_str, "r"); if (f == NULL) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); f = fopen(path_str, "r"); } @@ -808,7 +879,7 @@ Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) { && (path->components.length == 0 || !Text$equal_values(*(Text_t *)(path->components.data), Text("..")))) text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text); - if (color) text = Texts(Text("\033[32;1m"), text, Text("\033[m")); + if (color) text = Text$concat(Text("\033[32;1m"), text, Text("\033[m")); return text; } diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index 881a3c78..c272314c 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -39,6 +39,8 @@ Result_t Path$write(Path_t path, Text_t text, int permissions); Result_t Path$write_bytes(Path_t path, List_t bytes, int permissions); Result_t Path$append(Path_t path, Text_t text, int permissions); Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions); +Closure_t Path$byte_writer(Path_t path, bool append, int permissions); +Closure_t Path$writer(Path_t path, bool append, int permissions); OptionalText_t Path$read(Path_t path); OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t limit); Result_t Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks); diff --git a/src/stdlib/pointers.c b/src/stdlib/pointers.c index 74037613..0bf9a274 100644 --- a/src/stdlib/pointers.c +++ b/src/stdlib/pointers.c @@ -38,14 +38,14 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type) { if (top_level) { root = ptr; } else if (ptr == root) { - Text_t text = Texts(Text$from_str(ptr_info.sigil), Text("~1")); - return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + Text_t text = Text$concat(Text$from_str(ptr_info.sigil), Text("~1")); + return colorize ? Text$concat(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; } else { TypeInfo_t rec_table = *Table$info(type, &Int64$info); int64_t *id = Table$get(pending, x, &rec_table); if (id) { - Text_t text = Texts(Text$from_str(ptr_info.sigil), Int64$value_as_text(*id)); - return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + Text_t text = Text$concat(Text$from_str(ptr_info.sigil), Int64$value_as_text(*id)); + return colorize ? Text$concat(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; } int64_t next_id = (int64_t)pending.entries.length + 2; Table$set(&pending, x, &next_id, &rec_table); diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index 234048e9..087ed4bf 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -37,7 +37,7 @@ void tomo_cleanup(void); exit(1); \ }) -#define fail_source(filename, start, end, ...) \ +#define fail_source(filename, start, end, message) \ ({ \ tomo_cleanup(); \ fflush(stdout); \ @@ -46,7 +46,7 @@ void tomo_cleanup(void); print_stacktrace(stderr, 0); \ fputs("\n", stderr); \ if (USE_COLOR) fputs("\x1b[31;1m", stderr); \ - fprint_inline(stderr, __VA_ARGS__); \ + Text$print(stderr, message); \ file_t *_file = (filename) ? load_file(filename) : NULL; \ if ((filename) && _file) { \ fputs("\n", stderr); \ diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c index da8f1461..d1b9f824 100644 --- a/src/stdlib/structs.c +++ b/src/stdlib/structs.c @@ -126,10 +126,10 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type Text_t name = Text$from_str(type->StructInfo.name); if (type->StructInfo.is_secret || type->StructInfo.is_opaque) { - return colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Texts(name, Text("(...)")); + return colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Text$concat(name, Text("(...)")); } - Text_t text = colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Texts(name, Text("(")); + Text_t text = colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Text$concat(name, Text("(")); ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index a801957f..d417545d 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -24,14 +24,6 @@ #include "types.h" #include "util.h" -// #define DEBUG_TABLES - -#ifdef DEBUG_TABLES -#define hdebug(...) print_inline("\x1b[2m", __VA_ARGS__, "\x1b[m") -#else -#define hdebug(...) (void)0 -#endif - // Helper accessors for type functions/values: #define HASH_KEY(t, k) (generic_hash((k), type->TableInfo.key) % ((t).bucket_info->count)) #define EQUAL_KEYS(x, y) (generic_equal((x), (y), type->TableInfo.key)) @@ -76,18 +68,6 @@ PUREFUNC static INLINE size_t value_offset(const TypeInfo_t *info) { return offset; } -static INLINE void hshow(const Table_t *t) { - hdebug("{"); - for (uint32_t i = 0; t->bucket_info && i < t->bucket_info->count; i++) { - if (i > 0) hdebug(" "); - if (t->bucket_info->buckets[i].occupied) - hdebug("[", i, "]=", (uint32_t)t->bucket_info->buckets[i].index, "(", - t->bucket_info->buckets[i].next_bucket, ")"); - else hdebug("[", i, "]=_"); - } - hdebug("}\n"); -} - static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) { if (t->entries.data_refcount != 0) List$compact(&t->entries, (int64_t)entry_size(type)); @@ -104,14 +84,10 @@ PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t if (!key || !t.bucket_info) return NULL; uint64_t hash = HASH_KEY(t, key); - hshow(&t); - hdebug("Getting value with initial probe at ", hash, "\n"); bucket_t *buckets = t.bucket_info->buckets; for (uint64_t i = hash; buckets[i].occupied; i = buckets[i].next_bucket) { - hdebug("Checking against key in bucket ", i, "\n"); void *entry = GET_ENTRY(t, buckets[i].index); if (EQUAL_KEYS(entry, key)) { - hdebug("Found key!\n"); return entry + value_offset(type); } if (buckets[i].next_bucket == END_OF_CHAIN) break; @@ -130,24 +106,18 @@ PUREFUNC public void *Table$get(Table_t t, const void *key, const TypeInfo_t *ty static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const TypeInfo_t *type) { assert(t->bucket_info); - hshow(t); const void *key = entry; bucket_t *buckets = t->bucket_info->buckets; uint64_t hash = HASH_KEY(*t, key); - hdebug("Hash value (mod ", (int32_t)t->bucket_info->count, ") = ", hash, "\n"); bucket_t *bucket = &buckets[hash]; if (!bucket->occupied) { - hdebug("Got an empty space\n"); // Empty space: bucket->occupied = 1; bucket->index = index; bucket->next_bucket = END_OF_CHAIN; - hshow(t); return; } - hdebug("Collision detected in bucket ", hash, " (entry ", (uint32_t)bucket->index, ")\n"); - while (buckets[t->bucket_info->last_free].occupied) { assert(t->bucket_info->last_free > 0); --t->bucket_info->last_free; @@ -155,7 +125,6 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const uint64_t collided_hash = HASH_KEY(*t, GET_ENTRY(*t, bucket->index)); if (collided_hash != hash) { // Collided with a mid-chain entry - hdebug("Hit a mid-chain entry at bucket ", hash, " (chain starting at ", collided_hash, ")\n"); // Find chain predecessor uint64_t predecessor = collided_hash; while (buckets[predecessor].next_bucket != hash) @@ -169,19 +138,15 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const bucket->index = index; bucket->next_bucket = END_OF_CHAIN; } else { // Collided with the start of a chain, put the new entry in chain position #2 - hdebug("Hit start of a chain\n"); buckets[t->bucket_info->last_free] = (bucket_t){.occupied = 1, .index = index, .next_bucket = bucket->next_bucket}; bucket->next_bucket = t->bucket_info->last_free; } - hshow(t); } static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) { if (unlikely(new_capacity > TABLE_MAX_BUCKETS)) fail("Table has exceeded the maximum table size (2^31) and cannot grow further!"); - hdebug("About to resize from ", t->bucket_info ? (int32_t)t->bucket_info->count : 0, " to ", new_capacity, "\n"); - hshow(t); size_t alloc_size = sizeof(bucket_info_t) + sizeof(bucket_t[new_capacity]); t->bucket_info = GC_MALLOC_ATOMIC(alloc_size); memset(t->bucket_info->buckets, 0, sizeof(bucket_t[new_capacity])); @@ -189,12 +154,8 @@ static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const Type t->bucket_info->last_free = new_capacity - 1; // Rehash: for (int64_t i = 0; i < (int64_t)Table$length(*t); i++) { - hdebug("Rehashing ", i, "\n"); Table$set_bucket(t, GET_ENTRY(*t, i), i, type); } - - hshow(t); - hdebug("Finished resizing\n"); } // Return address of value @@ -206,7 +167,6 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (!t || !key) return NULL; - hshow(t); t->hash = 0; @@ -295,12 +255,10 @@ void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) { // maybe update lastfree_index1 to removed bucket's index uint64_t hash = HASH_KEY(*t, key); - hdebug("Removing key with hash ", hash, "\n"); bucket_t *bucket, *prev = NULL; for (uint64_t i = hash; t->bucket_info->buckets[i].occupied; i = t->bucket_info->buckets[i].next_bucket) { if (EQUAL_KEYS(GET_ENTRY(*t, t->bucket_info->buckets[i].index), key)) { bucket = &t->bucket_info->buckets[i]; - hdebug("Found key to delete in bucket ", i, "\n"); goto found_it; } if (t->bucket_info->buckets[i].next_bucket == END_OF_CHAIN) return; @@ -319,8 +277,6 @@ found_it:; // instead of O(N) int64_t last_entry = (int64_t)t->entries.length - 1; if (bucket->index != last_entry) { - hdebug("Removing key/value from the middle of the entries list\n"); - // Find the bucket that points to the last entry's index: uint64_t i = HASH_KEY(*t, GET_ENTRY(*t, last_entry)); while (t->bucket_info->buckets[i].index != last_entry) @@ -341,22 +297,17 @@ found_it:; int64_t bucket_to_clear; if (prev) { // Middle (or end) of a chain - hdebug("Removing from middle of a chain\n"); bucket_to_clear = (bucket - t->bucket_info->buckets); prev->next_bucket = bucket->next_bucket; } else if (bucket->next_bucket != END_OF_CHAIN) { // Start of a chain - hdebug("Removing from start of a chain\n"); bucket_to_clear = bucket->next_bucket; *bucket = t->bucket_info->buckets[bucket_to_clear]; } else { // Empty chain - hdebug("Removing from empty chain\n"); bucket_to_clear = (bucket - t->bucket_info->buckets); } t->bucket_info->buckets[bucket_to_clear] = (bucket_t){0}; if (bucket_to_clear > t->bucket_info->last_free) t->bucket_info->last_free = bucket_to_clear; - - hshow(t); } CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { @@ -537,9 +488,9 @@ Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { __typeof(type->TableInfo) table = type->TableInfo; if (!t) { - return table.value->size > 0 ? Texts("{", generic_as_text(NULL, false, table.key), ":", - generic_as_text(NULL, false, table.value), "}") - : Texts("{", generic_as_text(NULL, false, table.key), "}"); + return table.value->size > 0 ? Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text(":"), + generic_as_text(NULL, false, table.value), Text("}")) + : Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text("}")); } int64_t val_off = (int64_t)value_offset(type); diff --git a/src/stdlib/tables.h b/src/stdlib/tables.h index cf1c3625..93da465e 100644 --- a/src/stdlib/tables.h +++ b/src/stdlib/tables.h @@ -47,8 +47,8 @@ void *Table$get(Table_t t, const void *key, const TypeInfo_t *type); val_t *value = Table$get(t, &key, info); \ if (unlikely(value == NULL)) \ fail_source(__SOURCE_FILE__, start, end, \ - "This key was not found in the table: ", generic_as_text(&key, false, info->TableInfo.key), \ - "\n"); \ + Text$concat(Text("This key was not found in the table: "), \ + generic_as_text(&key, false, info->TableInfo.key), Text("\n"))); \ *value; \ }) #define Table$get_or_setdefault(table_expr, key_t, val_t, key_expr, default_expr, info_expr) \ diff --git a/src/stdlib/text.c b/src/stdlib/text.c index b4b27fed..411f3546 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -606,8 +606,8 @@ Text_t Text$middle_pad(Text_t text, Int_t width, Text_t padding, Text_t language if (padding.length == 0) fail("Cannot pad with an empty text!"); int64_t needed = Int64$from_int(width, false) - Int64$from_int(Text$width(text, language), false); - return Texts(Text$repeat_to_width(padding, needed / 2, language), text, - Text$repeat_to_width(padding, (needed + 1) / 2, language)); + return Text$concat(Text$repeat_to_width(padding, needed / 2, language), text, + Text$repeat_to_width(padding, (needed + 1) / 2, language)); } public @@ -1506,8 +1506,8 @@ Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) { Text_t ret = Text$escaped(text, colorize, quotation_mark); if (!(Text$equal_values(quotation_mark, Text("\"")) || Text$equal_values(quotation_mark, Text("'")) || Text$equal_values(quotation_mark, Text("`")))) - ret = Texts("$", quotation_mark, ret, quotation_mark); - else ret = Texts(quotation_mark, ret, quotation_mark); + ret = Text$concat(Text("$"), quotation_mark, ret, quotation_mark); + else ret = Text$concat(quotation_mark, ret, quotation_mark); return ret; } @@ -1803,11 +1803,11 @@ Int_t Text$memory_size(Text_t text) { public Text_t Text$layout(Text_t text) { switch (text.tag) { - case TEXT_ASCII: return Texts(Text("ASCII("), Int64$value_as_text(text.length), Text(")")); - case TEXT_GRAPHEMES: return Texts(Text("Graphemes("), Int64$value_as_text(text.length), Text(")")); - case TEXT_BLOB: return Texts(Text("Blob("), Int64$value_as_text(text.length), Text(")")); + case TEXT_ASCII: return Text$concat(Text("ASCII("), Int64$value_as_text(text.length), Text(")")); + case TEXT_GRAPHEMES: return Text$concat(Text("Graphemes("), Int64$value_as_text(text.length), Text(")")); + case TEXT_BLOB: return Text$concat(Text("Blob("), Int64$value_as_text(text.length), Text(")")); case TEXT_CONCAT: - return Texts(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); + return Text$concat(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); default: errx(1, "Invalid text tag: %d", text.tag); } } diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 9ad7441c..776ea8ec 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -63,8 +63,9 @@ OptionalText_t Text$cluster(Text_t text, Int_t index_int); Int_t index = index_expr; \ OptionalText_t cluster = Text$cluster(text, index); \ if (unlikely(cluster.tag == TEXT_NONE)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid text index: ", index, " (text has length ", \ - (int64_t)text.length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid text index: "), convert_to_text(index), Text(" (text has length "), \ + convert_to_text((int64_t)text.length), Text(")\n"))); \ cluster; \ }) OptionalText_t Text$from_str(const char *str); @@ -69,12 +69,6 @@ static const char *paths_str(List_t paths) { return Text$as_c_string(result); } -#ifdef __APPLE__ -#define SHARED_SUFFIX ".dylib" -#else -#define SHARED_SUFFIX ".so" -#endif - static OptionalBool_t verbose = false, quiet = false, show_version = false, show_prefix = false, clean_build = false, source_mapping = true, should_install = false; @@ -151,7 +145,6 @@ int main(int argc, char *argv[]) { if (stat(compiler_path, &compiler_stat) != 0) err(1, "Could not find age of compiler"); #endif - ldlibs = Texts(ldlibs, " -ltomo@", TOMO_VERSION); #ifdef __OpenBSD__ ldlibs = Texts(ldlibs, Text(" -lexecinfo")); #endif @@ -268,7 +261,8 @@ int main(int argc, char *argv[]) { cflags = Texts(cflags, Text(" -Wno-parentheses-equality")); } - ldflags = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib' ", ldflags); + ldflags = + Texts("-Wl,-rpath,'", TOMO_PATH, "/lib' ", ldflags, " -ffunction-sections -fdata-sections -Wl,--gc-sections"); #ifdef __APPLE__ cflags = Texts(cflags, Text(" -I/opt/homebrew/include")); @@ -397,6 +391,32 @@ int main(int argc, char *argv[]) { run_files = normalize_tm_paths(run_files); + if (run_files.length == 0 && format_files.length == 0 && format_files_inplace.length == 0 && parse_files.length == 0 + && transpile_files.length == 0 && compile_objects.length == 0 && compile_executables.length == 0 + && run_files.length == 0 && uninstall_libraries.length == 0 && libraries.length == 0) { + Path_t path = Path$from_str(String("~/.local/tomo/state/tomo@", TOMO_VERSION, "/run.tm")); + path = Path$expand_home(path); + Path$create_directory(Path$parent(path), 0755, true); + if (!Path$exists(path)) { + Path$write(path, + Text("# This is a handy Tomo REPL-like runner\n" // + "# Normally you would run `tomo ./file.tm` to run a script\n" // + "# See `tomo --help` for full usage\n" // + "\n" // + "func main()\n" // + " # Put your code here:\n" // + " pass\n" // + "\n" // + "# Save and exit to run\n"), + 0644); + } + List$insert(&run_files, &path, I(0), sizeof(path)); + const char *editor = getenv("EDITOR"); + if (!editor || editor[0] == '\0') editor = "vim"; + int status = system(String(editor, " ", path)); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return 1; + } + // Compile runnable files in parallel, then execute in serial: for (int64_t i = 0; i < (int64_t)run_files.length; i++) { Path_t path = *(Path_t *)(run_files.data + i * run_files.stride); @@ -481,28 +501,16 @@ void build_library(Path_t lib_dir) { compile_files(env, tm_files, &object_files, &extra_ldlibs, COMPILE_OBJ); Text_t lib_name = get_library_name(lib_dir); - Path_t shared_lib = Path$child(lib_dir, Texts(Text("lib"), lib_name, Text(SHARED_SUFFIX))); - if (!is_stale_for_any(shared_lib, object_files, false)) { - if (verbose) whisper("Unchanged: ", shared_lib); - return; + Path_t archive = Path$child(lib_dir, Texts(Text("lib"), lib_name, ".a")); + if (is_stale_for_any(archive, object_files, false)) { + FILE *prog = run_cmd("ar -rcs '", archive, "' ", paths_str(object_files)); + if (!prog) print_err("Failed to run `ar`"); + int status = pclose(prog); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); + if (!quiet) print("Compiled static library:\t", Path$relative_to(archive, Path$current_dir())); + } else { + if (verbose) whisper("Unchanged: ", archive); } - - FILE *prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), -#ifdef __APPLE__ - " -Wl,-install_name,@rpath/'lib", lib_name, SHARED_SUFFIX, - "'" -#else - " -Wl,-soname,'lib", lib_name, SHARED_SUFFIX, - "'" -#endif - " -shared ", - paths_str(object_files), " -o '", shared_lib, "'"); - - if (!prog) print_err("Failed to run C compiler: ", cc); - int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); - - if (!quiet) print("Compiled library:\t", Path$relative_to(shared_lib, Path$current_dir())); } void install_library(Path_t lib_dir) { @@ -519,11 +527,11 @@ void install_library(Path_t lib_dir) { } // If we have `debugedit` on this system, use it to remap the debugging source information // to point to the installed version of the source file. Otherwise, fail silently. - if (verbose) whisper("Updating debug symbols for ", dest, "/lib", lib_name, SHARED_SUFFIX); + if (verbose) whisper("Updating debug symbols for ", dest, "/lib", lib_name, ".a"); int result = system(String(as_owner, "debugedit -b ", lib_dir, " -d '", dest, "'" " '", - dest, "/lib", lib_name, SHARED_SUFFIX, + dest, "/lib", lib_name, ".a", "' " ">/dev/null 2>/dev/null")); (void)result; @@ -689,9 +697,8 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l case USE_MODULE: { module_info_t mod = get_used_module_info(stmt_ast); const char *full_name = mod.version ? String(mod.name, "@", mod.version) : mod.name; - Text_t lib = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), - "' '", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), "/lib", - Text$from_str(full_name), SHARED_SUFFIX "'"); + Text_t lib = Texts(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), "/lib", + Text$from_str(full_name), ".a"); Table$set(to_link, &lib, NULL, Table$info(&Text$info, &Void$info)); List_t children = Path$glob( @@ -917,8 +924,35 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t Path_t runner_file = build_file(path, ".runner.c"); Path$write(runner_file, program, 0644); - FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", - list_text(extra_ldlibs), " ", paths_str(object_files), " ", runner_file, " -o ", exe_path); + // .a archive files need to go later in the positional order: + List_t archives = EMPTY_LIST; + for (int64_t i = 0; i < (int64_t)extra_ldlibs.length;) { + Text_t *lib = (Text_t *)(extra_ldlibs.data + i * extra_ldlibs.stride); + if (Text$ends_with(*lib, Text(".a"), NULL)) { + List$insert(&archives, lib, I(0), sizeof(Text_t)); + List$remove_at(&extra_ldlibs, I(i + 1), I(1), sizeof(Text_t)); + } else { + i += 1; + } + } + + FILE *runner = run_cmd(cc, " ", + // C flags: + cflags, " -O", optimization, " ", + // Linker flags and dynamically linked shared libraries: + ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", + // Object files: + paths_str(object_files), " ", + // Input file: + runner_file, + // Statically linked archive files (must come after runner): + // Libraries are grouped to allow for circular dependencies among + // the libraries that are used. + " -Wl,--start-group ", list_text(archives), " -Wl,--end-group ", + // Tomo static library: + TOMO_PATH, "/lib/libtomo@", TOMO_VERSION, ".a", + // Output file: + " -o ", exe_path); if (show_codegen.length > 0) { FILE *out = run_cmd(show_codegen); diff --git a/src/typecheck.c b/src/typecheck.c index 1ac3943c..a073cb2e 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -1531,11 +1531,7 @@ PUREFUNC bool is_discardable(env_t *env, ast_t *ast) { case Metadata: return true; default: break; } - type_t *t = get_type(env, ast); - if (t->tag == StructType) { - return (Match(t, StructType)->fields == NULL); - } - return (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType); + return is_discardable_type(get_type(env, ast)); } type_t *get_arg_ast_type(env_t *env, arg_ast_t *arg) { diff --git a/src/types.c b/src/types.c index 46df5c64..1ccb7952 100644 --- a/src/types.c +++ b/src/types.c @@ -144,6 +144,13 @@ PUREFUNC type_t *value_type(type_t *t) { return t; } +PUREFUNC bool is_discardable_type(type_t *t) { + if (t->tag == StructType) { + return (Match(t, StructType)->fields == NULL); + } + return (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType); +} + type_t *type_or_type(type_t *a, type_t *b) { if (!a) return b; if (!b) return a; @@ -153,6 +160,8 @@ type_t *type_or_type(type_t *a, type_t *b) { return a->tag == OptionalType ? a : Type(OptionalType, a); if (a->tag == ReturnType && b->tag == ReturnType) return Type(ReturnType, .ret = type_or_type(Match(a, ReturnType)->ret, Match(b, ReturnType)->ret)); + if ((a->tag == VoidType && is_discardable_type(b)) || (is_discardable_type(a) && b->tag == VoidType)) + return Type(VoidType); if (is_incomplete_type(a) && type_eq(b, most_complete_type(a, b))) return b; if (is_incomplete_type(b) && type_eq(a, most_complete_type(a, b))) return a; diff --git a/src/types.h b/src/types.h index 66e6ba12..df5729ca 100644 --- a/src/types.h +++ b/src/types.h @@ -142,6 +142,7 @@ PUREFUNC bool type_eq(type_t *a, type_t *b); PUREFUNC bool type_is_a(type_t *t, type_t *req); type_t *type_or_type(type_t *a, type_t *b); type_t *value_type(type_t *a); +PUREFUNC bool is_discardable_type(type_t *t); typedef enum { NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, |
