From 4188d627c6869cb6b443ae3b6dece40486f4a039 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 17 Aug 2025 15:27:34 -0400 Subject: Bugfix: added support for Tomo identifiers that are C keywords --- CHANGES.md | 1 + src/compile.c | 10 ++++---- src/enums.c | 14 ++++++----- src/environment.c | 25 +------------------ src/environment.h | 2 -- src/naming.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/naming.h | 12 ++++++++++ src/structs.c | 7 +++--- src/tomo.c | 1 + src/typecheck.c | 1 + 10 files changed, 106 insertions(+), 39 deletions(-) create mode 100644 src/naming.c create mode 100644 src/naming.h diff --git a/CHANGES.md b/CHANGES.md index 2fd0c006..fade9357 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -43,6 +43,7 @@ C/assembly files or their `#include`s. - Memory offsets for enums with different member alignments were miscalculated. - Optional types with trailing padding were not correctly being detected as `none` + - Tomo identifiers that happened to coincide with C keywords were not allowed. ## v0.2 diff --git a/src/compile.c b/src/compile.c index 2b714acf..1648363a 100644 --- a/src/compile.c +++ b/src/compile.c @@ -11,6 +11,7 @@ #include "enums.h" #include "environment.h" #include "modules.h" +#include "naming.h" #include "parse.h" #include "stdlib/integers.h" #include "stdlib/nums.h" @@ -1120,7 +1121,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) const char *var_name = Match(args->value, Var)->name; if (!streq(var_name, "_")) { Text_t var = Texts("_$", var_name); - code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.", clause_tag_name, ";\n"); + code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.", valid_c_name(clause_tag_name), ";\n"); scope = fresh_scope(scope); set_binding(scope, Match(args->value, Var)->name, tag_type, EMPTY_TEXT); } @@ -1138,7 +1139,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) const char *var_name = Match(arg->value, Var)->name; if (!streq(var_name, "_")) { Text_t var = Texts("_$", var_name); - code = Texts(code, compile_declaration(field->type, var), " = _when_subject.", clause_tag_name, ".", field->name, ";\n"); + code = Texts(code, compile_declaration(field->type, var), " = _when_subject.", + valid_c_name(clause_tag_name), ".", valid_c_name(field->name), ";\n"); set_binding(scope, Match(arg->value, Var)->name, field->type, var); } field = field->next; @@ -3840,10 +3842,10 @@ Text_t compile(env_t *env, ast_t *ast) if (streq(field->name, f->field)) { if (fielded_t->tag == PointerType) { Text_t fielded = compile_to_pointer_depth(env, f->fielded, 1, false); - return Texts("(", fielded, ")->", f->field); + return Texts("(", fielded, ")->", valid_c_name(f->field)); } else { Text_t fielded = compile(env, f->fielded); - return Texts("(", fielded, ").", f->field); + return Texts("(", fielded, ").", valid_c_name(f->field)); } } } diff --git a/src/enums.c b/src/enums.c index c6bd9560..113eb91d 100644 --- a/src/enums.c +++ b/src/enums.c @@ -4,12 +4,13 @@ #include #include "ast.h" -#include "stdlib/text.h" #include "compile.h" -#include "structs.h" #include "environment.h" -#include "typecheck.h" +#include "naming.h" +#include "stdlib/text.h" #include "stdlib/util.h" +#include "structs.h" +#include "typecheck.h" Text_t compile_enum_typeinfo(env_t *env, ast_t *ast) { @@ -69,7 +70,7 @@ Text_t compile_enum_constructors(env_t *env, ast_t *ast) Text_t tagged_name = namespace_name(env, env->namespace, Texts(def->name, "$tagged$", tag->name)); Text_t tag_name = namespace_name(env, env->namespace, Texts(def->name, "$tag$", tag->name)); Text_t constructor_impl = Texts("public inline ", type_name, " ", tagged_name, "(", arg_sig, ") { return (", - type_name, "){.$tag=", tag_name, ", .", tag->name, "={"); + type_name, "){.$tag=", tag_name, ", .", valid_c_name(tag->name), "={"); for (arg_ast_t *field = tag->fields; field; field = field->next) { constructor_impl = Texts(constructor_impl, "$", field->name); if (field->next) constructor_impl = Texts(constructor_impl, ", "); @@ -109,10 +110,11 @@ Text_t compile_enum_header(env_t *env, ast_t *ast) "union {\n"); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; - Text_t field_def = compile_struct_header(env, WrapAST(ast, StructDef, .name=Text$as_c_string(Texts(def->name, "$", tag->name)), .fields=tag->fields)); + Text_t field_def = compile_struct_header( + env, WrapAST(ast, StructDef, .name=Text$as_c_string(Texts(def->name, "$", tag->name)), .fields=tag->fields)); all_defs = Texts(all_defs, field_def); Text_t tag_type = namespace_name(env, env->namespace, Texts(def->name, "$", tag->name, "$$type")); - enum_def = Texts(enum_def, tag_type, " ", tag->name, ";\n"); + enum_def = Texts(enum_def, tag_type, " ", valid_c_name(tag->name), ";\n"); } enum_def = Texts(enum_def, "};\n};\n"); all_defs = Texts(all_defs, enum_def); diff --git a/src/environment.c b/src/environment.c index 3faad24d..f43e8daf 100644 --- a/src/environment.c +++ b/src/environment.c @@ -5,6 +5,7 @@ #include #include "environment.h" +#include "naming.h" #include "parse.h" #include "stdlib/datatypes.h" #include "stdlib/paths.h" @@ -543,30 +544,6 @@ env_t *global_env(bool source_mapping) return env; } -Text_t CONSTFUNC namespace_name(env_t *env, namespace_t *ns, Text_t name) -{ - for (; ns; ns = ns->parent) - name = Texts(ns->name, "$", name); - if (env->id_suffix.length > 0) - name = Texts(name, env->id_suffix); - return name; -} - -Text_t get_id_suffix(const char *filename) -{ - assert(filename); - Path_t path = Path$from_str(filename); - Path_t build_dir = Path$sibling(path, Text(".build")); - if (mkdir(Path$as_c_string(build_dir), 0755) != 0) { - if (!Path$is_directory(build_dir, true)) - err(1, "Could not make .build directory"); - } - Path_t id_file = Path$child(build_dir, Texts(Path$base_name(path), Text$from_str(".id"))); - OptionalText_t id = Path$read(id_file); - if (id.length < 0) err(1, "Could not read ID file: ", id_file); - return Texts(Text("$"), id); -} - env_t *load_module_env(env_t *env, ast_t *ast) { const char *name = ast->file->filename; diff --git a/src/environment.h b/src/environment.h index 18b749ed..eb5b55bc 100644 --- a/src/environment.h +++ b/src/environment.h @@ -60,8 +60,6 @@ typedef struct { env_t *global_env(bool source_mapping); env_t *load_module_env(env_t *env, ast_t *ast); -Text_t namespace_name(env_t *env, namespace_t *ns, Text_t name); -Text_t get_id_suffix(const char *filename); env_t *get_namespace_by_type(env_t *env, type_t *t); env_t *fresh_scope(env_t *env); env_t *for_scope(env_t *env, ast_t *ast); diff --git a/src/naming.c b/src/naming.c new file mode 100644 index 00000000..abe87569 --- /dev/null +++ b/src/naming.c @@ -0,0 +1,72 @@ +// Logic for converting user's Tomo names into valid C identifiers + +#include +#include +#include + +#include "environment.h" +#include "stdlib/paths.h" +#include "stdlib/print.h" +#include "stdlib/text.h" + +static const char *c_keywords[] = { // Maintain sorted order: + "_Alignas", "_Alignof", "_Atomic", "_BitInt", "_Bool", "_Complex", "_Decimal128", "_Decimal32", "_Decimal64", "_Generic", + "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local", + "alignas", "alignof", "auto", "bool", "break", "case", "char", "const", "constexpr", "continue", "default", "do", "double", + "else", "enum", "extern", "false", "float", "for", "goto", "if", "inline", "int", "long", "nullptr", "register", "restrict", + "return", "short", "signed", "sizeof", "static", "static_assert", "struct", "switch", "thread_local", "true", "typedef", + "typeof", "typeof_unqual", "union", "unsigned", "void", "volatile", "while", +}; + +static CONSTFUNC bool is_keyword(const char *word, size_t len) { + int64_t lo = 0, hi = sizeof(c_keywords)/sizeof(c_keywords[0])-1; + while (lo <= hi) { + int64_t mid = (lo + hi) / 2; + int32_t cmp = strncmp(word, c_keywords[mid], len); + if (cmp == 0) + return true; + else if (cmp > 0) + lo = mid + 1; + else if (cmp < 0) + hi = mid - 1; + } + return false; +} + +public Text_t valid_c_name(const char *name) +{ + size_t len = strlen(name); + size_t trailing_underscores = 0; + while (trailing_underscores < len && name[len-1-trailing_underscores] == '_') + trailing_underscores += 1; + if (is_keyword(name, len-trailing_underscores)) { + return Texts(Text$from_str(name), Text("_")); + } + return Text$from_str(name); +} + +public Text_t CONSTFUNC namespace_name(env_t *env, namespace_t *ns, Text_t name) +{ + for (; ns; ns = ns->parent) + name = Texts(ns->name, "$", name); + if (env->id_suffix.length > 0) + name = Texts(name, env->id_suffix); + return name; +} + +public Text_t get_id_suffix(const char *filename) +{ + assert(filename); + Path_t path = Path$from_str(filename); + Path_t build_dir = Path$sibling(path, Text(".build")); + if (mkdir(Path$as_c_string(build_dir), 0755) != 0) { + if (!Path$is_directory(build_dir, true)) + err(1, "Could not make .build directory"); + } + Path_t id_file = Path$child(build_dir, Texts(Path$base_name(path), Text$from_str(".id"))); + OptionalText_t id = Path$read(id_file); + if (id.length < 0) err(1, "Could not read ID file: ", id_file); + return Texts(Text("$"), id); +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/naming.h b/src/naming.h new file mode 100644 index 00000000..30d4d061 --- /dev/null +++ b/src/naming.h @@ -0,0 +1,12 @@ +#pragma once + +// Compilation environments + +#include "environment.h" +#include "stdlib/datatypes.h" + +Text_t valid_c_name(const char *name); +Text_t namespace_name(env_t *env, namespace_t *ns, Text_t name); +Text_t get_id_suffix(const char *filename); + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/structs.c b/src/structs.c index 3e51f81b..873300c5 100644 --- a/src/structs.c +++ b/src/structs.c @@ -4,11 +4,12 @@ #include #include "ast.h" -#include "stdlib/text.h" #include "compile.h" #include "environment.h" -#include "typecheck.h" +#include "naming.h" +#include "stdlib/text.h" #include "stdlib/util.h" +#include "typecheck.h" Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, bool is_opaque) { @@ -60,7 +61,7 @@ Text_t compile_struct_header(env_t *env, ast_t *ast) else if (field->value) code_err(field->value, "This is an opaque type, so it can't be used as a struct field type"); } - fields = Texts(fields, compile_declaration(field_t, Text$from_str(field->name)), + fields = Texts(fields, compile_declaration(field_t, valid_c_name(field->name)), field_t->tag == BoolType ? Text(":1") : EMPTY_TEXT, ";\n"); } Text_t struct_code = def->external ? EMPTY_TEXT : Texts(type_code, " {\n", fields, "};\n"); diff --git a/src/tomo.c b/src/tomo.c index e105cb7a..e8012f9e 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -15,6 +15,7 @@ #include "ast.h" #include "compile.h" #include "modules.h" +#include "naming.h" #include "parse.h" #include "stdlib/bools.h" #include "stdlib/bytes.h" diff --git a/src/typecheck.c b/src/typecheck.c index d151c511..ba30931e 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -11,6 +11,7 @@ #include "ast.h" #include "environment.h" #include "modules.h" +#include "naming.h" #include "parse.h" #include "stdlib/paths.h" #include "stdlib/tables.h" -- cgit v1.2.3