summaryrefslogtreecommitdiffstatshomepage
path: root/py/emitcommon.c
blob: 95a00543ad19ae1f80513a4f28c27232723b3a4b (plain) (blame)
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
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>

#include "misc.h"
#include "lexer.h"
#include "machine.h"
#include "parse.h"
#include "scope.h"
#include "runtime.h"
#include "emit.h"

#define EMIT(fun, arg...) (emit_method_table->fun(emit, ##arg))

void emit_common_load_id(pass_kind_t pass, scope_t *scope, emit_t *emit, const emit_method_table_t *emit_method_table, qstr qstr___class__, qstr qstr) {
    id_info_t *id_info = NULL;
    if (pass == PASS_1) {
        // name adding/lookup
        bool added;
        id_info = scope_find_or_add_id(scope, qstr, &added);
        if (added) {
            if (strcmp(qstr_str(qstr), "AssertionError") == 0) {
                id_info->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
                // TODO how much of a hack is this?
            } else if (strcmp(qstr_str(qstr), "super") == 0 && scope->kind == SCOPE_FUNCTION) {
                // special case, super is a global, and also counts as use of __class__
                id_info->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
                id_info_t *id_info2 = scope_find_local_in_parent(scope, qstr___class__);
                if (id_info2 != NULL) {
                    id_info2 = scope_find_or_add_id(scope, qstr___class__, &added);
                    if (added) {
                        id_info2->kind = ID_INFO_KIND_FREE;
                        scope_close_over_in_parents(scope, qstr___class__);
                    }
                }
            } else {
                id_info_t *id_info2 = scope_find_local_in_parent(scope, qstr);
                if (id_info2 != NULL && (id_info2->kind == ID_INFO_KIND_LOCAL || id_info2->kind == ID_INFO_KIND_CELL || id_info2->kind == ID_INFO_KIND_FREE)) {
                    id_info->kind = ID_INFO_KIND_FREE;
                    scope_close_over_in_parents(scope, qstr);
                } else {
                    id_info->kind = ID_INFO_KIND_GLOBAL_IMPLICIT;
                }
            }
        }
    } else {
        id_info = scope_find(scope, qstr);
    }

    assert(id_info != NULL); // TODO can this ever fail?

    // call the emit backend with the correct code
    if (id_info == NULL || id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
        EMIT(load_name, qstr);
    } else if (id_info->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
        EMIT(load_global, qstr);
    } else if (id_info->kind == ID_INFO_KIND_LOCAL) {
        EMIT(load_fast, qstr, id_info->local_num);
    } else if (id_info->kind == ID_INFO_KIND_CELL || id_info->kind == ID_INFO_KIND_FREE) {
        EMIT(load_deref, qstr);
    } else {
        assert(0);
    }
}

static id_info_t *get_id_for_modification(pass_kind_t pass, scope_t *scope, qstr qstr) {
    id_info_t *id_info = NULL;
    if (pass == PASS_1) {
        // name adding/lookup
        bool added;
        id_info = scope_find_or_add_id(scope, qstr, &added);
        if (added) {
            if (scope->kind == SCOPE_MODULE || scope->kind == SCOPE_CLASS) {
                id_info->kind = ID_INFO_KIND_GLOBAL_IMPLICIT;
            } else {
                id_info->kind = ID_INFO_KIND_LOCAL;
            }
        } else if (scope->kind >= SCOPE_FUNCTION && scope->kind <= SCOPE_GEN_EXPR && id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
            // rebind as a local variable
            id_info->kind = ID_INFO_KIND_LOCAL;
        }
    } else {
        id_info = scope_find(scope, qstr);
    }

    assert(id_info != NULL); // TODO can this ever fail?

    return id_info;
}

void emit_common_store_id(pass_kind_t pass, scope_t *scope, emit_t *emit, const emit_method_table_t *emit_method_table, qstr qstr) {
    // create/get the id info
    id_info_t *id = get_id_for_modification(pass, scope, qstr);

    // call the emit backend with the correct code
    if (id == NULL || id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
        EMIT(store_name, qstr);
    } else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
        EMIT(store_global, qstr);
    } else if (id->kind == ID_INFO_KIND_LOCAL) {
        EMIT(store_fast, qstr, id->local_num);
    } else if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
        EMIT(store_deref, qstr);
    } else {
        assert(0);
    }
}

void emit_common_delete_id(pass_kind_t pass, scope_t *scope, emit_t *emit, const emit_method_table_t *emit_method_table, qstr qstr) {
    // create/get the id info
    id_info_t *id = get_id_for_modification(pass, scope, qstr);

    // call the emit backend with the correct code
    if (id == NULL || id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
        EMIT(delete_name, qstr);
    } else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
        EMIT(delete_global, qstr);
    } else if (id->kind == ID_INFO_KIND_LOCAL) {
        EMIT(delete_fast, qstr, id->local_num);
    } else if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
        EMIT(delete_deref, qstr);
    } else {
        assert(0);
    }
}