hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/eval.c
blob: b0ad4544ab480e1a192f0bdfd7fb5d6e4a1a18ee (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>

#include "eval.h"

static int evaluate_type(const struct Node* node, struct Type* out);
static int evaluate_value(const struct Node* node, int64_t* out);
static int evaluate_condition(const struct Node* node, bool* out);

static int complain(const struct Node* node, const char* message)
{
    (void)fprintf(stderr, "line %d: %s\n", node->line, message);
    return 1;
}

static int evaluate_operands(const struct Node* node, int64_t* left, int64_t* right)
{
    if (evaluate_value(node->first, left))
        return 1;

    return evaluate_value(node->second, right);
}

static int evaluate_value(const struct Node* node, int64_t* out)
{
    int64_t left = 0;
    int64_t right = 0;
    struct Type type;
    bool truth = false;
    switch (node->kind)
    {
        case VALUE_DIFFERENCE:
            *out = (int64_t)node->first->character - (int64_t)node->second->character;
            return 0;
        case VALUE_SUM:
            if (evaluate_operands(node, &left, &right))
                return 1;

            *out = left + right;
            return 0;
        case VALUE_PRODUCT:
            if (evaluate_operands(node, &left, &right))
                return 1;

            *out = left * right;
            return 0;
        case VALUE_QUOTIENT:
            if (evaluate_operands(node, &left, &right))
                return 1;

            if (right == 0)
                return complain(node, "division by zero");

            *out = left / right;
            return 0;
        case VALUE_EXTENT:
        case VALUE_TUPLE_SIZE:
            if (evaluate_type(node->first, &type))
                return 1;
            *out = type.kind == KIND_ARRAY ? type.number : 0;
            return 0;
        case VALUE_INTEGRAL_CONSTANT: return evaluate_value(node->first, out);
        case VALUE_CONDITIONAL:
            if (evaluate_condition(node->first, &truth))
                return 1;

            return evaluate_value(truth ? node->second : node->third, out);
        default: break;
    }

    assert(!"the parser handed a non-value where a value goes");
    return 1;
}

static int evaluate_type(const struct Node* node, struct Type* out)
{
    out->kind = KIND_VOID;
    out->number = 0;
    struct Type element;
    bool truth = false;
    switch (node->kind)
    {
        case TYPE_VOID: return 0;
        case TYPE_INT32: out->kind = KIND_INT32; return 0;
        case TYPE_BYTE: out->kind = KIND_BYTE; return 0;
        case TYPE_SIZE: out->kind = KIND_SIZE; return 0;
        case TYPE_REMOVE_CV: return evaluate_type(node->first, out);
        case TYPE_ARRAY:
            if (evaluate_type(node->first, &element) || evaluate_value(node->second, &out->number))
                return 1;
            out->kind = KIND_ARRAY;
            return 0;
        case TYPE_CONDITIONAL:
            if (evaluate_condition(node->first, &truth))
                return 1;
            return evaluate_type(truth ? node->second : node->third, out);
        case TYPE_DECLTYPE:
            if (evaluate_value(node->first, &out->number))
                return 1;
            out->kind = KIND_VALUE;
            return 0;
        default: break;
    }

    assert(!"the parser handed a non-type where a type goes");
    return 1;
}

static bool same_type(const struct Type* left, const struct Type* right)
{
    return left->kind == right->kind && left->number == right->number;
}

static int evaluate_condition(const struct Node* node, bool* out)
{
    int64_t left = 0;
    int64_t right = 0;
    struct Type left_type;
    struct Type right_type;
    *out = false;
    switch (node->kind)
    {
        case COND_TRUE: *out = true; return 0;
        case COND_FALSE: return 0;
        case COND_IS_SAME:
            if (evaluate_type(node->first, &left_type) || evaluate_type(node->second, &right_type))
                return 1;

            *out = same_type(&left_type, &right_type);
            return 0;
        case COND_EQUAL:
            if (evaluate_operands(node, &left, &right))
                return 1;

            *out = left == right;
            return 0;
        case COND_LESS:
            if (evaluate_operands(node, &left, &right))
                return 1;

            *out = left < right;
            return 0;
        default: break;
    }

    assert(!"the parser handed a non-condition where a condition goes");
    return 1;
}

static enum RunStatus emit(const struct Node* node)
{
    struct Type type;
    if (evaluate_type(node->first, &type))
        return RUN_FAILED;

    switch (type.kind)
    {
        case KIND_VOID: break;
        case KIND_INT32: (void)printf("int32_t\n"); break;
        case KIND_BYTE: (void)printf("byte\n"); break;
        case KIND_SIZE: (void)printf("size_t\n"); break;
        case KIND_ARRAY: (void)printf("array[%" PRId64 "]\n", type.number); break;
        case KIND_VALUE: (void)printf("%" PRId64 "\n", type.number); break;
    }

    return RUN_DONE;
}

static enum RunStatus static_assert_statement(const struct Node* node)
{
    bool truth = false;
    if (evaluate_condition(node->first, &truth))
        return RUN_FAILED;

    if (!truth)
    {
        (void)complain(node, "static assertion failed, at runtime, which is the best time");
        return RUN_FAILED;
    }

    return RUN_DONE;
}

static enum RunStatus execute_one(const struct Node* node)
{
    switch (node->kind)
    {
        case STMT_EMIT: return emit(node);
        case STMT_STATIC_ASSERT: return static_assert_statement(node);
        default: break;
    }

    assert(!"the parser handed a non-statement where a statement goes");
    return RUN_FAILED;
}

enum RunStatus execute(const struct Node* statement)
{
    while (statement != NULL)
    {
        enum RunStatus status = execute_one(statement);
        if (status != RUN_DONE)
            return status;

        statement = statement->next;
    }

    return RUN_DONE;
}