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