code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(162 lines)
1 // This file defines how to compile optionals and null
3 #include "../environment.h"
4 #include "../naming.h"
5 #include "../stdlib/datatypes.h"
6 #include "../stdlib/text.h"
7 #include "../stdlib/util.h"
8 #include "../typecheck.h"
9 #include "../types.h"
10 #include "compilation.h"
11 #include "indexing.h"
13 Text_t optional_into_nonnone(type_t *t, Text_t value) {
14 if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
15 switch (t->tag) {
16 case IntType:
17 case ByteType: return Texts(value, ".value");
18 case StructType: return Texts(value, ".value");
19 default: return value;
23 public
24 Text_t promote_to_optional(type_t *t, Text_t code) {
25 if (t->tag == IntType) {
26 switch (Match(t, IntType)->bits) {
27 case TYPE_IBITS8: return Texts("((OptionalInt8_t){.has_value=true, .value=", code, "})");
28 case TYPE_IBITS16: return Texts("((OptionalInt16_t){.has_value=true, .value=", code, "})");
29 case TYPE_IBITS32: return Texts("((OptionalInt32_t){.has_value=true, .value=", code, "})");
30 case TYPE_IBITS64: return Texts("((OptionalInt64_t){.has_value=true, .value=", code, "})");
31 default: errx(1, "Unsupported in type: %s", Text$as_c_string(type_to_text(t)));
33 return code;
34 } else if (t->tag == ByteType) {
35 return Texts("((OptionalByte_t){.has_value=true, .value=", code, "})");
36 } else if (t->tag == StructType) {
37 return Texts("((", compile_type(Type(OptionalType, .type = t)), "){.has_value=true, .value=", code, "})");
38 } else {
39 return code;
43 public
44 Text_t compile_none(type_t *t) {
45 if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type");
47 if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
49 if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type");
51 switch (t->tag) {
52 case BigIntType: return Text("NONE_INT");
53 case IntType: {
54 switch (Match(t, IntType)->bits) {
55 case TYPE_IBITS8: return Text("NONE_INT8");
56 case TYPE_IBITS16: return Text("NONE_INT16");
57 case TYPE_IBITS32: return Text("NONE_INT32");
58 case TYPE_IBITS64: return Text("NONE_INT64");
59 default: errx(1, "Invalid integer bit size");
61 break;
63 case BoolType: return Text("NONE_BOOL");
64 case ByteType: return Text("NONE_BYTE");
65 case ListType: return Text("NONE_LIST");
66 case TableType: return Text("NONE_TABLE");
67 case TextType: return Text("NONE_TEXT");
68 case CStringType: return Text("NULL");
69 case PathType: return Text("NONE_PATH");
70 case PointerType: return Texts("((", compile_type(t), ")NULL)");
71 case ClosureType: return Text("NONE_CLOSURE");
72 case NumType: return Text("nan(\"none\")");
73 case StructType: return Texts("((", compile_type(Type(OptionalType, .type = t)), "){.has_value=false})");
74 case EnumType: {
75 env_t *enum_env = Match(t, EnumType)->env;
76 return Texts("((", compile_type(t), "){", namespace_name(enum_env, enum_env->namespace, Text("none")), "})");
78 default: compiler_err(NULL, NULL, NULL, "none isn't implemented for this type: ", type_to_text(t));
80 return EMPTY_TEXT;
83 public
84 Text_t check_none(type_t *t, Text_t value) {
85 t = Match(t, OptionalType)->type;
86 // NOTE: these use statement expressions ({...;}) because some compilers
87 // complain about excessive parens around equality comparisons
88 switch (t->tag) {
89 case PointerType:
90 case FunctionType:
91 case CStringType:
92 case PathType: return Texts("(", value, " == NULL)");
93 case BigIntType: return Texts("((", value, ").small == 0)");
94 case ClosureType: return Texts("((", value, ").fn == NULL)");
95 case NumType: return Texts(Match(t, NumType)->bits == TYPE_NBITS64 ? "Num$isnan(" : "Num32$isnan(", value, ")");
96 case ListType: return Texts("((", value, ").data == NULL)");
97 case TableType: return Texts("((", value, ").entries.data == NULL)");
98 case BoolType: return Texts("((", value, ") == NONE_BOOL)");
99 case TextType: return Texts("((", value, ").tag == TEXT_NONE)");
100 case IntType:
101 case ByteType:
102 case StructType: return Texts("!(", value, ").has_value");
103 case EnumType: return Texts("((", value, ").$tag == 0)");
104 default: {
105 print_err("Optional check not implemented for: ", type_to_text(t));
106 return EMPTY_TEXT;
111 public
112 Text_t compile_non_optional(env_t *env, ast_t *ast) {
113 ast_t *value = Match(ast, NonOptional)->value;
114 if (value->tag == Index && Match(value, Index)->index != NULL) return compile_indexing(env, value, true);
115 type_t *value_t = get_type(env, value);
116 if (value_t->tag == PointerType) {
117 // Dereference pointers automatically
118 return compile_non_optional(env, WrapAST(ast, NonOptional, WrapAST(ast, Index, .indexed = value)));
120 int64_t line = get_line_number(ast->file, ast->start);
121 if (value_t->tag == EnumType) {
122 // For this case:
123 // enum Foo(FirstField, SecondField(msg:Text))
124 // e := ...
125 // e!
126 // We desugar into `e.FirstField!` using the first enum field
127 tag_t *first_tag = Match(value_t, EnumType)->tags;
128 if (!first_tag) code_err(ast, "'!' cannot be used on an empty enum");
129 return compile_non_optional(
130 env, WrapAST(ast, NonOptional, WrapAST(value, FieldAccess, .fielded = value, .field = first_tag->name)));
131 } else if (value->tag == FieldAccess
132 && value_type(get_type(env, Match(value, FieldAccess)->fielded))->tag == EnumType) {
133 type_t *enum_t = value_type(get_type(env, Match(value, FieldAccess)->fielded));
134 DeclareMatch(e, enum_t, EnumType);
135 DeclareMatch(f, value, FieldAccess);
136 for (tag_t *tag = e->tags; tag; tag = tag->next) {
137 if (streq(f->field, tag->name)) {
138 Text_t tag_name = namespace_name(e->env, e->env->namespace, Texts("tag$", tag->name));
139 return Texts(
140 "({ ", compile_declaration(enum_t, Text("_test_enum")), " = ",
141 compile_to_pointer_depth(env, f->fielded, 0, true), ";",
142 "if unlikely (_test_enum.$tag != ", tag_name, ") {\n", "#line ", line, "\n", "fail_source(",
143 quoted_str(f->fielded->file->filename), ", ", (int64_t)(f->fielded->start - f->fielded->file->text),
144 ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ",
145 "Text$concat(Text(\"This was expected to be ", tag->name, ", but it was: \"), ",
146 expr_as_text(Text("_test_enum"), enum_t, Text("false")), ", Text(\"\\n\")));\n}\n",
147 compile_maybe_incref(
148 env, WrapLiteralCode(value, Texts("_test_enum.", tag->name), .type = tag->type), tag->type),
149 "; })");
152 code_err(ast, "The field '", f->field, "' is not a valid tag name of ", type_to_text(enum_t));
153 } else {
154 Text_t value_code = compile(env, value);
155 return Texts("({ ", compile_declaration(value_t, Text("opt")), " = ", value_code, "; ", "if unlikely (",
156 check_none(value_t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(",
157 quoted_str(value->file->filename), ", ", (int64_t)(value->start - value->file->text), ", ",
158 (int64_t)(value->end - value->file->text), ", ",
159 "Text(\"This was expected to be a value, but it's `none`\\n\"));\n",
160 optional_into_nonnone(value_t, Text("opt")), "; })");