Add a toJSON primop

This commit is contained in:
Eelco Dolstra 2013-11-19 00:03:11 +01:00
parent 285df765b9
commit 77c13cdf56
10 changed files with 179 additions and 24 deletions

View file

@ -750,6 +750,18 @@ in foo</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.toJSON</function> <replaceable>e</replaceable></term>
<listitem><para>Return a string containing a JSON representation
of <replaceable>e</replaceable>. Strings, integers, booleans,
nulls and lists are mapped to their JSON equivalents. Sets
(except derivations) are represented as objects. Derivations are
translated to a JSON string containing the derivations output
path. Paths are copied to the store and represented as a JSON
string of the resulting store path.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
<listitem><para>Convert the string value

View file

@ -13,6 +13,11 @@
<itemizedlist>
<listitem>
<para>New built-in function: <function>builtins.toJSON</function>,
which returns a JSON representation of a value.</para>
</listitem>
<listitem><para><command>nix-setuid-helper</command> is
gone.</para></listitem>

View file

@ -2,13 +2,13 @@ pkglib_LTLIBRARIES = libexpr.la
libexpr_la_SOURCES = \
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \
names.cc
get-drvs.cc attr-path.cc value-to-xml.cc value-to-json.cc \
common-opts.cc names.cc
pkginclude_HEADERS = \
nixexpr.hh eval.hh eval-inline.hh lexer-tab.hh parser-tab.hh \
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
names.hh symbol-table.hh value.hh
get-drvs.hh attr-path.hh value-to-xml.hh value-to-json.hh \
common-opts.hh names.hh symbol-table.hh value.hh
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la @BDW_GC_LIBS@

View file

@ -1163,26 +1163,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (v.type == tPath) {
Path path(canonPath(v.path));
if (!copyToStore) return path;
if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
Path dstPath;
if (srcToStore[path] != "")
dstPath = srcToStore[path];
else {
dstPath = settings.readOnlyMode
? computeStorePathForPath(path).first
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
srcToStore[path] = dstPath;
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
% path % dstPath);
}
context.insert(dstPath);
return dstPath;
return copyToStore ? copyPathToStore(context, path) : path;
}
if (v.type == tAttrs) {
@ -1218,6 +1199,28 @@ string EvalState::coerceToString(Value & v, PathSet & context,
}
string EvalState::copyPathToStore(PathSet & context, const Path & path)
{
if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
Path dstPath;
if (srcToStore[path] != "")
dstPath = srcToStore[path];
else {
dstPath = settings.readOnlyMode
? computeStorePathForPath(path).first
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
srcToStore[path] = dstPath;
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
% path % dstPath);
}
context.insert(dstPath);
return dstPath;
}
Path EvalState::coerceToPath(Value & v, PathSet & context)
{
string path = coerceToString(v, context, false, false);

View file

@ -181,6 +181,8 @@ public:
string coerceToString(Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true);
string copyPathToStore(PathSet & context, const Path & path);
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */

View file

@ -5,6 +5,7 @@
#include "util.hh"
#include "archive.hh"
#include "value-to-xml.hh"
#include "value-to-json.hh"
#include "names.hh"
#include "eval-inline.hh"
@ -647,6 +648,18 @@ static void prim_toXML(EvalState & state, Value * * args, Value & v)
}
/* Convert the argument (which can be any Nix expression) to a JSON
string. Not all Nix expressions can be sensibly or completely
represented (e.g., functions). */
static void prim_toJSON(EvalState & state, Value * * args, Value & v)
{
std::ostringstream out;
PathSet context;
printValueAsJSON(state, true, *args[0], out, context);
mkString(v, out.str(), context);
}
/* Store a string in the Nix store as a source file that can be used
as an input by derivations. */
static void prim_toFile(EvalState & state, Value * * args, Value & v)
@ -1259,6 +1272,7 @@ void EvalState::createBaseEnv()
// Creating files
addPrimOp("__toXML", 1, prim_toXML);
addPrimOp("__toJSON", 1, prim_toJSON);
addPrimOp("__toFile", 2, prim_toFile);
addPrimOp("__filterSource", 2, prim_filterSource);

View file

@ -0,0 +1,93 @@
#include "value-to-xml.hh"
#include "xml-writer.hh"
#include "eval-inline.hh"
#include "util.hh"
#include <cstdlib>
namespace nix {
static void escapeJSON(std::ostream & str, const string & s)
{
str << "\"";
foreach (string::const_iterator, i, s)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
else if (*i == '\t') str << "\\t";
else str << *i;
str << "\"";
}
void printValueAsJSON(EvalState & state, bool strict,
Value & v, std::ostream & str, PathSet & context)
{
checkInterrupt();
if (strict) state.forceValue(v);
switch (v.type) {
case tInt:
str << v.integer;
break;
case tBool:
str << (v.boolean ? "true" : "false");
break;
case tString:
copyContext(v, context);
escapeJSON(str, v.string.s);
break;
case tPath:
escapeJSON(str, state.copyPathToStore(context, v.path));
break;
case tNull:
str << "null";
break;
case tAttrs: {
Bindings::iterator i = v.attrs->find(state.sOutPath);
if (i == v.attrs->end()) {
str << "{";
StringSet names;
foreach (Bindings::iterator, i, *v.attrs)
names.insert(i->name);
bool first = true;
foreach (StringSet::iterator, i, names) {
if (!first) str << ","; else first = false;
Attr & a(*v.attrs->find(state.symbols.create(*i)));
escapeJSON(str, *i);
str << ":";
printValueAsJSON(state, strict, *a.value, str, context);
}
str << "}";
} else
printValueAsJSON(state, strict, *i->value, str, context);
break;
}
case tList: {
str << "[";
bool first = true;
for (unsigned int n = 0; n < v.list.length; ++n) {
if (!first) str << ","; else first = false;
printValueAsJSON(state, strict, *v.list.elems[n], str, context);
}
str << "]";
break;
}
default:
throw TypeError(format("cannot convert %1% to JSON") % showType(v));
}
}
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "nixexpr.hh"
#include "eval.hh"
#include <string>
#include <map>
namespace nix {
void printValueAsJSON(EvalState & state, bool strict,
Value & v, std::ostream & out, PathSet & context);
}

View file

@ -0,0 +1 @@
"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3}"

View file

@ -0,0 +1,11 @@
builtins.toJSON
{ a = 123;
b = -456;
c = "foo";
d = "foo\n\"bar\"";
e = true;
f = false;
g = [ 1 2 3 ];
h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
i = 1 + 2;
}