diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 16:05:24 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 16:05:24 -0400 |
| commit | bd312d20901b968e0603359e8cd01276d114031a (patch) | |
| tree | 12817d84846921475aa3dbcf8fa0456cc68667e5 /src | |
| parent | e920d306fb51c16fd473033b64aa5ba5c3e07409 (diff) | |
Further split out
Diffstat (limited to 'src')
| -rw-r--r-- | src/compile.c | 236 | ||||
| -rw-r--r-- | src/compile.h | 2 | ||||
| -rw-r--r-- | src/compile/functions.c | 101 | ||||
| -rw-r--r-- | src/compile/functions.h | 1 | ||||
| -rw-r--r-- | src/compile/text.c | 148 | ||||
| -rw-r--r-- | src/compile/text.h | 10 |
6 files changed, 265 insertions, 233 deletions
diff --git a/src/compile.c b/src/compile.c index 859b17f3..c906112b 100644 --- a/src/compile.c +++ b/src/compile.c @@ -1,5 +1,4 @@ // Compilation logic -#include <ctype.h> #include <gc.h> #include <glob.h> #include <gmp.h> @@ -9,6 +8,7 @@ #include "ast.h" #include "compile.h" #include "compile/enums.h" +#include "compile/functions.h" #include "compile/integers.h" #include "compile/lists.h" #include "compile/optionals.h" @@ -17,6 +17,7 @@ #include "compile/sets.h" #include "compile/structs.h" #include "compile/tables.h" +#include "compile/text.h" #include "config.h" #include "environment.h" #include "modules.h" @@ -30,8 +31,6 @@ typedef ast_t *(*comprehension_body_t)(ast_t *, ast_t *); -static Text_t compile_text(env_t *env, ast_t *ast, Text_t color); -static Text_t compile_text_literal(Text_t literal); static Text_t compile_unsigned_type(type_t *t); static Text_t compile_declared_value(env_t *env, ast_t *declaration_ast); @@ -1894,78 +1893,6 @@ Text_t compile_statement(env_t *env, ast_t *ast) { return with_source_info(env, ast, stmt); } -Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) { - switch (t->tag) { - case MemoryType: return Texts("Memory$as_text(stack(", expr, "), ", color, ", &Memory$info)"); - case BoolType: - // NOTE: this cannot use stack(), since bools may actually be bit - // fields: - return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)"); - case CStringType: return Texts("CString$as_text(stack(", expr, "), ", color, ", &CString$info)"); - case BigIntType: - case IntType: - case ByteType: - case NumType: { - Text_t name = type_to_text(t); - return Texts(name, "$as_text(stack(", expr, "), ", color, ", &", name, "$info)"); - } - case TextType: return Texts("Text$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case ListType: return Texts("List$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case SetType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case TableType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case FunctionType: - case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case PointerType: return Texts("Pointer$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case OptionalType: return Texts("Optional$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case StructType: - case EnumType: return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for ", type_to_str(t)); - } - return EMPTY_TEXT; -} - -Text_t compile_text(env_t *env, ast_t *ast, Text_t color) { - type_t *t = get_type(env, ast); - Text_t expr = compile(env, ast); - return expr_as_text(expr, t, color); -} - -Text_t compile_text_literal(Text_t literal) { - Text_t code = Text("\""); - const char *utf8 = Text$as_c_string(literal); - for (const char *p = utf8; *p; p++) { - switch (*p) { - case '\\': code = Texts(code, "\\\\"); break; - case '"': code = Texts(code, "\\\""); break; - case '\a': code = Texts(code, "\\a"); break; - case '\b': code = Texts(code, "\\b"); break; - case '\n': code = Texts(code, "\\n"); break; - case '\r': code = Texts(code, "\\r"); break; - case '\t': code = Texts(code, "\\t"); break; - case '\v': code = Texts(code, "\\v"); break; - default: { - if (isprint(*p)) { - code = Texts(code, Text$from_strn(p, 1)); - } else { - uint8_t byte = *(uint8_t *)p; - code = Texts(code, "\\x", String(hex(byte, .no_prefix = true, .uppercase = true, .digits = 2)), "\"\""); - } - break; - } - } - } - return Texts(code, "\""); -} - -PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) { - TextIter_t state = NEW_TEXT_ITER_STATE(literal); - for (int64_t i = 0; i < literal.length; i++) { - int32_t g = Text$get_grapheme_fast(&state, i); - if (g < 0 || g > 127 || !isascii(g)) return false; - } - return true; -} - public Text_t compile_empty(type_t *t) { if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a value with no type"); @@ -2214,64 +2141,8 @@ Text_t compile(env_t *env, ast_t *ast) { op, " 0)"); } } - case TextLiteral: { - Text_t literal = Match(ast, TextLiteral)->text; - if (literal.length == 0) return Text("EMPTY_TEXT"); - - if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")"); - else return Texts("Text$from_str(", compile_text_literal(literal), ")"); - } - case TextJoin: { - const char *lang = Match(ast, TextJoin)->lang; - Text_t colorize = Match(ast, TextJoin)->colorize ? Text("yes") : Text("no"); - - 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, "(\"\")"); - } 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), ")"); - return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); - } else { - Text_t code = EMPTY_TEXT; - for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { - Text_t chunk_code; - type_t *chunk_t = get_type(env, chunk->ast); - if (chunk->ast->tag == TextLiteral || type_eq(chunk_t, text_t)) { - chunk_code = compile(env, chunk->ast); - } else { - binding_t *constructor = - get_constructor(env, text_t, new (arg_ast_t, .value = chunk->ast), - env->current_type != NULL && type_eq(env->current_type, text_t)); - if (constructor) { - arg_t *arg_spec = Match(constructor->type, FunctionType)->args; - arg_ast_t *args = new (arg_ast_t, .value = chunk->ast); - chunk_code = Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, args), ")"); - } else if (type_eq(text_t, TEXT_TYPE)) { - if (chunk_t->tag == TextType) chunk_code = compile(env, chunk->ast); - else chunk_code = compile_text(env, chunk->ast, colorize); - } else { - code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", - type_to_str(text_t)); - } - } - code = Texts(code, chunk_code); - if (chunk->next) code = Texts(code, ", "); - } - if (chunks->next) return Texts(lang_constructor, "s(", code, ")"); - else return code; - } - } + case TextLiteral: + case TextJoin: return compile_text_ast(env, ast); case Path: { return Texts("Path(", compile_text_literal(Text$from_str(Match(ast, Path)->path)), ")"); } @@ -2819,104 +2690,7 @@ Text_t compile(env_t *env, ast_t *ast) { } #undef EXPECT_POINTER } - case FunctionCall: { - DeclareMatch(call, ast, FunctionCall); - type_t *fn_t = get_type(env, call->fn); - if (fn_t->tag == FunctionType) { - Text_t fn = compile(env, call->fn); - if (!is_valid_call(env, Match(fn_t, FunctionType)->args, call->args, (call_opts_t){.promotion = true})) { - if (is_valid_call(env, Match(fn_t, FunctionType)->args, call->args, - (call_opts_t){.promotion = true, .underscores = true})) { - code_err(ast, "You can't pass underscore arguments to this function (those are private)"); - } else { - arg_t *args = NULL; - for (arg_ast_t *a = call->args; a; a = a->next) - args = new (arg_t, .name = a->name, .type = get_type(env, a->value), .next = args); - REVERSE_LIST(args); - code_err(ast, - "This function's public signature doesn't match this call site.\n" - "The signature is: ", - type_to_text(fn_t), - "\n" - "But it's being called with: ", - type_to_text(Type(FunctionType, .args = args))); - } - } - return Texts(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")"); - } else if (fn_t->tag == TypeInfoType) { - type_t *t = Match(fn_t, TypeInfoType)->type; - - // Literal constructors for numeric types like `Byte(123)` should - // not go through any conversion, just a cast: - if (is_numeric_type(t) && call->args && !call->args->next && call->args->value->tag == Int) - return compile_to_type(env, call->args->value, t); - else if (t->tag == NumType && call->args && !call->args->next && call->args->value->tag == Num) - return compile_to_type(env, call->args->value, t); - - binding_t *constructor = - get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t)); - if (constructor) { - arg_t *arg_spec = Match(constructor->type, FunctionType)->args; - return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")"); - } - - type_t *actual = call->args ? get_type(env, call->args->value) : NULL; - if (t->tag == TextType) { - if (!call->args) code_err(ast, "This constructor needs a value"); - if (!type_eq(t, TEXT_TYPE)) - code_err(call->fn, "I don't have a constructor defined for " - "these arguments"); - // Text constructor: - if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); - if (type_eq(actual, t)) return compile(env, call->args->value); - return expr_as_text(compile(env, call->args->value), actual, Text("no")); - } else if (t->tag == CStringType) { - // C String constructor: - if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); - if (call->args->value->tag == TextLiteral) - return compile_text_literal(Match(call->args->value, TextLiteral)->text); - else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children == NULL) - return Text("\"\""); - else if (call->args->value->tag == TextJoin - && Match(call->args->value, TextJoin)->children->next == NULL) - return compile_text_literal( - Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text); - return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), - ")"); - } else if (t->tag == StructType) { - return compile_struct_literal(env, ast, t, call->args); - } - code_err(ast, - "I could not find a constructor matching these arguments " - "for ", - type_to_str(t)); - } else if (fn_t->tag == ClosureType) { - fn_t = Match(fn_t, ClosureType)->fn; - arg_t *type_args = Match(fn_t, FunctionType)->args; - - arg_t *closure_fn_args = NULL; - for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next) - closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val, - .next = closure_fn_args); - closure_fn_args = new (arg_t, .name = "userdata", .type = Type(PointerType, .pointed = Type(MemoryType)), - .next = closure_fn_args); - REVERSE_LIST(closure_fn_args); - Text_t fn_type_code = - compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret)); - - Text_t closure = compile(env, call->fn); - Text_t arg_code = compile_arguments(env, ast, type_args, call->args); - if (arg_code.length > 0) arg_code = Texts(arg_code, ", "); - if (call->fn->tag == Var) { - return Texts("((", fn_type_code, ")", closure, ".fn)(", arg_code, closure, ".userdata)"); - } else { - return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", arg_code, - "closure.userdata); })"); - } - } else { - code_err(call->fn, "This is not a function, it's a ", type_to_str(fn_t)); - } - } + case FunctionCall: return compile_function_call(env, ast); case Deserialize: { ast_t *value = Match(ast, Deserialize)->value; type_t *value_type = get_type(env, value); diff --git a/src/compile.h b/src/compile.h index 6ab62f9e..f3758643 100644 --- a/src/compile.h +++ b/src/compile.h @@ -8,7 +8,6 @@ #include "types.h" Text_t compile(env_t *env, ast_t *ast); -Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args); Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version); Text_t compile_declaration(type_t *t, Text_t name); Text_t compile_empty(type_t *t); @@ -22,6 +21,5 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast); Text_t compile_type(type_t *t); Text_t compile_type_info(type_t *t); -Text_t expr_as_text(Text_t expr, type_t *t, Text_t color); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/compile/functions.c b/src/compile/functions.c index ac4e27ea..1a2e5d3d 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -10,6 +10,9 @@ #include "../typecheck.h" #include "../types.h" #include "integers.h" +#include "promotion.h" +#include "structs.h" +#include "text.h" public Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) { @@ -94,3 +97,101 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ } return code; } + +public +Text_t compile_function_call(env_t *env, ast_t *ast) { + DeclareMatch(call, ast, FunctionCall); + type_t *fn_t = get_type(env, call->fn); + if (fn_t->tag == FunctionType) { + Text_t fn = compile(env, call->fn); + if (!is_valid_call(env, Match(fn_t, FunctionType)->args, call->args, (call_opts_t){.promotion = true})) { + if (is_valid_call(env, Match(fn_t, FunctionType)->args, call->args, + (call_opts_t){.promotion = true, .underscores = true})) { + code_err(ast, "You can't pass underscore arguments to this function (those are private)"); + } else { + arg_t *args = NULL; + for (arg_ast_t *a = call->args; a; a = a->next) + args = new (arg_t, .name = a->name, .type = get_type(env, a->value), .next = args); + REVERSE_LIST(args); + code_err(ast, + "This function's public signature doesn't match this call site.\n" + "The signature is: ", + type_to_text(fn_t), + "\n" + "But it's being called with: ", + type_to_text(Type(FunctionType, .args = args))); + } + } + return Texts(fn, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), ")"); + } else if (fn_t->tag == TypeInfoType) { + type_t *t = Match(fn_t, TypeInfoType)->type; + + // Literal constructors for numeric types like `Byte(123)` should + // not go through any conversion, just a cast: + if (is_numeric_type(t) && call->args && !call->args->next && call->args->value->tag == Int) + return compile_to_type(env, call->args->value, t); + else if (t->tag == NumType && call->args && !call->args->next && call->args->value->tag == Num) + return compile_to_type(env, call->args->value, t); + + binding_t *constructor = + get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t)); + if (constructor) { + arg_t *arg_spec = Match(constructor->type, FunctionType)->args; + return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")"); + } + + type_t *actual = call->args ? get_type(env, call->args->value) : NULL; + if (t->tag == TextType) { + if (!call->args) code_err(ast, "This constructor needs a value"); + if (!type_eq(t, TEXT_TYPE)) + code_err(call->fn, "I don't have a constructor defined for " + "these arguments"); + // Text constructor: + if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); + if (type_eq(actual, t)) return compile(env, call->args->value); + return expr_as_text(compile(env, call->args->value), actual, Text("no")); + } else if (t->tag == CStringType) { + // C String constructor: + if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); + if (call->args->value->tag == TextLiteral) + return compile_text_literal(Match(call->args->value, TextLiteral)->text); + else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children == NULL) + return Text("\"\""); + else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL) + return compile_text_literal( + Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text); + return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), ")"); + } else if (t->tag == StructType) { + return compile_struct_literal(env, ast, t, call->args); + } + code_err(ast, + "I could not find a constructor matching these arguments " + "for ", + type_to_str(t)); + } else if (fn_t->tag == ClosureType) { + fn_t = Match(fn_t, ClosureType)->fn; + arg_t *type_args = Match(fn_t, FunctionType)->args; + + arg_t *closure_fn_args = NULL; + for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next) + closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val, + .next = closure_fn_args); + closure_fn_args = new (arg_t, .name = "userdata", .type = Type(PointerType, .pointed = Type(MemoryType)), + .next = closure_fn_args); + REVERSE_LIST(closure_fn_args); + Text_t fn_type_code = + compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret)); + + Text_t closure = compile(env, call->fn); + Text_t arg_code = compile_arguments(env, ast, type_args, call->args); + if (arg_code.length > 0) arg_code = Texts(arg_code, ", "); + if (call->fn->tag == Var) { + return Texts("((", fn_type_code, ")", closure, ".fn)(", arg_code, closure, ".userdata)"); + } else { + return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", arg_code, + "closure.userdata); })"); + } + } else { + code_err(call->fn, "This is not a function, it's a ", type_to_str(fn_t)); + } +} diff --git a/src/compile/functions.h b/src/compile/functions.h index 1d2362a4..fdab495f 100644 --- a/src/compile/functions.h +++ b/src/compile/functions.h @@ -3,4 +3,5 @@ #include "../environment.h" #include "../stdlib/datatypes.h" +Text_t compile_function_call(env_t *env, ast_t *ast); Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args); diff --git a/src/compile/text.c b/src/compile/text.c new file mode 100644 index 00000000..049f8f94 --- /dev/null +++ b/src/compile/text.c @@ -0,0 +1,148 @@ +#include <ctype.h> + +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../naming.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/tables.h" +#include "../stdlib/text.h" +#include "../typecheck.h" +#include "../types.h" +#include "functions.h" + +public +Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) { + switch (t->tag) { + case MemoryType: return Texts("Memory$as_text(stack(", expr, "), ", color, ", &Memory$info)"); + case BoolType: + // NOTE: this cannot use stack(), since bools may actually be bit + // fields: + return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)"); + case CStringType: return Texts("CString$as_text(stack(", expr, "), ", color, ", &CString$info)"); + case BigIntType: + case IntType: + case ByteType: + case NumType: { + Text_t name = type_to_text(t); + return Texts(name, "$as_text(stack(", expr, "), ", color, ", &", name, "$info)"); + } + case TextType: return Texts("Text$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case ListType: return Texts("List$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case SetType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case TableType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case FunctionType: + case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case PointerType: return Texts("Pointer$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case OptionalType: return Texts("Optional$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case StructType: + case EnumType: return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for ", type_to_str(t)); + } + return EMPTY_TEXT; +} + +public +Text_t compile_text(env_t *env, ast_t *ast, Text_t color) { + type_t *t = get_type(env, ast); + Text_t expr = compile(env, ast); + return expr_as_text(expr, t, color); +} + +public +Text_t compile_text_literal(Text_t literal) { + Text_t code = Text("\""); + const char *utf8 = Text$as_c_string(literal); + for (const char *p = utf8; *p; p++) { + switch (*p) { + case '\\': code = Texts(code, "\\\\"); break; + case '"': code = Texts(code, "\\\""); break; + case '\a': code = Texts(code, "\\a"); break; + case '\b': code = Texts(code, "\\b"); break; + case '\n': code = Texts(code, "\\n"); break; + case '\r': code = Texts(code, "\\r"); break; + case '\t': code = Texts(code, "\\t"); break; + case '\v': code = Texts(code, "\\v"); break; + default: { + if (isprint(*p)) { + code = Texts(code, Text$from_strn(p, 1)); + } else { + uint8_t byte = *(uint8_t *)p; + code = Texts(code, "\\x", String(hex(byte, .no_prefix = true, .uppercase = true, .digits = 2)), "\"\""); + } + break; + } + } + } + return Texts(code, "\""); +} + +PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) { + TextIter_t state = NEW_TEXT_ITER_STATE(literal); + for (int64_t i = 0; i < literal.length; i++) { + int32_t g = Text$get_grapheme_fast(&state, i); + if (g < 0 || g > 127 || !isascii(g)) return false; + } + return true; +} + +public +Text_t compile_text_ast(env_t *env, ast_t *ast) { + if (ast->tag == TextLiteral) { + Text_t literal = Match(ast, TextLiteral)->text; + if (literal.length == 0) return Text("EMPTY_TEXT"); + + if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")"); + else return Texts("Text$from_str(", compile_text_literal(literal), ")"); + } + + const char *lang = Match(ast, TextJoin)->lang; + Text_t colorize = Match(ast, TextJoin)->colorize ? Text("yes") : Text("no"); + + 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, "(\"\")"); + } 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), ")"); + return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); + } else { + Text_t code = EMPTY_TEXT; + for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) { + Text_t chunk_code; + type_t *chunk_t = get_type(env, chunk->ast); + if (chunk->ast->tag == TextLiteral || type_eq(chunk_t, text_t)) { + chunk_code = compile(env, chunk->ast); + } else { + binding_t *constructor = + get_constructor(env, text_t, new (arg_ast_t, .value = chunk->ast), + env->current_type != NULL && type_eq(env->current_type, text_t)); + if (constructor) { + arg_t *arg_spec = Match(constructor->type, FunctionType)->args; + arg_ast_t *args = new (arg_ast_t, .value = chunk->ast); + chunk_code = Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, args), ")"); + } else if (type_eq(text_t, TEXT_TYPE)) { + if (chunk_t->tag == TextType) chunk_code = compile(env, chunk->ast); + else chunk_code = compile_text(env, chunk->ast, colorize); + } else { + code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", + type_to_str(text_t)); + } + } + code = Texts(code, chunk_code); + if (chunk->next) code = Texts(code, ", "); + } + if (chunks->next) return Texts(lang_constructor, "s(", code, ")"); + else return code; + } +} diff --git a/src/compile/text.h b/src/compile/text.h new file mode 100644 index 00000000..cdee4a5d --- /dev/null +++ b/src/compile/text.h @@ -0,0 +1,10 @@ + +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../types.h" + +Text_t compile_text_ast(env_t *env, ast_t *ast); +Text_t compile_text(env_t *env, ast_t *ast, Text_t color); +Text_t compile_text_literal(Text_t literal); +Text_t expr_as_text(Text_t expr, type_t *t, Text_t color); |
