diff --git a/src/libexpr/include/nix/expr/json-to-value.hh b/src/libexpr/include/nix/expr/json-to-value.hh index 2a2913d68..bccb456b4 100644 --- a/src/libexpr/include/nix/expr/json-to-value.hh +++ b/src/libexpr/include/nix/expr/json-to-value.hh @@ -12,6 +12,13 @@ struct Value; MakeError(JSONParseError, Error); -void parseJSON(EvalState & state, const std::string_view & s, Value & v); +/** + * Parse the string into the Value + * @param state + * @param s + * @param v + * @param allowComments Whether to allow comments in the JSON + */ +void parseJSON(EvalState & state, const std::string_view & s, Value & v, bool allowComments = false); } // namespace nix diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index f2fb03569..dc569cfb4 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -201,11 +201,11 @@ public: } }; -void parseJSON(EvalState & state, const std::string_view & s_, Value & v) +void parseJSON(EvalState & state, const std::string_view & s_, Value & v, bool allowComments) { JSONSax parser(state, v); bool res = json::sax_parse( - s_, &parser, nlohmann::detail::input_format_t::json, /* strict= */ true, /* ignore_comments= */ true); + s_, &parser, nlohmann::detail::input_format_t::json, /* strict= */ true, /* ignore_comments= */ allowComments); if (!res) throw JSONParseError("Invalid JSON Value"); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bafaccc83..feb5748f0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2470,11 +2470,11 @@ static RegisterPrimOp primop_toJSON({ }); /* Parse a JSON string to a value. */ -static void prim_fromJSON(EvalState & state, const PosIdx pos, Value ** args, Value & v) +static void prim_fromJSON(EvalState & state, const PosIdx pos, Value ** args, Value & v, bool allow_comments) { auto s = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.fromJSON"); try { - parseJSON(state, s, v); + parseJSON(state, s, v, allow_comments); } catch (JSONParseError & e) { e.addTrace(state.positions[pos], "while decoding a JSON string"); throw; @@ -2492,10 +2492,39 @@ static RegisterPrimOp primop_fromJSON({ ``` returns the value `{ x = [ 1 2 3 ]; y = null; }`. + )", + .fun = + [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + return prim_fromJSON(state, pos, args, v, false); + }, +}); + +static RegisterPrimOp primop_fromJSONC({ + .name = "__fromJSONC", + .args = {"e"}, + .doc = R"( + Convert a JSON string, potentially with comments, to a Nix value. + For example, + + ```nix + builtins.fromJSONC '' + { + // This is a comment + "x": [1, 2, 3], + /* This is another comment */ + "y": null + } + '' + ``` + + returns the value `{ x = [ 1 2 3 ]; y = null; }`. This function supports JSON with comments. )", - .fun = prim_fromJSON, + .fun = + [](EvalState & state, const PosIdx pos, Value ** args, Value & v) { + return prim_fromJSON(state, pos, args, v, true); + }, }); /* Store a string in the Nix store as a source file that can be used