Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions doc/manual/src/command-ref/nix-instantiate.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,50 @@ standard input.

- `--parse`\
Just parse the input files, and print their abstract syntax trees on
standard output in ATerm format.
standard output as a Nix expression.

- `--eval`\
Just parse and evaluate the input files, and print the resulting
values on standard output. No instantiation of store derivations
takes place.

> **Warning**
>
> This option produces ambiguous output which is not suitable for machine
> consumption. For example, these two Nix expressions print the same result
> despite having different types:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably they have the same type at this time, tThunk, maybe “despite not being equal” would be better? In any case, this is sort of the point of not forcing the value completely, nix eval will also print the same output for non-equal values/values where the type isn't the same.

Wouldn't be clearer to just say that the problem with the output is that it produces something that can be parsed as a Nix expression, but would not return the same result?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and actually it is ambiguous what the ambiguity is. It can be either

  • intentional ambiguity because --strict wasn't specified,
  • or ambiguity in the mind of the reader, who might "naively" (can't blame) read it is an expression instead of a mere value, while non-value expressions are not printed by this command.
rant: why lawyering about "non-value expression"

I don't think we should ever conflate values and their expression literal counterparts, but someone might take that view and I had to be explicit. "expression" should have been enough IMO. The thing represented by Expr.
We really need to define some language terminology in the manual, but I do feel that we should just improve this text first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #9755.

>
> ```console
> $ nix-instantiate --eval --expr '{ a = {}; }'
> { a = <CODE>; }
> $ nix-instantiate --eval --expr '{ a = <CODE>; }'
> { a = <CODE>; }
> ```
>
> For human-readable output, `nix eval` (experimental) is more informative:
>
> ```console
> $ nix-instantiate --eval --expr 'a: a'
> <LAMBDA>
> $ nix eval --expr 'a: a'
> «lambda @ «string»:1:1»
> ```
>
> For machine-readable output, the `--xml` option produces unambiguous
> output:
>
> ```console
> $ nix-instantiate --eval --xml --expr '{ foo = <CODE>; }'
> <?xml version='1.0' encoding='utf-8'?>
> <expr>
> <attrs>
> <attr column="3" line="1" name="foo">
> <unevaluated />
> </attr>
> </attrs>
> </expr>
> ```

- `--find-file`\
Look up the given files in Nix’s search path (as specified by the
`NIX_PATH` environment variable). If found, print the corresponding
Expand All @@ -61,11 +98,11 @@ standard input.

- `--json`\
When used with `--eval`, print the resulting value as an JSON
representation of the abstract syntax tree rather than as an ATerm.
representation of the abstract syntax tree rather than as a Nix expression.

- `--xml`\
When used with `--eval`, print the resulting value as an XML
representation of the abstract syntax tree rather than as an ATerm.
representation of the abstract syntax tree rather than as a Nix expression.
The schema is the same as that used by the [`toXML`
built-in](../language/builtins.md).

Expand Down Expand Up @@ -133,28 +170,29 @@ $ nix-instantiate --eval --xml --expr '1 + 2'
The difference between non-strict and strict evaluation:

```console
$ nix-instantiate --eval --xml --expr 'rec { x = "foo"; y = x; }'
...
<attr name="x">
<string value="foo" />
</attr>
<attr name="y">
<unevaluated />
</attr>
...
$ nix-instantiate --eval --xml --expr '{ x = {}; }'
<?xml version='1.0' encoding='utf-8'?>
<expr>
<attrs>
<attr column="3" line="1" name="x">
<unevaluated />
</attr>
</attrs>
</expr>
```

Note that `y` is left unevaluated (the XML representation doesn’t
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

y no longer exists in the example printouts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #9755.

attempt to show non-normal forms).

```console
$ nix-instantiate --eval --xml --strict --expr 'rec { x = "foo"; y = x; }'
...
<attr name="x">
<string value="foo" />
</attr>
<attr name="y">
<string value="foo" />
</attr>
...
$ nix-instantiate --eval --xml --strict --expr '{ x = {}; }'
<?xml version='1.0' encoding='utf-8'?>
<expr>
<attrs>
<attr column="3" line="1" name="x">
<attrs>
</attrs>
</attr>
</attrs>
</expr>
```
158 changes: 15 additions & 143 deletions src/libcmd/repl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,17 @@ struct NixRepl
void evalString(std::string s, Value & v);
void loadDebugTraceEnv(DebugTrace & dt);

typedef std::set<Value *> ValuesSeen;
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
void printValue(std::ostream & str,
Value & v,
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
{
::nix::printValue(*state, str, v, PrintOptions {
.ansiColors = true,
.force = true,
.derivationPaths = true,
.maxDepth = maxDepth
});
}
};

std::string removeWhitespace(std::string s)
Expand Down Expand Up @@ -708,7 +716,8 @@ bool NixRepl::processLine(std::string line)
else if (command == ":p" || command == ":print") {
Value v;
evalString(arg, v);
printValue(std::cout, v, 1000000000) << std::endl;
printValue(std::cout, v);
std::cout << std::endl;
}

else if (command == ":q" || command == ":quit") {
Expand Down Expand Up @@ -770,7 +779,8 @@ bool NixRepl::processLine(std::string line)
} else {
Value v;
evalString(line, v);
printValue(std::cout, v, 1) << std::endl;
printValue(std::cout, v, 1);
std::cout << std::endl;
}
}

Expand Down Expand Up @@ -892,144 +902,6 @@ void NixRepl::evalString(std::string s, Value & v)
}


std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth)
{
ValuesSeen seen;
return printValue(str, v, maxDepth, seen);
}




// FIXME: lot of cut&paste from Nix's eval.cc.
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen)
{
str.flush();
checkInterrupt();

state->forceValue(v, v.determinePos(noPos));

switch (v.type()) {

case nInt:
str << ANSI_CYAN << v.integer << ANSI_NORMAL;
break;

case nBool:
str << ANSI_CYAN;
printLiteralBool(str, v.boolean);
str << ANSI_NORMAL;
break;

case nString:
str << ANSI_WARNING;
printLiteralString(str, v.string_view());
str << ANSI_NORMAL;
break;

case nPath:
str << ANSI_GREEN << v.path().to_string() << ANSI_NORMAL; // !!! escaping?
break;

case nNull:
str << ANSI_CYAN "null" ANSI_NORMAL;
break;

case nAttrs: {
seen.insert(&v);

bool isDrv = state->isDerivation(v);

if (isDrv) {
str << "«derivation ";
Bindings::iterator i = v.attrs->find(state->sDrvPath);
NixStringContext context;
if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
else
str << "???";
str << "»";
}

else if (maxDepth > 0) {
str << "{ ";

typedef std::map<std::string, Value *> Sorted;
Sorted sorted;
for (auto & i : *v.attrs)
sorted.emplace(state->symbols[i.name], i.value);

for (auto & i : sorted) {
printAttributeName(str, i.first);
str << " = ";
if (seen.count(i.second))
str << "«repeated»";
else
try {
printValue(str, *i.second, maxDepth - 1, seen);
} catch (AssertionError & e) {
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
}
str << "; ";
}

str << "}";
} else
str << "{ ... }";

break;
}

case nList:
seen.insert(&v);

str << "[ ";
if (maxDepth > 0)
for (auto elem : v.listItems()) {
if (seen.count(elem))
str << "«repeated»";
else
try {
printValue(str, *elem, maxDepth - 1, seen);
} catch (AssertionError & e) {
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
}
str << " ";
}
else
str << "... ";
str << "]";
break;

case nFunction:
if (v.isLambda()) {
std::ostringstream s;
s << state->positions[v.lambda.fun->pos];
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
} else if (v.isPrimOp()) {
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
} else if (v.isPrimOpApp()) {
str << ANSI_BLUE "«primop-app»" ANSI_NORMAL;
} else {
abort();
}
break;

case nFloat:
str << v.fpoint;
break;

case nThunk:
case nExternal:
default:
str << ANSI_RED "«unknown»" ANSI_NORMAL;
break;
}

return str;
}


std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues)
Expand Down
Loading