aboutsummaryrefslogtreecommitdiff
path: root/src/compile
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-09-06 14:47:45 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-09-06 14:47:45 -0400
commita8316252db95e3d77f9f0e9beb89cfcb4573d5b1 (patch)
treee5905bce9611e35ccb2f84481232fca0e657ff42 /src/compile
parenta0ac652cd1eebdc42425b34f1685f8cb20cb4eea (diff)
parent73246764f88f6f652316ee0c138a990d836698a7 (diff)
Merge branch 'main' into simplified-quotes
Diffstat (limited to 'src/compile')
-rw-r--r--src/compile/assertions.c35
-rw-r--r--src/compile/assignments.c7
-rw-r--r--src/compile/binops.c2
-rw-r--r--src/compile/comparisons.c14
-rw-r--r--src/compile/conditionals.c2
-rw-r--r--src/compile/doctests.c17
-rw-r--r--src/compile/enums.c20
-rw-r--r--src/compile/expressions.c5
-rw-r--r--src/compile/files.c49
-rw-r--r--src/compile/functions.c44
-rw-r--r--src/compile/headers.c6
-rw-r--r--src/compile/indexing.c4
-rw-r--r--src/compile/lists.c2
-rw-r--r--src/compile/loops.c4
-rw-r--r--src/compile/optionals.c11
-rw-r--r--src/compile/promotions.c12
-rw-r--r--src/compile/reductions.c7
-rw-r--r--src/compile/sets.c2
-rw-r--r--src/compile/statements.c81
-rw-r--r--src/compile/structs.c9
-rw-r--r--src/compile/tables.c2
-rw-r--r--src/compile/types.c4
22 files changed, 164 insertions, 175 deletions
diff --git a/src/compile/assertions.c b/src/compile/assertions.c
index ce9abcbc..0f1d27b6 100644
--- a/src/compile/assertions.c
+++ b/src/compile/assertions.c
@@ -53,27 +53,26 @@ Text_t compile_assertion(env_t *env, ast_t *ast) {
ast_t *var_comparison = new (ast_t, .file = expr->file, .start = expr->start, .end = expr->end,
.tag = expr->tag, .__data.Equals = {.lhs = lhs_var, .rhs = rhs_var});
int64_t line = get_line_number(ast->file, ast->start);
- return Texts("{ // assertion\n", compile_declaration(operand_t, Text("_lhs")), " = ",
- compile_to_type(env, cmp.lhs, operand_t), ";\n", "\n#line ", String(line), "\n",
- compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t),
- ";\n", "\n#line ", String(line), "\n", "if (!(", compile_condition(env, var_comparison), "))\n",
- "#line ", String(line), "\n",
- Texts("fail_source(", quoted_str(ast->file->filename), ", ",
- String((int64_t)(expr->start - expr->file->text)), ", ",
- String((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"),
- "}\n");
+ return Texts(
+ "{ // assertion\n", compile_declaration(operand_t, Text("_lhs")), " = ",
+ compile_to_type(env, cmp.lhs, operand_t), ";\n", "\n#line ", line, "\n",
+ 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"),
+ "}\n");
}
default: {
int64_t line = get_line_number(ast->file, ast->start);
- return Texts("if (!(", compile_condition(env, expr), "))\n", "#line ", String(line), "\n", "fail_source(",
- quoted_str(ast->file->filename), ", ", String((int64_t)(expr->start - expr->file->text)), ", ",
- String((int64_t)(expr->end - expr->file->text)), ", ",
+ 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!\""),
");\n");
diff --git a/src/compile/assignments.c b/src/compile/assignments.c
index ab28b972..3cb60fd5 100644
--- a/src/compile/assignments.c
+++ b/src/compile/assignments.c
@@ -119,12 +119,12 @@ Text_t compile_assignment_statement(env_t *env, ast_t *ast) {
"stack memory.");
env_t *val_env = with_enum_scope(env, lhs_t);
Text_t val = compile_to_type(val_env, value->ast, lhs_t);
- code = Texts(code, compile_type(lhs_t), " $", String(i), " = ", val, ";\n");
+ code = Texts(code, compile_type(lhs_t), " $", i, " = ", val, ";\n");
i += 1;
}
i = 1;
for (ast_list_t *target = assign->targets; target; target = target->next) {
- code = Texts(code, compile_assignment(env, target->ast, Texts("$", String(i))), ";\n");
+ code = Texts(code, compile_assignment(env, target->ast, Texts("$", i)), ";\n");
i += 1;
}
return Texts(code, "\n}");
@@ -171,8 +171,7 @@ Text_t compile_lvalue(env_t *env, ast_t *ast) {
")");
} else {
return Texts("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", index_code, ", ",
- String((int)(ast->start - ast->file->text)), ", ",
- String((int)(ast->end - ast->file->text)), ")");
+ (int64_t)(ast->start - ast->file->text), ", ", (int64_t)(ast->end - ast->file->text), ")");
}
} else if (container_t->tag == TableType) {
DeclareMatch(table_type, container_t, TableType);
diff --git a/src/compile/binops.c b/src/compile/binops.c
index 87fd2c7a..ed4aaeba 100644
--- a/src/compile/binops.c
+++ b/src/compile/binops.c
@@ -67,7 +67,7 @@ Text_t compile_binary_op(env_t *env, ast_t *ast) {
}
}
} else if ((ast->tag == Divide || ast->tag == Mod || ast->tag == Mod1) && is_numeric_type(rhs_t)) {
- b = get_namespace_binding(env, binop.lhs, binop_method_name(ast->tag));
+ b = get_namespace_binding(env, binop.lhs, binop_info[ast->tag].method_name);
if (b && b->type->tag == FunctionType) {
DeclareMatch(fn, b->type, FunctionType);
if (type_eq(fn->ret, lhs_t)) {
diff --git a/src/compile/comparisons.c b/src/compile/comparisons.c
index d73664de..d7531261 100644
--- a/src/compile/comparisons.c
+++ b/src/compile/comparisons.c
@@ -7,6 +7,18 @@
#include "../typecheck.h"
#include "compilation.h"
+static CONSTFUNC const char *comparison_operator(ast_e tag) {
+ switch (tag) {
+ case Equals: return "==";
+ case NotEquals: return "!=";
+ case LessThan: return "<";
+ case LessThanOrEquals: return "<=";
+ case GreaterThan: return ">";
+ case GreaterThanOrEquals: return ">=";
+ default: return NULL;
+ }
+}
+
Text_t compile_comparison(env_t *env, ast_t *ast) {
switch (ast->tag) {
@@ -75,7 +87,7 @@ Text_t compile_comparison(env_t *env, ast_t *ast) {
if (ast->tag == Compare)
return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ")");
- const char *op = binop_operator(ast->tag);
+ const char *op = comparison_operator(ast->tag);
switch (operand_t->tag) {
case BigIntType: return Texts("(Int$compare_value(", lhs, ", ", rhs, ") ", op, " 0)");
case BoolType:
diff --git a/src/compile/conditionals.c b/src/compile/conditionals.c
index 23d2a177..f9dfa751 100644
--- a/src/compile/conditionals.c
+++ b/src/compile/conditionals.c
@@ -59,7 +59,7 @@ Text_t compile_if_statement(env_t *env, ast_t *ast) {
code = Texts(code, compile_block(nonnull_scope, if_->body));
if (if_->else_body) {
- Text_t label = Texts("_falsey_", String((int64_t)(ast->start - ast->file->text)));
+ Text_t label = Texts("_falsey_", (int64_t)(ast->start - ast->file->text));
code = Texts(code, "else goto ", label,
";\n"
"} else {\n",
diff --git a/src/compile/doctests.c b/src/compile/doctests.c
index 872684ed..20081ae7 100644
--- a/src/compile/doctests.c
+++ b/src/compile/doctests.c
@@ -4,7 +4,6 @@
#include "../config.h"
#include "../environment.h"
#include "../stdlib/datatypes.h"
-#include "../stdlib/print.h"
#include "../stdlib/text.h"
#include "../stdlib/util.h"
#include "../typecheck.h"
@@ -69,12 +68,12 @@ Text_t compile_doctest(env_t *env, ast_t *ast) {
if (target == assign->targets) expr_t = lhs_t;
env_t *val_scope = with_enum_scope(env, lhs_t);
Text_t val_code = compile_to_type(val_scope, value->ast, lhs_t);
- test_code = Texts(test_code, compile_type(lhs_t), " $", String(i), " = ", val_code, ";\n");
+ test_code = Texts(test_code, compile_type(lhs_t), " $", i, " = ", val_code, ";\n");
i += 1;
}
i = 1;
for (ast_list_t *target = assign->targets; target; target = target->next) {
- test_code = Texts(test_code, compile_assignment(env, target->ast, Texts("$", String(i))), ";\n");
+ test_code = Texts(test_code, compile_assignment(env, target->ast, Texts("$", i)), ";\n");
i += 1;
}
@@ -104,16 +103,16 @@ Text_t compile_doctest(env_t *env, ast_t *ast) {
if (test->expected) {
return Texts(setup, "test(", compile_type(expr_t), ", ", test_code, ", ",
compile_to_type(env, test->expected, expr_t), ", ", compile_type_info(expr_t), ", ",
- String((int64_t)(test->expr->start - test->expr->file->text)), ", ",
- String((int64_t)(test->expr->end - test->expr->file->text)), ");");
+ (int64_t)(test->expr->start - test->expr->file->text), ", ",
+ (int64_t)(test->expr->end - test->expr->file->text), ");");
} else {
if (expr_t->tag == VoidType || expr_t->tag == AbortType) {
return Texts(setup, "inspect_void(", test_code, ", ", compile_type_info(expr_t), ", ",
- String((int64_t)(test->expr->start - test->expr->file->text)), ", ",
- String((int64_t)(test->expr->end - test->expr->file->text)), ");");
+ (int64_t)(test->expr->start - test->expr->file->text), ", ",
+ (int64_t)(test->expr->end - test->expr->file->text), ");");
}
return Texts(setup, "inspect(", compile_type(expr_t), ", ", test_code, ", ", compile_type_info(expr_t), ", ",
- String((int64_t)(test->expr->start - test->expr->file->text)), ", ",
- String((int64_t)(test->expr->end - test->expr->file->text)), ");");
+ (int64_t)(test->expr->start - test->expr->file->text), ", ",
+ (int64_t)(test->expr->end - test->expr->file->text), ");");
}
}
diff --git a/src/compile/enums.c b/src/compile/enums.c
index d9c29f26..f5500831 100644
--- a/src/compile/enums.c
+++ b/src/compile/enums.c
@@ -30,12 +30,12 @@ Text_t compile_enum_typeinfo(env_t *env, ast_t *ast) {
type_t *t = Table$str_get(*env->types, def->name);
const char *metamethods = is_packed_data(t) ? "PackedDataEnum$metamethods" : "Enum$metamethods";
Text_t info = namespace_name(env, env->namespace, Texts(def->name, "$$info"));
- Text_t typeinfo = Texts("public const TypeInfo_t ", info, " = {", String((int64_t)type_size(t)), "u, ",
- String((int64_t)type_align(t)), "u, .metamethods=", metamethods,
- ", {.tag=EnumInfo, .EnumInfo={.name=\"", def->name,
- "\", "
- ".num_tags=",
- String((int64_t)num_tags), ", .tags=(NamedType_t[]){");
+ Text_t typeinfo =
+ Texts("public const TypeInfo_t ", info, " = {", (int64_t)type_size(t), "u, ", (int64_t)type_align(t),
+ "u, .metamethods=", metamethods, ", {.tag=EnumInfo, .EnumInfo={.name=\"", def->name,
+ "\", "
+ ".num_tags=",
+ (int64_t)num_tags, ", .tags=(NamedType_t[]){");
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
const char *tag_type_name = String(def->name, "$", tag->name);
@@ -144,10 +144,10 @@ Text_t compile_empty_enum(type_t *t) {
assert(tag);
assert(tag->type);
if (Match(tag->type, StructType)->fields)
- return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), ", .", tag->name, "=",
- compile_empty(tag->type), "})");
- else if (enum_has_fields(t)) return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), "})");
- else return Texts("((", compile_type(t), ")", String(tag->tag_value), ")");
+ return Texts("((", compile_type(t), "){.$tag=", tag->tag_value, ", .", tag->name, "=", compile_empty(tag->type),
+ "})");
+ else if (enum_has_fields(t)) return Texts("((", compile_type(t), "){.$tag=", tag->tag_value, "})");
+ else return Texts("((", compile_type(t), ")", tag->tag_value, ")");
}
public
diff --git a/src/compile/expressions.c b/src/compile/expressions.c
index 2320474a..544b7723 100644
--- a/src/compile/expressions.c
+++ b/src/compile/expressions.c
@@ -152,7 +152,7 @@ Text_t compile(env_t *env, ast_t *ast) {
ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key;
ast_t *lhs = ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs;
ast_t *rhs = ast->tag == Min ? Match(ast, Min)->rhs : Match(ast, Max)->rhs;
- const char *key_name = "$";
+ const char *key_name = ast->tag == Min ? "_min_" : "_max_";
if (key == NULL) key = FakeAST(Var, key_name);
env_t *expr_env = fresh_scope(env);
@@ -237,7 +237,8 @@ Text_t compile(env_t *env, ast_t *ast) {
case Index: return compile_indexing(env, ast);
case InlineCCode: {
type_t *t = get_type(env, ast);
- if (t->tag == VoidType) return Texts("{\n", compile_statement(env, ast), "\n}");
+ if (Match(ast, InlineCCode)->type_ast != NULL) return Texts("({", compile_statement(env, ast), "; })");
+ else if (t->tag == VoidType) return Texts("{\n", compile_statement(env, ast), "\n}");
else return compile_statement(env, ast);
}
case Use: code_err(ast, "Compiling 'use' as expression!");
diff --git a/src/compile/files.c b/src/compile/files.c
index 2b0ac4bf..4d6fb1a8 100644
--- a/src/compile/files.c
+++ b/src/compile/files.c
@@ -12,7 +12,16 @@
#include "../types.h"
#include "compilation.h"
-static void initialize_vars_and_statics(env_t *env, ast_t *ast) {
+static void initialize_vars_and_statics(env_t *env, ast_t *ast);
+static void initialize_namespace(env_t *env, const char *name, ast_t *namespace);
+static Text_t compile_top_level_code(env_t *env, ast_t *ast);
+static Text_t compile_namespace(env_t *env, const char *name, ast_t *namespace);
+
+void initialize_namespace(env_t *env, const char *name, ast_t *namespace) {
+ initialize_vars_and_statics(namespace_env(env, name), namespace);
+}
+
+void initialize_vars_and_statics(env_t *env, ast_t *ast) {
if (!ast) return;
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
@@ -34,17 +43,13 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) {
Texts(full_name, " = ", val_code, ",\n", initialized_name, " = true;\n")));
}
} else if (stmt->ast->tag == StructDef) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, StructDef)->name),
- Match(stmt->ast, StructDef)->namespace);
+ initialize_namespace(env, Match(stmt->ast, StructDef)->name, Match(stmt->ast, StructDef)->namespace);
} else if (stmt->ast->tag == EnumDef) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, EnumDef)->name),
- Match(stmt->ast, EnumDef)->namespace);
+ initialize_namespace(env, Match(stmt->ast, EnumDef)->name, Match(stmt->ast, EnumDef)->namespace);
} else if (stmt->ast->tag == LangDef) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, LangDef)->name),
- Match(stmt->ast, LangDef)->namespace);
+ initialize_namespace(env, Match(stmt->ast, LangDef)->name, Match(stmt->ast, LangDef)->namespace);
} else if (stmt->ast->tag == Extend) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, Extend)->name),
- Match(stmt->ast, Extend)->body);
+ initialize_namespace(env, Match(stmt->ast, Extend)->name, Match(stmt->ast, Extend)->body);
} else if (stmt->ast->tag == Use) {
continue;
} else {
@@ -54,7 +59,12 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) {
}
}
-static Text_t compile_top_level_code(env_t *env, ast_t *ast) {
+Text_t compile_namespace(env_t *env, const char *name, ast_t *namespace) {
+ env_t *ns_env = namespace_env(env, name);
+ return namespace ? compile_top_level_code(ns_env, namespace) : EMPTY_TEXT;
+}
+
+Text_t compile_top_level_code(env_t *env, ast_t *ast) {
if (!ast) return EMPTY_TEXT;
switch (ast->tag) {
@@ -95,15 +105,15 @@ static Text_t compile_top_level_code(env_t *env, ast_t *ast) {
return compile_function(env, name_code, ast, &env->code->staticdefs);
}
case ConvertDef: {
- type_t *type = get_function_def_type(env, ast);
- const char *name = get_type_name(Match(type, FunctionType)->ret);
+ type_t *type = get_function_return_type(env, ast);
+ const char *name = get_type_name(type);
if (!name)
code_err(ast,
"Conversions are only supported for text, struct, and enum "
"types, not ",
- type_to_str(Match(type, FunctionType)->ret));
+ type_to_str(type));
Text_t name_code =
- namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start))));
+ namespace_name(env, env->namespace, Texts(name, "$", get_line_number(ast->file, ast->start)));
return compile_function(env, name_code, ast, &env->code->staticdefs);
}
case StructDef: {
@@ -111,24 +121,21 @@ static Text_t compile_top_level_code(env_t *env, ast_t *ast) {
type_t *t = Table$str_get(*env->types, def->name);
assert(t && t->tag == StructType);
Text_t code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret, def->opaque);
- env_t *ns_env = namespace_env(env, def->name);
- return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT);
+ return Texts(code, compile_namespace(env, def->name, def->namespace));
}
case EnumDef: {
DeclareMatch(def, ast, EnumDef);
Text_t code = compile_enum_typeinfo(env, ast);
code = Texts(code, compile_enum_constructors(env, ast));
- env_t *ns_env = namespace_env(env, def->name);
- return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT);
+ return Texts(code, compile_namespace(env, def->name, def->namespace));
}
case LangDef: {
DeclareMatch(def, ast, LangDef);
Text_t code =
Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), " = {",
- String((int64_t)sizeof(Text_t)), ", ", String((int64_t)__alignof__(Text_t)),
+ (int64_t)sizeof(Text_t), ", ", (int64_t)__alignof__(Text_t),
", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n");
- env_t *ns_env = namespace_env(env, def->name);
- return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT);
+ return Texts(code, compile_namespace(env, def->name, def->namespace));
}
case Extend: {
DeclareMatch(extend, ast, Extend);
diff --git a/src/compile/functions.c b/src/compile/functions.c
index 01de26e3..f04a3b59 100644
--- a/src/compile/functions.c
+++ b/src/compile/functions.c
@@ -32,7 +32,7 @@ Text_t compile_function_declaration(env_t *env, ast_t *ast) {
if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code);
Text_t name = namespace_name(env, env->namespace, Text$from_str(decl_name));
if (env->namespace && env->namespace->parent && env->namespace->name && streq(decl_name, env->namespace->name))
- name = namespace_name(env, env->namespace, Text$from_str(String(get_line_number(ast->file, ast->start))));
+ name = namespace_name(env, env->namespace, Texts(get_line_number(ast->file, ast->start)));
return Texts(ret_type_code, " ", name, arg_signature, ";\n");
}
@@ -56,8 +56,7 @@ Text_t compile_convert_declaration(env_t *env, ast_t *ast) {
"Conversions are only supported for text, struct, and enum "
"types, not ",
type_to_str(ret_t));
- Text_t name_code =
- namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start))));
+ Text_t name_code = namespace_name(env, env->namespace, Texts(name, "$", get_line_number(ast->file, ast->start)));
return Texts(ret_type_code, " ", name_code, arg_signature, ";\n");
}
@@ -248,7 +247,7 @@ Text_t compile_function_call(env_t *env, ast_t *ast) {
public
Text_t compile_lambda(env_t *env, ast_t *ast) {
DeclareMatch(lambda, ast, Lambda);
- Text_t name = namespace_name(env, env->namespace, Texts("lambda$", String(lambda->id)));
+ Text_t name = namespace_name(env, env->namespace, Texts("lambda$", lambda->id));
env_t *body_scope = fresh_scope(env);
body_scope->deferred = NULL;
@@ -257,18 +256,7 @@ Text_t compile_lambda(env_t *env, ast_t *ast) {
set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
}
- type_t *ret_t = get_type(body_scope, lambda->body);
- if (ret_t->tag == ReturnType) ret_t = Match(ret_t, ReturnType)->ret;
-
- if (lambda->ret_type) {
- type_t *declared = parse_type_ast(env, lambda->ret_type);
- if (can_promote(ret_t, declared)) ret_t = declared;
- else
- code_err(ast, "This function was declared to return a value of type ", type_to_str(declared),
- ", but actually returns a value of type ", type_to_str(ret_t));
- }
-
- body_scope->fn_ret = ret_t;
+ body_scope->fn = ast;
Table_t closed_vars = get_closed_vars(env, lambda->args, ast);
if (Table$length(closed_vars) > 0) { // Create a typedef for the lambda's closure userdata
@@ -290,6 +278,7 @@ Text_t compile_lambda(env_t *env, ast_t *ast) {
env->code->local_typedefs = Texts(env->code->local_typedefs, def);
}
+ type_t *ret_t = get_function_return_type(env, ast);
Text_t code = Texts("static ", compile_type(ret_t), " ", name, "(");
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
type_t *arg_type = get_arg_ast_type(env, arg);
@@ -624,7 +613,7 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
bool is_private = false;
const char *function_name;
arg_ast_t *args;
- type_t *ret_t;
+ type_t *ret_t = get_function_return_type(env, ast);
ast_t *body;
ast_t *cache;
bool is_inline;
@@ -633,14 +622,12 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
function_name = Match(fndef->name, Var)->name;
is_private = function_name[0] == '_';
args = fndef->args;
- ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType);
body = fndef->body;
cache = fndef->cache;
is_inline = fndef->is_inline;
} else {
DeclareMatch(convertdef, ast, ConvertDef);
args = convertdef->args;
- ret_t = convertdef->ret_type ? parse_type_ast(env, convertdef->ret_type) : Type(VoidType);
function_name = get_type_name(ret_t);
if (!function_name)
code_err(ast,
@@ -690,7 +677,7 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
set_binding(body_scope, arg->name, arg_type, Texts("_$", arg->name));
}
- body_scope->fn_ret = ret_t;
+ body_scope->fn = ast;
type_t *body_type = get_type(body_scope, body);
if (ret_t->tag == AbortType) {
@@ -736,7 +723,7 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
// FIXME: this currently just deletes the first entry, but this
// should be more like a least-recently-used cache eviction policy
// or least-frequently-used
- pop_code = Texts("if (cache.entries.length > ", String(cache_size.value),
+ pop_code = Texts("if (cache.entries.length > ", cache_size.value,
") Table$remove(&cache, cache.entries.data + "
"cache.entries.stride*0, table_type);\n");
}
@@ -772,14 +759,13 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static
int64_t num_fields = used_names.entries.length;
const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods";
- Text_t args_typeinfo =
- Texts("((TypeInfo_t[1]){{.size=sizeof(args), "
- ".align=__alignof__(args), .metamethods=",
- metamethods,
- ", .tag=StructInfo, "
- ".StructInfo.name=\"FunctionArguments\", "
- ".StructInfo.num_fields=",
- String(num_fields), ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){");
+ Text_t args_typeinfo = Texts("((TypeInfo_t[1]){{.size=sizeof(args), "
+ ".align=__alignof__(args), .metamethods=",
+ metamethods,
+ ", .tag=StructInfo, "
+ ".StructInfo.name=\"FunctionArguments\", "
+ ".StructInfo.num_fields=",
+ num_fields, ", .StructInfo.fields=(NamedType_t[", num_fields, "]){");
Text_t args_type = Text("struct { ");
for (arg_t *f = fields; f; f = f->next) {
args_typeinfo = Texts(args_typeinfo, "{\"", f->name, "\", ", compile_type_info(f->type), "}");
diff --git a/src/compile/headers.c b/src/compile/headers.c
index 8c0863ee..6dc69f03 100644
--- a/src/compile/headers.c
+++ b/src/compile/headers.c
@@ -174,10 +174,10 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast)
module_info_t mod = get_module_info(ast);
glob_t tm_files;
const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
- if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE,
- NULL, &tm_files)
+ if (glob(String(TOMO_PATH, "/lib/tomo_" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL,
+ &tm_files)
!= 0) {
- if (!try_install_module(mod)) code_err(ast, "Could not find library");
+ if (!try_install_module(mod, true)) code_err(ast, "Could not find library");
}
Text_t includes = EMPTY_TEXT;
diff --git a/src/compile/indexing.c b/src/compile/indexing.c
index e99feeb2..39af1160 100644
--- a/src/compile/indexing.c
+++ b/src/compile/indexing.c
@@ -44,8 +44,8 @@ Text_t compile_indexing(env_t *env, ast_t *ast) {
return Texts("List_get_unchecked(", compile_type(item_type), ", ", list, ", ", index_code, ")");
else
return Texts("List_get(", compile_type(item_type), ", ", list, ", ", index_code, ", ",
- String((int64_t)(indexing->index->start - f->text)), ", ",
- String((int64_t)(indexing->index->end - f->text)), ")");
+ (int64_t)(indexing->index->start - f->text), ", ", (int64_t)(indexing->index->end - f->text),
+ ")");
} else if (container_t->tag == TableType) {
DeclareMatch(table_type, container_t, TableType);
if (indexing->unchecked) code_err(ast, "Table indexes cannot be unchecked");
diff --git a/src/compile/lists.c b/src/compile/lists.c
index 5df39863..d9d71278 100644
--- a/src/compile/lists.c
+++ b/src/compile/lists.c
@@ -33,7 +33,7 @@ Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) {
{
env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env;
if (is_incomplete_type(item_type)) code_err(ast, "This list's type can't be inferred!");
- Text_t code = Texts("TypedListN(", compile_type(item_type), ", ", String(n));
+ Text_t code = Texts("TypedListN(", compile_type(item_type), ", ", n);
for (ast_list_t *item = list->items; item; item = item->next) {
code = Texts(code, ", ", compile_to_type(scope, item->ast, item_type));
}
diff --git a/src/compile/loops.c b/src/compile/loops.c
index c742589a..332024f4 100644
--- a/src/compile/loops.c
+++ b/src/compile/loops.c
@@ -405,7 +405,7 @@ Text_t compile_skip(env_t *env, ast_t *ast) {
if (matched) {
if (ctx->skip_label.length == 0) {
static int64_t skip_label_count = 1;
- ctx->skip_label = Texts("skip_", String(skip_label_count));
+ ctx->skip_label = Texts("skip_", skip_label_count);
++skip_label_count;
}
Text_t code = EMPTY_TEXT;
@@ -431,7 +431,7 @@ Text_t compile_stop(env_t *env, ast_t *ast) {
if (matched) {
if (ctx->stop_label.length == 0) {
static int64_t stop_label_count = 1;
- ctx->stop_label = Texts("stop_", String(stop_label_count));
+ ctx->stop_label = Texts("stop_", stop_label_count);
++stop_label_count;
}
Text_t code = EMPTY_TEXT;
diff --git a/src/compile/optionals.c b/src/compile/optionals.c
index b3d94005..cd50b1bf 100644
--- a/src/compile/optionals.c
+++ b/src/compile/optionals.c
@@ -125,10 +125,9 @@ Text_t compile_non_optional(env_t *env, ast_t *ast) {
type_t *t = get_type(env, value);
Text_t value_code = compile(env, value);
int64_t line = get_line_number(ast->file, ast->start);
- return Texts("({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", "if unlikely (",
- check_none(t, Text("opt")), ")\n", "#line ", String(line), "\n", "fail_source(",
- quoted_str(ast->file->filename), ", ", String((int64_t)(value->start - value->file->text)), ", ",
- String((int64_t)(value->end - value->file->text)), ", ",
- "\"This was expected to be a value, but it's none\");\n", optional_into_nonnone(t, Text("opt")),
- "; })");
+ return Texts(
+ "({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", "if unlikely (",
+ check_none(t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->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", optional_into_nonnone(t, Text("opt")), "; })");
}
diff --git a/src/compile/promotions.c b/src/compile/promotions.c
index fcedce3f..6595e5aa 100644
--- a/src/compile/promotions.c
+++ b/src/compile/promotions.c
@@ -43,12 +43,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 ", String(line), "\n", "fail_source(",
- quoted_str(ast->file->filename), ", ", String((int64_t)(ast->start - ast->file->text)), ", ",
- String((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), ", ", "\"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/reductions.c b/src/compile/reductions.c
index e0477a9c..438e072b 100644
--- a/src/compile/reductions.c
+++ b/src/compile/reductions.c
@@ -12,6 +12,7 @@ public
Text_t compile_reduction(env_t *env, ast_t *ast) {
DeclareMatch(reduction, ast, Reduction);
ast_e op = reduction->op;
+ const char *op_str = binop_info[op].operator;
type_t *iter_t = get_type(env, reduction->iter);
type_t *item_t = get_iterated_type(iter_t);
@@ -29,7 +30,7 @@ Text_t compile_reduction(env_t *env, ast_t *ast) {
type_t *item_value_type = item_t;
ast_t *item_value = item;
if (reduction->key) {
- set_binding(body_scope, "$", item_t, compile(body_scope, item));
+ set_binding(body_scope, op_str, item_t, compile(body_scope, item));
item_value = reduction->key;
item_value_type = get_type(body_scope, reduction->key);
}
@@ -67,7 +68,7 @@ Text_t compile_reduction(env_t *env, ast_t *ast) {
ast_e cmp_op = op == Min ? LessThan : GreaterThan;
if (reduction->key) {
env_t *key_scope = fresh_scope(env);
- set_binding(key_scope, "$", item_t, item_code);
+ set_binding(key_scope, op_str, item_t, item_code);
type_t *key_type = get_type(key_scope, reduction->key);
Text_t superlative_key = op == Min ? Text("min_key") : Text("max_key");
code = Texts(code, compile_declaration(key_type, superlative_key), ";\n");
@@ -111,7 +112,7 @@ Text_t compile_reduction(env_t *env, ast_t *ast) {
type_t *reduction_type = Match(get_type(env, ast), OptionalType)->type;
ast_t *item_value = item;
if (reduction->key) {
- set_binding(body_scope, "$", item_t, compile(body_scope, item));
+ set_binding(body_scope, op_str, item_t, compile(body_scope, item));
item_value = reduction->key;
}
diff --git a/src/compile/sets.c b/src/compile/sets.c
index d33677cc..3346a9aa 100644
--- a/src/compile/sets.c
+++ b/src/compile/sets.c
@@ -25,7 +25,7 @@ Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) {
}
{ // No comprehension:
- Text_t code = Texts("Set(", compile_type(item_type), ", ", compile_type_info(item_type), ", ", String(n));
+ Text_t code = Texts("Set(", compile_type(item_type), ", ", compile_type_info(item_type), ", ", n);
env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env;
for (ast_list_t *item = set->items; item; item = item->next) {
code = Texts(code, ", ", compile_to_type(scope, item->ast, item_type));
diff --git a/src/compile/statements.c b/src/compile/statements.c
index 7c58559d..bde9ae36 100644
--- a/src/compile/statements.c
+++ b/src/compile/statements.c
@@ -22,7 +22,15 @@ public
Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) {
if (code.length == 0 || !ast || !ast->file || !env->do_source_mapping) return code;
int64_t line = get_line_number(ast->file, ast->start);
- return Texts("\n#line ", String(line), "\n", code);
+ return Texts("\n#line ", line, "\n", code);
+}
+
+static Text_t compile_simple_update_assignment(env_t *env, ast_t *ast, const char *op) {
+ binary_operands_t update = BINARY_OPERANDS(ast);
+ type_t *lhs_t = get_type(env, update.lhs);
+ if (is_idempotent(update.lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
+ return Texts(compile_lvalue(env, update.lhs), " ", op, "= ", compile_to_type(env, update.rhs, lhs_t), ";");
+ return compile_update_assignment(env, ast);
}
static Text_t _compile_statement(env_t *env, ast_t *ast) {
@@ -47,41 +55,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
}
}
case Assign: return compile_assignment_statement(env, ast);
- case PlusUpdate: {
- DeclareMatch(update, ast, PlusUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " += ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case MinusUpdate: {
- DeclareMatch(update, ast, MinusUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " -= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case MultiplyUpdate: {
- DeclareMatch(update, ast, MultiplyUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " *= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case DivideUpdate: {
- DeclareMatch(update, ast, DivideUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " /= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case ModUpdate: {
- DeclareMatch(update, ast, ModUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " %= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
+ case PlusUpdate: return compile_simple_update_assignment(env, ast, "+");
+ case MinusUpdate: return compile_simple_update_assignment(env, ast, "-");
+ case MultiplyUpdate: return compile_simple_update_assignment(env, ast, "*");
+ case DivideUpdate: return compile_simple_update_assignment(env, ast, "/");
+ case ModUpdate: return compile_simple_update_assignment(env, ast, "%");
+
case PowerUpdate:
case Mod1Update:
case ConcatUpdate:
@@ -121,7 +100,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
if (Text$starts_with(entry->b->code, Text("userdata->"), NULL)) {
Table$str_set(defer_env->locals, entry->name, entry->b);
} else {
- Text_t defer_name = Texts("defer$", String(++defer_id), "$", entry->name);
+ Text_t defer_name = Texts("defer$", ++defer_id, "$", entry->name);
defer_id += 1;
code = Texts(code, compile_declaration(entry->b->type, defer_name), " = ", entry->b->code, ";\n");
set_binding(defer_env, entry->name, entry->b->type, defer_name);
@@ -131,7 +110,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
return code;
}
case Return: {
- if (!env->fn_ret) code_err(ast, "This return statement is not inside any function");
+ if (!env->fn) code_err(ast, "This return statement is not inside any function");
ast_t *ret = Match(ast, Return)->value;
Text_t code = EMPTY_TEXT;
@@ -139,22 +118,30 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
code = Texts(code, compile_statement(deferred->defer_env, deferred->block));
}
+ type_t *ret_type = get_function_return_type(env, env->fn);
if (ret) {
- if (env->fn_ret->tag == VoidType || env->fn_ret->tag == AbortType)
+ if (ret_type->tag == VoidType || ret_type->tag == AbortType)
code_err(ast, "This function is not supposed to return any values, "
"according to its type signature");
- env = with_enum_scope(env, env->fn_ret);
- Text_t value = compile_to_type(env, ret, env->fn_ret);
+ env = with_enum_scope(env, ret_type);
+ if (env->fn->tag == ConvertDef) {
+ type_t *value_type = get_type(env, ret);
+ if (!type_eq(value_type, ret_type)) {
+ code_err(ret, "This value is a ", type_to_text(value_type),
+ " but this conversion needs an explicit ", type_to_text(ret_type));
+ }
+ }
+ Text_t value = compile_to_type(env, ret, ret_type);
if (env->deferred) {
- code = Texts(compile_declaration(env->fn_ret, Text("ret")), " = ", value, ";\n", code);
+ code = Texts(compile_declaration(ret_type, Text("ret")), " = ", value, ";\n", code);
value = Text("ret");
}
return Texts(code, "return ", value, ";");
} else {
- if (env->fn_ret->tag != VoidType)
- code_err(ast, "This function expects you to return a ", type_to_str(env->fn_ret), " value");
+ if (ret_type->tag != VoidType)
+ code_err(ast, "This function expects you to return a ", type_to_text(ret_type), " value");
return Texts(code, "return;");
}
}
@@ -204,10 +191,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
module_info_t mod = get_module_info(ast);
glob_t tm_files;
const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name;
- if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE,
- NULL, &tm_files)
+ if (glob(String(TOMO_PATH, "/lib/tomo_" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL,
+ &tm_files)
!= 0) {
- if (!try_install_module(mod)) code_err(ast, "Could not find library");
+ if (!try_install_module(mod, true)) code_err(ast, "Could not find library");
}
Text_t initialization = EMPTY_TEXT;
diff --git a/src/compile/structs.c b/src/compile/structs.c
index 526c11c7..8560a790 100644
--- a/src/compile/structs.c
+++ b/src/compile/structs.c
@@ -18,7 +18,7 @@ Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_
? Text$from_str(name)
: Texts("struct ", namespace_name(env, env->namespace, Texts(name, "$$struct")));
- int num_fields = 0;
+ int64_t num_fields = 0;
for (arg_ast_t *f = fields; f; f = f->next)
num_fields += 1;
const char *short_name = name;
@@ -33,9 +33,9 @@ Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_
", "
".tag=StructInfo, .StructInfo.name=\"",
short_name, "\"", is_secret ? Text(", .StructInfo.is_secret=true") : EMPTY_TEXT,
- is_opaque ? Text(", .StructInfo.is_opaque=true") : EMPTY_TEXT, ", .StructInfo.num_fields=", String(num_fields));
+ is_opaque ? Text(", .StructInfo.is_opaque=true") : EMPTY_TEXT, ", .StructInfo.num_fields=", num_fields);
if (fields) {
- typeinfo = Texts(typeinfo, ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){");
+ typeinfo = Texts(typeinfo, ", .StructInfo.fields=(NamedType_t[", num_fields, "]){");
for (arg_ast_t *f = fields; f; f = f->next) {
type_t *field_type = get_arg_ast_type(env, f);
typeinfo = Texts(typeinfo, "{\"", f->name, "\", ", compile_type_info(field_type), "}");
@@ -70,8 +70,7 @@ Text_t compile_struct_header(env_t *env, ast_t *ast) {
Text_t struct_code = def->external ? EMPTY_TEXT : Texts(type_code, " {\n", fields, "};\n");
type_t *t = Table$str_get(*env->types, def->name);
- Text_t unpadded_size =
- def->opaque ? Texts("sizeof(", type_code, ")") : Text$from_str(String((int64_t)unpadded_struct_size(t)));
+ Text_t unpadded_size = def->opaque ? Texts("sizeof(", type_code, ")") : Texts((int64_t)unpadded_struct_size(t));
Text_t typeinfo_code = Texts("extern const TypeInfo_t ", typeinfo_name, ";\n");
Text_t optional_code = EMPTY_TEXT;
if (!def->opaque) {
diff --git a/src/compile/tables.c b/src/compile/tables.c
index b955178e..dde8669a 100644
--- a/src/compile/tables.c
+++ b/src/compile/tables.c
@@ -43,7 +43,7 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
size_t n = 0;
for (ast_list_t *entry = table->entries; entry; entry = entry->next)
++n;
- code = Texts(code, ", ", String((int64_t)n));
+ code = Texts(code, ", ", (int64_t)n);
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
DeclareMatch(e, entry->ast, TableEntry);
diff --git a/src/compile/types.c b/src/compile/types.c
index aa06e2fd..f6f276a5 100644
--- a/src/compile/types.c
+++ b/src/compile/types.c
@@ -23,10 +23,10 @@ Text_t compile_type(type_t *t) {
case ByteType: return Text("Byte_t");
case CStringType: return Text("const char*");
case BigIntType: return Text("Int_t");
- case IntType: return Texts("Int", String(Match(t, IntType)->bits), "_t");
+ case IntType: return Texts("Int", (int32_t)Match(t, IntType)->bits, "_t");
case NumType:
return Match(t, NumType)->bits == TYPE_NBITS64 ? Text("Num_t")
- : Texts("Num", String(Match(t, NumType)->bits), "_t");
+ : Texts("Num", (int32_t)Match(t, NumType)->bits, "_t");
case TextType: {
DeclareMatch(text, t, TextType);
if (!text->lang || streq(text->lang, "Text")) return Text("Text_t");