aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/attr-set.hh
blob: 82c348287180e8068a6a2aad8f9e6f17bf227824 (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
#pragma once

#include "nixexpr.hh"
#include "symbol-table.hh"

#include <algorithm>
#include <optional>

namespace nix {


class EvalState;
struct Value;

/* Map one attribute name to its value. */
struct Attr
{
    Symbol name;
    Value * value;
    ptr<Pos> pos;
    Attr(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
        : name(name), value(value), pos(pos) { };
    Attr() : pos(&noPos) { };
    bool operator < (const Attr & a) const
    {
        return name < a.name;
    }
};

/* Bindings contains all the attributes of an attribute set. It is defined
   by its size and its capacity, the capacity being the number of Attr
   elements allocated after this structure, while the size corresponds to
   the number of elements already inserted in this structure. */
class Bindings
{
public:
    typedef uint32_t size_t;
    ptr<Pos> pos;

private:
    size_t size_, capacity_;
    Attr attrs[0];

    Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { }
    Bindings(const Bindings & bindings) = delete;

public:
    size_t size() const { return size_; }

    bool empty() const { return !size_; }

    typedef Attr * iterator;

    void push_back(const Attr & attr)
    {
        assert(size_ < capacity_);
        attrs[size_++] = attr;
    }

    iterator find(const Symbol & name)
    {
        Attr key(name, 0);
        iterator i = std::lower_bound(begin(), end(), key);
        if (i != end() && i->name == name) return i;
        return end();
    }

    Attr * get(const Symbol & name)
    {
        Attr key(name, 0);
        iterator i = std::lower_bound(begin(), end(), key);
        if (i != end() && i->name == name) return &*i;
        return nullptr;
    }

    Attr & need(const Symbol & name, const Pos & pos = noPos)
    {
        auto a = get(name);
        if (!a)
            throw Error({
                .msg = hintfmt("attribute '%s' missing", name),
                .errPos = pos
            });

        return *a;
    }

    iterator begin() { return &attrs[0]; }
    iterator end() { return &attrs[size_]; }

    Attr & operator[](size_t pos)
    {
        return attrs[pos];
    }

    void sort();

    size_t capacity() { return capacity_; }

    /* Returns the attributes in lexicographically sorted order. */
    std::vector<const Attr *> lexicographicOrder() const
    {
        std::vector<const Attr *> res;
        res.reserve(size_);
        for (size_t n = 0; n < size_; n++)
            res.emplace_back(&attrs[n]);
        std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
            return (const string &) a->name < (const string &) b->name;
        });
        return res;
    }

    friend class EvalState;
};

/* A wrapper around Bindings that ensures that its always in sorted
   order at the end. The only way to consume a BindingsBuilder is to
   call finish(), which sorts the bindings. */
class BindingsBuilder
{
    Bindings * bindings;

public:

    EvalState & state;

    BindingsBuilder(EvalState & state, Bindings * bindings)
        : bindings(bindings), state(state)
    { }

    void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
    {
        insert(Attr(name, value, pos));
    }

    void insert(const Attr & attr)
    {
        bindings->push_back(attr);
    }

    Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos));

    Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos));

    Bindings * finish()
    {
        bindings->sort();
        return bindings;
    }

    Bindings * alreadySorted()
    {
        return bindings;
    }
};

}