diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2020-01-21 21:18:52 +0100 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2020-01-21 21:18:52 +0100 |
commit | 6fadb3fc03a1a3d51a1aaea003bfbe347c3879b3 (patch) | |
tree | 2bd6687cc2c2cb83a9ed4666ed06afb5c8a967ec /src/libexpr | |
parent | 62f712c8aedc08d6478ce9b380a2cf317d7e4d3a (diff) | |
parent | aef635da78d33bf679f49fd10e7130d918a82549 (diff) |
Merge remote-tracking branch 'origin/master' into flakes
Diffstat (limited to 'src/libexpr')
-rw-r--r-- | src/libexpr/eval.cc | 11 | ||||
-rw-r--r-- | src/libexpr/json-to-value.cc | 244 | ||||
-rw-r--r-- | src/libexpr/primops.cc | 2 |
3 files changed, 137 insertions, 120 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 427a6f299..852e8aa11 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -566,9 +566,9 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun throw TypeError(format(s) % fun.showNamePos() % s2 % pos); } -LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos)) +LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s1, const Pos & pos)) { - throw AssertionError(format(s) % pos); + throw AssertionError(format(s) % s1 % pos); } LocalNoInlineNoReturn(void throwUndefinedVarError(const char * s, const string & s1, const Pos & pos)) @@ -1302,8 +1302,11 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v) void ExprAssert::eval(EvalState & state, Env & env, Value & v) { - if (!state.evalBool(env, cond, pos)) - throwAssertionError("assertion failed at %1%", pos); + if (!state.evalBool(env, cond, pos)) { + std::ostringstream out; + cond->show(out); + throwAssertionError("assertion '%1%' failed at %2%", out.str(), pos); + } body->eval(state, env, v); } diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 8bae986f9..1fdce1983 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -1,149 +1,161 @@ #include "json-to-value.hh" -#include <cstring> +#include <variant> +#include <nlohmann/json.hpp> -namespace nix { +using json = nlohmann::json; +using std::unique_ptr; +namespace nix { -static void skipWhitespace(const char * & s) -{ - while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++; -} +// for more information, refer to +// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp +class JSONSax : nlohmann::json_sax<json> { + class JSONState { + protected: + unique_ptr<JSONState> parent; + Value * v; + public: + virtual unique_ptr<JSONState> resolve(EvalState &) + { + throw std::logic_error("tried to close toplevel json parser state"); + }; + explicit JSONState(unique_ptr<JSONState>&& p) : parent(std::move(p)), v(nullptr) {}; + explicit JSONState(Value* v) : v(v) {}; + JSONState(JSONState& p) = delete; + Value& value(EvalState & state) + { + if (v == nullptr) + v = state.allocValue(); + return *v; + }; + virtual ~JSONState() {}; + virtual void add() {}; + }; + + class JSONObjectState : public JSONState { + using JSONState::JSONState; + ValueMap attrs = ValueMap(); + virtual unique_ptr<JSONState> resolve(EvalState & state) override + { + Value& v = parent->value(state); + state.mkAttrs(v, attrs.size()); + for (auto & i : attrs) + v.attrs->push_back(Attr(i.first, i.second)); + return std::move(parent); + } + virtual void add() override { v = nullptr; }; + public: + void key(string_t& name, EvalState & state) + { + attrs[state.symbols.create(name)] = &value(state); + } + }; + + class JSONListState : public JSONState { + ValueVector values = ValueVector(); + virtual unique_ptr<JSONState> resolve(EvalState & state) override + { + Value& v = parent->value(state); + state.mkList(v, values.size()); + for (size_t n = 0; n < values.size(); ++n) { + v.listElems()[n] = values[n]; + } + return std::move(parent); + } + virtual void add() override { + values.push_back(v); + v = nullptr; + }; + public: + JSONListState(unique_ptr<JSONState>&& p, std::size_t reserve) : JSONState(std::move(p)) + { + values.reserve(reserve); + } + }; + EvalState & state; + unique_ptr<JSONState> rs; -static string parseJSONString(const char * & s) -{ - string res; - if (*s++ != '"') throw JSONParseError("expected JSON string"); - while (*s != '"') { - if (!*s) throw JSONParseError("got end-of-string in JSON string"); - if (*s == '\\') { - s++; - if (*s == '"') res += '"'; - else if (*s == '\\') res += '\\'; - else if (*s == '/') res += '/'; - else if (*s == '/') res += '/'; - else if (*s == 'b') res += '\b'; - else if (*s == 'f') res += '\f'; - else if (*s == 'n') res += '\n'; - else if (*s == 'r') res += '\r'; - else if (*s == 't') res += '\t'; - else if (*s == 'u') throw JSONParseError("\\u characters in JSON strings are currently not supported"); - else throw JSONParseError("invalid escaped character in JSON string"); - s++; - } else - res += *s++; + template<typename T, typename... Args> inline bool handle_value(T f, Args... args) + { + f(rs->value(state), args...); + rs->add(); + return true; } - s++; - return res; -} +public: + JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {}; -static void parseJSON(EvalState & state, const char * & s, Value & v) -{ - skipWhitespace(s); - - if (!*s) throw JSONParseError("expected JSON value"); - - if (*s == '[') { - s++; - ValueVector values; - values.reserve(128); - skipWhitespace(s); - while (1) { - if (values.empty() && *s == ']') break; - Value * v2 = state.allocValue(); - parseJSON(state, s, *v2); - values.push_back(v2); - skipWhitespace(s); - if (*s == ']') break; - if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element"); - s++; - } - s++; - state.mkList(v, values.size()); - for (size_t n = 0; n < values.size(); ++n) - v.listElems()[n] = values[n]; + bool null() + { + return handle_value(mkNull); } - else if (*s == '{') { - s++; - ValueMap attrs; - while (1) { - skipWhitespace(s); - if (attrs.empty() && *s == '}') break; - string name = parseJSONString(s); - skipWhitespace(s); - if (*s != ':') throw JSONParseError("expected ':' in JSON object"); - s++; - Value * v2 = state.allocValue(); - parseJSON(state, s, *v2); - attrs[state.symbols.create(name)] = v2; - skipWhitespace(s); - if (*s == '}') break; - if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member"); - s++; - } - state.mkAttrs(v, attrs.size()); - for (auto & i : attrs) - v.attrs->push_back(Attr(i.first, i.second)); - v.attrs->sort(); - s++; + bool boolean(bool val) + { + return handle_value(mkBool, val); } - else if (*s == '"') { - mkString(v, parseJSONString(s)); + bool number_integer(number_integer_t val) + { + return handle_value(mkInt, val); } - else if (isdigit(*s) || *s == '-' || *s == '.' ) { - // Buffer into a string first, then use built-in C++ conversions - std::string tmp_number; - ValueType number_type = tInt; + bool number_unsigned(number_unsigned_t val) + { + return handle_value(mkInt, val); + } - while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') { - if (*s == '.' || *s == 'e' || *s == 'E') - number_type = tFloat; - tmp_number += *s++; - } + bool number_float(number_float_t val, const string_t& s) + { + return handle_value(mkFloat, val); + } - try { - if (number_type == tFloat) - mkFloat(v, stod(tmp_number)); - else - mkInt(v, stol(tmp_number)); - } catch (std::invalid_argument & e) { - throw JSONParseError("invalid JSON number"); - } catch (std::out_of_range & e) { - throw JSONParseError("out-of-range JSON number"); - } + bool string(string_t& val) + { + return handle_value<void(Value&, const char*)>(mkString, val.c_str()); } - else if (strncmp(s, "true", 4) == 0) { - s += 4; - mkBool(v, true); + bool start_object(std::size_t len) + { + rs = std::make_unique<JSONObjectState>(std::move(rs)); + return true; } - else if (strncmp(s, "false", 5) == 0) { - s += 5; - mkBool(v, false); + bool key(string_t& name) + { + dynamic_cast<JSONObjectState*>(rs.get())->key(name, state); + return true; } - else if (strncmp(s, "null", 4) == 0) { - s += 4; - mkNull(v); + bool end_object() { + rs = rs->resolve(state); + rs->add(); + return true; } - else throw JSONParseError("unrecognised JSON value"); -} + bool end_array() { + return end_object(); + } + bool start_array(size_t len) { + rs = std::make_unique<JSONListState>(std::move(rs), + len != std::numeric_limits<size_t>::max() ? len : 128); + return true; + } + + bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) { + throw JSONParseError(ex.what()); + } +}; void parseJSON(EvalState & state, const string & s_, Value & v) { - const char * s = s_.c_str(); - parseJSON(state, s, v); - skipWhitespace(s); - if (*s) throw JSONParseError(format("expected end-of-string while parsing JSON value: %1%") % s); + JSONSax parser(state, v); + bool res = json::sax_parse(s_, &parser); + if (!res) + throw JSONParseError("Invalid JSON Value"); } - } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2df9277c9..af6c91954 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -732,6 +732,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * the hash. */ for (auto & i : outputs) { if (!jsonObject) drv.env[i] = ""; + drv.outputs.insert_or_assign(i, + DerivationOutput(StorePath::dummy.clone(), "", "")); } Hash h = hashDerivationModulo(*state.store, Derivation(drv), true); |