From 058a028aef6f056a31d1e9a09fa83d498c553b78 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 19 Feb 2025 18:50:50 -0500 Subject: [PATCH] Switch to langs using constructors --- compile.c | 7 ++++++- docs/langs.md | 8 ++++---- environment.c | 33 ++++++++++++++++++++++++++------- environment.h | 1 + test/lang.tm | 4 ++-- typecheck.c | 9 ++++++++- 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/compile.c b/compile.c index 3da8397..a2c638e 100644 --- a/compile.c +++ b/compile.c @@ -4082,6 +4082,8 @@ CORD compile_function(env_t *env, ast_t *ast, CORD *staticdefs) auto fndef = Match(ast, FunctionDef); bool is_private = Match(fndef->name, Var)->name[0] == '_'; CORD name = compile(env, fndef->name); + if (env->namespace && env->namespace->parent && env->namespace->name && streq(Match(fndef->name, Var)->name, env->namespace->name)) + name = CORD_asprintf("%r$%ld", name, get_line_number(ast->file, ast->start)); type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType); CORD arg_signature = "("; @@ -4485,7 +4487,10 @@ CORD compile_statement_namespace_header(env_t *env, ast_t *ast) type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType); CORD ret_type_code = compile_type(ret_t); - return CORD_all(ret_type_code, " ", namespace_prefix(env, env->namespace), decl_name, arg_signature, ";\n"); + CORD name = CORD_all(namespace_prefix(env, env->namespace), decl_name); + if (env->namespace && env->namespace->parent && env->namespace->name && streq(decl_name, env->namespace->name)) + name = CORD_asprintf("%r$%ld", name, get_line_number(ast->file, ast->start)); + return CORD_all(ret_type_code, " ", name, arg_signature, ";\n"); } default: return CORD_EMPTY; } diff --git a/docs/langs.md b/docs/langs.md index 37141bd..96c026f 100644 --- a/docs/langs.md +++ b/docs/langs.md @@ -10,7 +10,7 @@ where a different type of string is needed. ```tomo lang HTML: - func escape(t:Text -> HTML): + func HTML(t:Text -> HTML): t = t:replace_all({ $/&/ = "&", $/ Sh): + func Sh(text:Text -> Sh): return Sh.without_escaping("'" ++ text:replace($/'/, "''") ++ "'") func execute(sh:Sh -> Text): diff --git a/environment.c b/environment.c index 1354999..3cc5392 100644 --- a/environment.c +++ b/environment.c @@ -463,6 +463,27 @@ env_t *new_compilation_unit(CORD libname) } } + // Conversion constructors: +#define ADD_CONSTRUCTOR(ns_env, identifier, typestr) Array$insert(&ns_env->namespace->constructors, ((binding_t[1]){{.code=identifier, .type=Match(parse_type_string(ns_env, typestr), ClosureType)->fn}}), I(0), sizeof(binding_t)) + { + env_t *ns_env = namespace_env(env, "Pattern"); + ADD_CONSTRUCTOR(ns_env, "Pattern$escape_text", "func(text:Text -> Pattern)"); + ADD_CONSTRUCTOR(ns_env, "Int$value_as_text", "func(i:Int -> Pattern)"); + } + { + env_t *ns_env = namespace_env(env, "Path"); + ADD_CONSTRUCTOR(ns_env, "Path$escape_text", "func(text:Text -> Path)"); + ADD_CONSTRUCTOR(ns_env, "Path$escape_path", "func(path:Path -> Path)"); + ADD_CONSTRUCTOR(ns_env, "Int$value_as_text", "func(i:Int -> Path)"); + } + { + env_t *ns_env = namespace_env(env, "Shell"); + ADD_CONSTRUCTOR(ns_env, "Shell$escape_text", "func(text:Text -> Shell)"); + ADD_CONSTRUCTOR(ns_env, "Shell$escape_text_array", "func(texts:[Text] -> Shell)"); + ADD_CONSTRUCTOR(ns_env, "Int$value_as_text", "func(i:Int -> Shell)"); + } + + set_binding(namespace_env(env, "Shell"), "without_escaping", Type(FunctionType, .args=new(arg_t, .name="text", .type=TEXT_TYPE), .ret=Type(TextType, .lang="Shell", .env=namespace_env(env, "Shell"))), @@ -675,18 +696,16 @@ PUREFUNC binding_t *get_lang_escape_function(env_t *env, const char *lang_name, binding_t *typeinfo = get_binding(env, lang_name); assert(typeinfo && typeinfo->type->tag == TypeInfoType); env_t *lang_env = Match(typeinfo->type, TypeInfoType)->env; - for (int64_t i = 1; i <= Table$length(*lang_env->locals); i++) { - struct {const char *name; binding_t *b; } *entry = Table$entry(*lang_env->locals, i); - if (entry->b->type->tag != FunctionType) continue; - if (!(streq(entry->name, "escape") || strncmp(entry->name, "escape_", strlen("escape_")) == 0)) - continue; - auto fn = Match(entry->b->type, FunctionType); + Array_t constructors = lang_env->namespace->constructors; + for (int64_t i = 0; i < constructors.length; i++) { + binding_t *b = constructors.data + i*constructors.stride; + auto fn = Match(b->type, FunctionType); if (!fn->args || fn->args->next) continue; if (fn->ret->tag != TextType || !streq(Match(fn->ret, TextType)->lang, lang_name)) continue; if (!can_promote(type_to_escape, fn->args->type)) continue; - return entry->b; + return b; } return NULL; } diff --git a/environment.h b/environment.h index e32e7bd..97a6647 100644 --- a/environment.h +++ b/environment.h @@ -31,6 +31,7 @@ typedef struct loop_ctx_s { typedef struct namespace_s { const char *name; + Array_t constructors; struct namespace_s *parent; } namespace_t; diff --git a/test/lang.tm b/test/lang.tm index c0add87..77fd795 100644 --- a/test/lang.tm +++ b/test/lang.tm @@ -1,6 +1,6 @@ lang HTML: HEADER := $HTML"" - func escape(t:Text->HTML): + func HTML(t:Text->HTML): t = t:replace_all({ $/&/="&", $/HTML): + func HTML(i:Int->HTML): return HTML.without_escaping("$i") func paragraph(content:HTML->HTML): diff --git a/typecheck.c b/typecheck.c index 7e63396..cbdbc8c 100644 --- a/typecheck.c +++ b/typecheck.c @@ -298,9 +298,16 @@ void bind_statement(env_t *env, ast_t *statement) case FunctionDef: { auto def = Match(statement, FunctionDef); const char *name = Match(def->name, Var)->name; + type_t *type = get_function_def_type(env, statement); + if (env->namespace && env->namespace->parent && env->namespace->name && streq(name, env->namespace->name)) { + CORD code = CORD_asprintf("%r%ld", namespace_prefix(env, env->namespace), get_line_number(statement->file, statement->start)); + binding_t binding = {.type=type, .code=code}; + Array$insert(&env->namespace->constructors, &binding, I(0), sizeof(binding)); + break; + } + if (get_binding(env, name)) code_err(def->name, "A %T called '%s' has already been defined", get_binding(env, name)->type, name); - type_t *type = get_function_def_type(env, statement); CORD code = CORD_all(namespace_prefix(env, env->namespace), name); set_binding(env, name, type, code); break;