aboutsummaryrefslogtreecommitdiff
path: root/src/compile/optionals.c
blob: b4930d02b5da6a27b07d4364ec6c86d956088ff3 (plain)
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("PATHTYPE_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 == PATHTYPE_NONE)");
    else if (t == PATH_TYPE_TYPE) return Texts("((", value, ").$tag == PATHTYPE_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")), "; })");
}