forked from lix-project/lix
6526d1676b
Pos objects are somewhat wasteful as they duplicate the origin file name and input type for each object. on files that produce more than one Pos when parsed this a sizeable waste of memory (one pointer per Pos). the same goes for ptr<Pos> on 64 bit machines: parsing enough source to require 8 bytes to locate a position would need at least 8GB of input and 64GB of expression memory. it's not likely that we'll hit that any time soon, so we can use a uint32_t index to locate positions instead.
128 lines
3.8 KiB
C++
128 lines
3.8 KiB
C++
#include "command.hh"
|
|
#include "common-args.hh"
|
|
#include "shared.hh"
|
|
#include "store-api.hh"
|
|
#include "eval.hh"
|
|
#include "eval-inline.hh"
|
|
#include "json.hh"
|
|
#include "value-to-json.hh"
|
|
#include "progress-bar.hh"
|
|
|
|
using namespace nix;
|
|
|
|
struct CmdEval : MixJSON, InstallableCommand
|
|
{
|
|
bool raw = false;
|
|
std::optional<std::string> apply;
|
|
std::optional<Path> writeTo;
|
|
|
|
CmdEval() : InstallableCommand(true /* supportReadOnlyMode */)
|
|
{
|
|
addFlag({
|
|
.longName = "raw",
|
|
.description = "Print strings without quotes or escaping.",
|
|
.handler = {&raw, true},
|
|
});
|
|
|
|
addFlag({
|
|
.longName = "apply",
|
|
.description = "Apply the function *expr* to each argument.",
|
|
.labels = {"expr"},
|
|
.handler = {&apply},
|
|
});
|
|
|
|
addFlag({
|
|
.longName = "write-to",
|
|
.description = "Write a string or attrset of strings to *path*.",
|
|
.labels = {"path"},
|
|
.handler = {&writeTo},
|
|
});
|
|
}
|
|
|
|
std::string description() override
|
|
{
|
|
return "evaluate a Nix expression";
|
|
}
|
|
|
|
std::string doc() override
|
|
{
|
|
return
|
|
#include "eval.md"
|
|
;
|
|
}
|
|
|
|
Category category() override { return catSecondary; }
|
|
|
|
void run(ref<Store> store) override
|
|
{
|
|
if (raw && json)
|
|
throw UsageError("--raw and --json are mutually exclusive");
|
|
|
|
auto state = getEvalState();
|
|
|
|
auto [v, pos] = installable->toValue(*state);
|
|
PathSet context;
|
|
|
|
if (apply) {
|
|
auto vApply = state->allocValue();
|
|
state->eval(state->parseExprFromString(*apply, absPath(".")), *vApply);
|
|
auto vRes = state->allocValue();
|
|
state->callFunction(*vApply, *v, *vRes, noPos);
|
|
v = vRes;
|
|
}
|
|
|
|
if (writeTo) {
|
|
stopProgressBar();
|
|
|
|
if (pathExists(*writeTo))
|
|
throw Error("path '%s' already exists", *writeTo);
|
|
|
|
std::function<void(Value & v, const PosIdx pos, const Path & path)> recurse;
|
|
|
|
recurse = [&](Value & v, const PosIdx pos, const Path & path)
|
|
{
|
|
state->forceValue(v, pos);
|
|
if (v.type() == nString)
|
|
// FIXME: disallow strings with contexts?
|
|
writeFile(path, v.string.s);
|
|
else if (v.type() == nAttrs) {
|
|
if (mkdir(path.c_str(), 0777) == -1)
|
|
throw SysError("creating directory '%s'", path);
|
|
for (auto & attr : *v.attrs)
|
|
try {
|
|
if (attr.name == "." || attr.name == "..")
|
|
throw Error("invalid file name '%s'", attr.name);
|
|
recurse(*attr.value, attr.pos, path + "/" + std::string(attr.name));
|
|
} catch (Error & e) {
|
|
e.addTrace(
|
|
state->positions[attr.pos],
|
|
hintfmt("while evaluating the attribute '%s'", attr.name));
|
|
throw;
|
|
}
|
|
}
|
|
else
|
|
throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]);
|
|
};
|
|
|
|
recurse(*v, pos, *writeTo);
|
|
}
|
|
|
|
else if (raw) {
|
|
stopProgressBar();
|
|
std::cout << *state->coerceToString(noPos, *v, context);
|
|
}
|
|
|
|
else if (json) {
|
|
JSONPlaceholder jsonOut(std::cout);
|
|
printValueAsJSON(*state, true, *v, pos, jsonOut, context);
|
|
}
|
|
|
|
else {
|
|
state->forceValueDeep(*v);
|
|
logger->cout("%s", *v);
|
|
}
|
|
}
|
|
};
|
|
|
|
static auto rCmdEval = registerCommand<CmdEval>("eval");
|