about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/primops/fromTOML.cc
blob: e3d2a4940769f73cb443b4f1a9dd250856f70015 (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
#include "cpptoml/cpptoml.h"
#include "libexpr/eval-inline.hh"
#include "libexpr/primops.hh"

namespace nix {

static void prim_fromTOML(EvalState& state, const Pos& pos, Value** args,
                          Value& v) {
  using namespace cpptoml;

  auto toml = state.forceStringNoCtx(*args[0], pos);

  std::istringstream tomlStream(toml);

  std::function<void(Value&, std::shared_ptr<base>)> visit;

  visit = [&](Value& v, std::shared_ptr<base> t) {
    if (auto t2 = t->as_table()) {
      size_t size = 0;
      for (auto& i : *t2) {
        (void)i;
        size++;
      }

      state.mkAttrs(v, size);

      for (auto& i : *t2) {
        auto& v2 = *state.allocAttr(v, state.symbols.Create(i.first));

        if (auto i2 = i.second->as_table_array()) {
          size_t size2 = i2->get().size();
          state.mkList(v2, size2);
          for (size_t j = 0; j < size2; ++j) {
            visit(*((*v2.list)[j] = state.allocValue()), i2->get()[j]);
          }
        } else {
          visit(v2, i.second);
        }
      }
    }

    else if (auto t2 = t->as_array()) {
      size_t size = t2->get().size();

      state.mkList(v, size);

      for (size_t i = 0; i < size; ++i) {
        visit(*((*v.list)[i] = state.allocValue()), t2->get()[i]);
      }
    }

    // Handle cases like 'a = [[{ a = true }]]', which IMHO should be
    // parsed as a array containing an array containing a table,
    // but instead are parsed as an array containing a table array
    // containing a table.
    else if (auto t2 = t->as_table_array()) {
      size_t size = t2->get().size();

      state.mkList(v, size);

      for (size_t j = 0; j < size; ++j) {
        visit(*((*v.list)[j] = state.allocValue()), t2->get()[j]);
      }
    }

    else if (t->is_value()) {
      if (auto val = t->as<int64_t>()) {
        mkInt(v, val->get());
      } else if (auto val = t->as<NixFloat>()) {
        mkFloat(v, val->get());
      } else if (auto val = t->as<bool>()) {
        mkBool(v, val->get());
      } else if (auto val = t->as<std::string>()) {
        mkString(v, val->get());
      } else {
        throw EvalError("unsupported value type in TOML");
      }
    }

    else {
      abort();
    }
  };

  try {
    visit(v, parser(tomlStream).parse());
  } catch (std::runtime_error& e) {
    throw EvalError("while parsing a TOML string at %s: %s", pos, e.what());
  }
}

static RegisterPrimOp r("fromTOML", 1, prim_fromTOML);

}  // namespace nix