aboutsummaryrefslogtreecommitdiff
path: root/src/compile/fieldaccess.c
blob: cca9d9e6413d0eb86d15c4822341f4df8ea6ced1 (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
// This file defines how to compile field accessing like `foo.x`

#include "../ast.h"
#include "../config.h"
#include "../environment.h"
#include "../stdlib/tables.h"
#include "../stdlib/text.h"
#include "../util.h"
#include "../typecheck.h"
#include "compilation.h"

Text_t compile_field_access(env_t *env, ast_t *ast) {
    DeclareMatch(f, ast, FieldAccess);
    type_t *fielded_t = get_type(env, f->fielded);
    type_t *value_t = value_type(fielded_t);
    switch (value_t->tag) {
    case TypeInfoType: {
        DeclareMatch(info, value_t, TypeInfoType);
        if (f->field[0] == '_') {
            if (!type_eq(env->current_type, info->type))
                code_err(ast, "Fields that start with underscores are not "
                              "accessible on outside of the type definition.");
        }
        binding_t *b = get_binding(info->env, f->field);
        if (!b) code_err(ast, "I couldn't find the field '", f->field, "' on this type");
        if (b->code.length == 0) code_err(ast, "I couldn't figure out how to compile this field");
        return b->code;
    }
    case TextType: {
        const char *lang = Match(value_t, TextType)->lang;
        if (lang && streq(f->field, "text")) {
            Text_t text = compile_to_pointer_depth(env, f->fielded, 0, false);
            return Texts("((Text_t)", text, ")");
        } else if (streq(f->field, "length")) {
            return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)");
        }
        code_err(ast, "There is no '", f->field, "' field on ", type_to_text(value_t), " values");
    }
    case StructType: {
        return compile_struct_field_access(env, ast);
    }
    case EnumType: {
        return compile_enum_field_access(env, ast);
    }
    case ListType: {
        if (streq(f->field, "length"))
            return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)");
        code_err(ast, "There is no ", f->field, " field on lists");
    }
    case TableType: {
        if (streq(f->field, "length")) {
            return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)");
        } else if (streq(f->field, "keys")) {
            return Texts("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)");
        } else if (streq(f->field, "values")) {
            DeclareMatch(table, value_t, TableType);
            Text_t offset = Texts("offsetof(struct { ", compile_declaration(table->key_type, Text("k")), "; ",
                                  compile_declaration(table->value_type, Text("v")), "; }, v)");
            return Texts("({ List_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false),
                         ").entries;\n"
                         "LIST_INCREF(*entries);\n"
                         "List_t values = *entries;\n"
                         "values.data += ",
                         offset,
                         ";\n"
                         "values; })");
        } else if (streq(f->field, "fallback")) {
            return Texts("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false),
                         ").fallback; _fallback ? *_fallback : NONE_TABLE; })");
        }
        code_err(ast, "There is no '", f->field, "' field on tables");
    }
    case ModuleType: {
        const char *name = Match(value_t, ModuleType)->name;
        env_t *module_env = Table$str_get(*env->imports, name);
        return compile(module_env, WrapAST(ast, Var, f->field));
    }
    default: code_err(ast, "Field accesses are not supported on ", type_to_text(fielded_t), " values");
    }
}