1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4 
5 #include <libcurv/record.h>
6 #include <libcurv/exception.h>
7 
8 namespace curv {
9 
10 const char Record::name[] = "record";
11 
12 Value
getfield(Symbol_Ref field,const Context & cx) const13 Record::getfield(Symbol_Ref field, const Context& cx) const
14 {
15     Value val = find_field(field, cx);
16     if (val.is_missing())
17         throw Exception(cx, stringify(*this," does not contain the field .",field));
18     return val;
19 }
20 
21 Ternary
equal(const Record & rhs,const Context & cx) const22 Record::equal(const Record& rhs, const Context& cx) const
23 {
24     if (this->size() != rhs.size())
25         return Ternary::False;
26     Ternary result = Ternary::True;
27     for (auto i = iter(); !i->empty(); i->next()) {
28         if (!rhs.hasfield(i->key()))
29             return Ternary::False;
30         Ternary ter = i->value(cx).equal(rhs.getfield(i->key(),cx),cx);
31         if (ter == Ternary::False)
32             return Ternary::False;
33         result &= ter;
34     }
35     return result;
36 }
37 
38 void
each_field(const Context & cx,std::function<void (Symbol_Ref,Value)> visitor) const39 Record::each_field(
40     const Context& cx, std::function<void(Symbol_Ref,Value)> visitor) const
41 {
42     for (auto f = iter(); !f->empty(); f->next())
43         visitor(f->key(), f->value(cx));
44 }
45 
46 Shared<List>
fields() const47 Record::fields() const
48 {
49     auto list = List::make(size());
50     int i = 0;
51     for (auto f = iter(); !f->empty(); f->next()) {
52         list->at(i) = f->key().to_value();
53         ++i;
54     }
55     return {move(list)};
56 }
57 
58 std::pair<Symbol_Ref, Value>
value_to_variant(Value val,const Context & cx)59 value_to_variant(Value val, const Context& cx)
60 {
61     auto sym = maybe_symbol(val);
62     if (!sym.empty()) {
63         return std::pair<Symbol_Ref,Value>{sym, missing};
64     }
65     Shared<Record> rec = val.maybe<Record>();
66     if (rec && rec->size() == 1) {
67         auto i = rec->iter();
68         return std::pair<Symbol_Ref,Value>{i->key(), i->value(cx)};
69     }
70     throw Exception(cx, stringify(val, " is not a variant"));
71 }
72 
73 void
print_repr(std::ostream & out) const74 DRecord::print_repr(std::ostream& out) const
75 {
76     out << "{";
77     bool first = true;
78     for (auto i : fields_) {
79         if (!first) out << ",";
80         first = false;
81         out << i.first << ":";
82         i.second.print_repr(out);
83     }
84     out << "}";
85 }
86 
87 Value
find_field(Symbol_Ref name,const Context & cx) const88 DRecord::find_field(Symbol_Ref name, const Context& cx) const
89 {
90     auto fp = fields_.find(name);
91     if (fp != fields_.end())
92         return fp->second;
93     return missing;
94 }
95 
96 bool
hasfield(Symbol_Ref name) const97 DRecord::hasfield(Symbol_Ref name) const
98 {
99     auto fp = fields_.find(name);
100     return (fp != fields_.end());
101 }
102 
103 Shared<Record>
clone() const104 DRecord::clone() const
105 {
106     return make<DRecord>(fields_);
107 }
108 
109 Value*
ref_field(Symbol_Ref name,bool need_value,const Context & cx)110 DRecord::ref_field(Symbol_Ref name, bool need_value, const Context& cx)
111 {
112     auto fp = fields_.find(name);
113     if (fp != fields_.end())
114         return &fp->second;
115     throw Exception(cx, stringify(Value{share(*this)},
116         " has no field named ", name));
117 }
118 
update_drecord(Value arg,const Context & cx)119 Shared<DRecord> update_drecord(Value arg, const Context& cx)
120 {
121     // TODO: optimize: if arg is a drecord with usecount==1, make no copy
122     auto arec = arg.to<Record>(cx);
123     auto drec = make<DRecord>();
124     arec->each_field(cx, [&](Symbol_Ref id, Value val) -> void {
125         drec->fields_[id] = val;
126     });
127     return drec;
128 }
129 
130 } // namespace curv
131