code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(140 lines)
1 // This file defines how to compile text
3 #include <ctype.h>
5 #include "../ast.h"
6 #include "../environment.h"
7 #include "../stdlib/datatypes.h"
8 #include "../stdlib/tables.h"
9 #include "../stdlib/text.h"
10 #include "../typecheck.h"
11 #include "../types.h"
12 #include "compilation.h"
14 public
15 Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) {
16 switch (t->tag) {
17 case MemoryType: return Texts("Memory$as_text(stack(", expr, "), ", color, ", &Memory$info)");
18 case BoolType:
19 // NOTE: this cannot use stack(), since bools may actually be bit
20 // fields:
21 return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)");
22 case CStringType: return Texts("CString$as_text(stack(", expr, "), ", color, ", &CString$info)");
23 case PathType: return Texts("Path$as_text(stack(", expr, "), ", color, ", &Path$info)");
24 case BigIntType:
25 case IntType:
26 case ByteType:
27 case NumType: {
28 Text_t name = type_to_text(t);
29 return Texts(name, "$as_text(stack(", expr, "), ", color, ", &", name, "$info)");
31 case TextType: return Texts("Text$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
32 case ListType: return Texts("List$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
33 case TableType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
34 case FunctionType:
35 case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
36 case PointerType: return Texts("Pointer$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
37 case OptionalType: return Texts("Optional$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
38 case StructType:
39 case EnumType: return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")");
40 default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for ", type_to_text(t));
42 return EMPTY_TEXT;
45 public
46 Text_t compile_text(env_t *env, ast_t *ast, Text_t color) {
47 type_t *t = get_type(env, ast);
48 Text_t expr = compile(env, ast);
49 return expr_as_text(expr, t, color);
52 public
53 Text_t compile_text_literal(Text_t literal) {
54 Text_t code = Text("\"");
55 const char *utf8 = Text$as_c_string(literal);
56 for (const char *p = utf8; *p; p++) {
57 switch (*p) {
58 case '\\': code = Texts(code, "\\\\"); break;
59 case '"': code = Texts(code, "\\\""); break;
60 case '\a': code = Texts(code, "\\a"); break;
61 case '\b': code = Texts(code, "\\b"); break;
62 case '\n': code = Texts(code, "\\n"); break;
63 case '\r': code = Texts(code, "\\r"); break;
64 case '\t': code = Texts(code, "\\t"); break;
65 case '\v': code = Texts(code, "\\v"); break;
66 default: {
67 if (isprint(*p)) {
68 code = Texts(code, Text$from_strn(p, 1));
69 } else {
70 uint8_t byte = *(uint8_t *)p;
71 code = Texts(code, "\\x", String(hex(byte, .no_prefix = true, .uppercase = true, .digits = 2)), "\"\"");
73 break;
77 return Texts(code, "\"");
80 PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) {
81 TextIter_t state = NEW_TEXT_ITER_STATE(literal);
82 for (int64_t i = 0; i < (int64_t)literal.length; i++) {
83 int32_t g = Text$get_grapheme_fast(&state, i);
84 if (g < 0 || g > 127 || !isascii(g)) return false;
86 return true;
89 public
90 Text_t compile_text_ast(env_t *env, ast_t *ast) {
91 if (ast->tag == TextLiteral) {
92 Text_t literal = Match(ast, TextLiteral)->text;
93 if (literal.length == 0) return Text("EMPTY_TEXT");
95 if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")");
96 else return Texts("Text$from_str(", compile_text_literal(literal), ")");
99 const char *lang = Match(ast, TextJoin)->lang;
100 Text_t colorize = Match(ast, TextJoin)->colorize ? Text("yes") : Text("no");
102 type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE;
103 if (!text_t || text_t->tag != TextType) code_err(ast, quoted(lang), " is not a valid text language name");
105 ast_list_t *chunks = Match(ast, TextJoin)->children;
106 if (!chunks) {
107 return Text("EMPTY_TEXT");
108 } else if (!chunks->next && chunks->ast->tag == TextLiteral) {
109 Text_t literal = Match(chunks->ast, TextLiteral)->text;
110 if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")");
111 return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")");
112 } else {
113 Text_t code = EMPTY_TEXT;
114 for (ast_list_t *chunk = chunks; chunk; chunk = chunk->next) {
115 Text_t chunk_code;
116 type_t *chunk_t = get_type(env, chunk->ast);
117 if (chunk->ast->tag == TextLiteral || type_eq(chunk_t, text_t)) {
118 chunk_code = compile(env, chunk->ast);
119 } else {
120 binding_t *constructor =
121 get_constructor(env, text_t, new (arg_ast_t, .value = chunk->ast),
122 env->current_type != NULL && type_eq(env->current_type, text_t));
123 if (constructor) {
124 arg_t *arg_spec = Match(constructor->type, FunctionType)->args;
125 arg_ast_t *args = new (arg_ast_t, .value = chunk->ast);
126 chunk_code = Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, args), ")");
127 } else if (type_eq(text_t, TEXT_TYPE)) {
128 if (chunk_t->tag == TextType) chunk_code = compile(env, chunk->ast);
129 else chunk_code = compile_text(env, chunk->ast, colorize);
130 } else {
131 code_err(chunk->ast, "I don't know how to convert ", type_to_text(chunk_t), " to ",
132 type_to_text(text_t));
135 code = Texts(code, chunk_code);
136 if (chunk->next) code = Texts(code, ", ");
138 return Texts("Text$concat(", code, ")");