1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
// This file defines how to compile optionals and null
#include "../environment.h"
#include "../naming.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/text.h"
#include "../stdlib/util.h"
#include "../typecheck.h"
#include "../types.h"
#include "compilation.h"
#include "indexing.h"
Text_t optional_into_nonnone(type_t *t, Text_t value) {
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
switch (t->tag) {
case IntType:
case ByteType: return Texts(value, ".value");
case StructType:
if (t == PATH_TYPE || t == PATH_TYPE_TYPE) return value;
return Texts(value, ".value");
default: return value;
}
}
public
Text_t promote_to_optional(type_t *t, Text_t code) {
if (t == PATH_TYPE || t == PATH_TYPE_TYPE) {
return code;
} else if (t->tag == IntType) {
switch (Match(t, IntType)->bits) {
case TYPE_IBITS8: return Texts("((OptionalInt8_t){.has_value=true, .value=", code, "})");
case TYPE_IBITS16: return Texts("((OptionalInt16_t){.has_value=true, .value=", code, "})");
case TYPE_IBITS32: return Texts("((OptionalInt32_t){.has_value=true, .value=", code, "})");
case TYPE_IBITS64: return Texts("((OptionalInt64_t){.has_value=true, .value=", code, "})");
default: errx(1, "Unsupported in type: %s", Text$as_c_string(type_to_text(t)));
}
return code;
} else if (t->tag == ByteType) {
return Texts("((OptionalByte_t){.has_value=true, .value=", code, "})");
} else if (t->tag == StructType) {
return Texts("((", compile_type(Type(OptionalType, .type = t)), "){.has_value=true, .value=", code, "})");
} else {
return code;
}
}
public
Text_t compile_none(type_t *t) {
if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type");
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type");
if (t == PATH_TYPE) return Text("NONE_PATH");
else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){.type = PATH_NONE})");
switch (t->tag) {
case BigIntType: return Text("NONE_INT");
case IntType: {
switch (Match(t, IntType)->bits) {
case TYPE_IBITS8: return Text("NONE_INT8");
case TYPE_IBITS16: return Text("NONE_INT16");
case TYPE_IBITS32: return Text("NONE_INT32");
case TYPE_IBITS64: return Text("NONE_INT64");
default: errx(1, "Invalid integer bit size");
}
break;
}
case BoolType: return Text("NONE_BOOL");
case ByteType: return Text("NONE_BYTE");
case ListType: return Text("NONE_LIST");
case TableType: return Text("NONE_TABLE");
case TextType: return Text("NONE_TEXT");
case CStringType: return Text("NULL");
case PointerType: return Texts("((", compile_type(t), ")NULL)");
case ClosureType: return Text("NONE_CLOSURE");
case NumType: return Text("nan(\"none\")");
case StructType: return Texts("((", compile_type(Type(OptionalType, .type = t)), "){.has_value=false})");
case EnumType: {
env_t *enum_env = Match(t, EnumType)->env;
return Texts("((", compile_type(t), "){", namespace_name(enum_env, enum_env->namespace, Text("none")), "})");
}
default: compiler_err(NULL, NULL, NULL, "none isn't implemented for this type: ", type_to_text(t));
}
return EMPTY_TEXT;
}
public
Text_t check_none(type_t *t, Text_t value) {
t = Match(t, OptionalType)->type;
// NOTE: these use statement expressions ({...;}) because some compilers
// complain about excessive parens around equality comparisons
if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType) return Texts("(", value, " == NULL)");
else if (t == PATH_TYPE) return Texts("((", value, ").type.$tag == PATH_NONE)");
else if (t == PATH_TYPE_TYPE) return Texts("((", value, ").$tag == PATH_NONE)");
else if (t->tag == BigIntType) return Texts("((", value, ").small == 0)");
else if (t->tag == ClosureType) return Texts("((", value, ").fn == NULL)");
else if (t->tag == NumType)
return Texts(Match(t, NumType)->bits == TYPE_NBITS64 ? "Num$isnan(" : "Num32$isnan(", value, ")");
else if (t->tag == ListType) return Texts("((", value, ").data == NULL)");
else if (t->tag == TableType) return Texts("((", value, ").entries.data == NULL)");
else if (t->tag == BoolType) return Texts("((", value, ") == NONE_BOOL)");
else if (t->tag == TextType) return Texts("((", value, ").tag == TEXT_NONE)");
else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) return Texts("!(", value, ").has_value");
else if (t->tag == EnumType) {
if (enum_has_fields(t)) return Texts("((", value, ").$tag == 0)");
else return Texts("((", value, ") == 0)");
}
print_err("Optional check not implemented for: ", type_to_text(t));
return EMPTY_TEXT;
}
public
Text_t compile_non_optional(env_t *env, ast_t *ast) {
ast_t *value = Match(ast, NonOptional)->value;
if (value->tag == Index && Match(value, Index)->index != NULL) return compile_indexing(env, value, true);
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 ", 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\");\n", optional_into_nonnone(t, Text("opt")), "; })");
}
|