diff --git a/doc/manual/rl-next/nix-instantiate-parse-json.md b/doc/manual/rl-next/nix-instantiate-parse-json.md new file mode 100644 index 000000000..e3a6e8162 --- /dev/null +++ b/doc/manual/rl-next/nix-instantiate-parse-json.md @@ -0,0 +1,15 @@ +--- +synopsis: '`nix-instantiate --parse` outputs json' +issues: [fj#487, 11124, 4726, 3077] +cls: [2190] +category: Breaking Changes +credits: [piegames, horrors] +--- + +`nix-instantiate --parse` does not print out the AST in a Nix-like format anymore. +Instead, it now prints a JSON representation of the internal expression tree. +Tooling should not rely on the stdout of `nix-instantiate --parse`. + +We've done our best to ensure that the new behavior is as compatible with the old one as possible. +If you depend on the old behavior in ways that are not covered anymore or are otherwise negatively affected by this change, +then please reach out so that we can find a sustainable solution together. diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index 479c9abcf..3ca5bbe96 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -35,7 +35,14 @@ standard input. - `--parse`\ Just parse the input files, and print their abstract syntax trees on - standard output as a Nix expression. + standard output. The output format of the AST depends on the current + internal representation and may change in the future. + + Tooling can use the stderr and exit code of `--parse` to check any + Nix code for correctness, but should not rely on stdout without careful + versioning. Note that `--parse` also checks for unbound variables. + In cases where this is undesired, `with {};` can be prepended + to the program to transform all such parse errors into eval errors. - `--eval`\ Just parse and evaluate the input files, and print the resulting diff --git a/lix/legacy/nix-instantiate.cc b/lix/legacy/nix-instantiate.cc index 13a05c845..aaf0b1701 100644 --- a/lix/legacy/nix-instantiate.cc +++ b/lix/legacy/nix-instantiate.cc @@ -31,7 +31,7 @@ void processExpr(EvalState & state, const Strings & attrPaths, bool evalOnly, OutputKind output, bool location, Expr & e) { if (parseOnly) { - e.show(state.ctx.symbols, std::cout); + std::cout << e.toJSON(state.ctx.symbols).dump(2); std::cout << "\n"; return; } diff --git a/lix/libexpr/nixexpr.cc b/lix/libexpr/nixexpr.cc index d7844d6a7..851186b0b 100644 --- a/lix/libexpr/nixexpr.cc +++ b/lix/libexpr/nixexpr.cc @@ -8,6 +8,8 @@ #include #include +using json = nlohmann::json; + namespace nix { ExprBlackHole eBlackHole; @@ -29,63 +31,80 @@ AttrName::AttrName(PosIdx pos, std::unique_ptr e) : pos(pos), expr(std::mo { } -void Expr::show(const SymbolTable & symbols, std::ostream & str) const +json Expr::toJSON(const SymbolTable & symbols) const { abort(); } -void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const +json ExprInt::toJSON(const SymbolTable & symbols) const { - str << n; + return { + {"_type", "ExprInt"}, + {"value", n.value} + }; } -void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const +json ExprFloat::toJSON(const SymbolTable & symbols) const { - str << nf; + return { + {"_type", "ExprFloat"}, + {"value", nf} + }; } -void ExprString::show(const SymbolTable & symbols, std::ostream & str) const +json ExprString::toJSON(const SymbolTable & symbols) const { - escapeString(str, s); + return { + {"_type", "ExprString"}, + {"value", s} + }; } -void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const +json ExprPath::toJSON(const SymbolTable & symbols) const { - str << s; + return { + {"_type", "ExprPath"}, + {"value", s} + }; } -void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const +json ExprVar::toJSON(const SymbolTable & symbols) const { - str << symbols[name]; + return { + {"_type", "ExprVar"}, + {"value", symbols[name]} + }; } -void ExprInheritFrom::show(SymbolTable const & symbols, std::ostream & str) const +json ExprInheritFrom::toJSON(SymbolTable const & symbols) const { - str << "(/* expanded inherit (expr) */ "; - fromExpr->show(symbols, str); - str << ")"; + return { + {"_type", "ExprInheritFrom"} + }; } -void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const +json ExprSelect::toJSON(const SymbolTable & symbols) const { - str << "("; - e->show(symbols, str); - str << ")." << showAttrPath(symbols, attrPath); - if (def) { - str << " or ("; - def->show(symbols, str); - str << ")"; - } + json out = { + {"_type", "ExprSelect"}, + {"e", e->toJSON(symbols)}, + {"attrs", printAttrPathToJson(symbols, attrPath)} + }; + if (def) + out["default"] = def->toJSON(symbols); + return out; } -void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const +json ExprOpHasAttr::toJSON(const SymbolTable & symbols) const { - str << "(("; - e->show(symbols, str); - str << ") ? " << showAttrPath(symbols, attrPath) << ")"; + return { + {"_type", "ExprOpHasAttr"}, + {"e", e->toJSON(symbols)}, + {"attrs", printAttrPathToJson(symbols, attrPath)} + }; } -void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const +void ExprAttrs::addBindingsToJSON(json & out, const SymbolTable & symbols) const { typedef const decltype(attrs)::value_type * Attr; std::vector sorted; @@ -94,14 +113,14 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co std::string_view sa = symbols[a->first], sb = symbols[b->first]; return sa < sb; }); - std::vector inherits; std::map> inheritsFrom; for (auto & i : sorted) { switch (i->second.kind) { case AttrDef::Kind::Plain: + out["attrs"][symbols[i->first]] = i->second.e->toJSON(symbols); break; case AttrDef::Kind::Inherited: - inherits.push_back(i->first); + out["inherit"][symbols[i->first]] = i->second.e->toJSON(symbols); break; case AttrDef::Kind::InheritedFrom: { auto & select = dynamic_cast(*i->second.e); @@ -111,167 +130,142 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co } } } - if (!inherits.empty()) { - str << "inherit"; - for (auto sym : inherits) str << " " << symbols[sym]; - str << "; "; - } + for (const auto & [from, syms] : inheritsFrom) { - str << "inherit ("; - (*inheritFromExprs)[from]->show(symbols, str); - str << ")"; - for (auto sym : syms) str << " " << symbols[sym]; - str << "; "; - } - for (auto & i : sorted) { - if (i->second.kind == AttrDef::Kind::Plain) { - str << symbols[i->first] << " = "; - i->second.e->show(symbols, str); - str << "; "; - } + json attrs = json::array(); + for (auto sym : syms) + attrs.push_back(symbols[sym]); + out["inheritFrom"].push_back({ + {"from", (*inheritFromExprs)[from]->toJSON(symbols)}, + {"attrs", attrs} + }); } + for (auto & i : dynamicAttrs) { - str << "\"${"; - i.nameExpr->show(symbols, str); - str << "}\" = "; - i.valueExpr->show(symbols, str); - str << "; "; + out["dynamicAttrs"].push_back({ + {"name", i.nameExpr->toJSON(symbols) }, + {"value", i.valueExpr->toJSON(symbols)} + }); } } -void ExprSet::show(const SymbolTable & symbols, std::ostream & str) const +json ExprSet::toJSON(const SymbolTable & symbols) const { - if (recursive) str << "rec "; - str << "{ "; - showBindings(symbols, str); - str << "}"; + json out = { + {"_type", "ExprSet"}, + {"recursive", recursive}, + }; + addBindingsToJSON(out, symbols); + return out; } -void ExprList::show(const SymbolTable & symbols, std::ostream & str) const +json ExprList::toJSON(const SymbolTable & symbols) const { - str << "[ "; - for (auto & i : elems) { - str << "("; - i->show(symbols, str); - str << ") "; - } - str << "]"; + json list = json::array(); + for (auto & i : elems) + list.push_back(i->toJSON(symbols)); + return { + { "_type", "ExprList" }, + { "elems", list }, + }; } -void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const +json ExprLambda::toJSON(const SymbolTable & symbols) const { - str << "("; + json out = { + { "_type", "ExprLambda" }, + { "body", body->toJSON(symbols) } + }; if (hasFormals()) { - str << "{ "; - bool first = true; // the natural Symbol ordering is by creation time, which can lead to the // same expression being printed in two different ways depending on its // context. always use lexicographic ordering to avoid this. for (const Formal & i : formals->lexicographicOrder(symbols)) { - if (first) first = false; else str << ", "; - str << symbols[i.name]; - if (i.def) { - str << " ? "; - i.def->show(symbols, str); - } + if (i.def) + out["formals"][symbols[i.name]] = i.def->toJSON(symbols); + else + out["formals"][symbols[i.name]] = nullptr; } - if (formals->ellipsis) { - if (!first) str << ", "; - str << "..."; - } - str << " }"; - if (arg) str << " @ "; + out["formalsEllipsis"] = formals->ellipsis; } - if (arg) str << symbols[arg]; - str << ": "; - body->show(symbols, str); - str << ")"; + if (arg) + out["arg"] = symbols[arg]; + return out; } -void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const +json ExprCall::toJSON(const SymbolTable & symbols) const { - str << '('; - fun->show(symbols, str); - for (auto & e : args) { - str << ' '; - e->show(symbols, str); - } - str << ')'; + json outArgs = json::array(); + for (auto & e : args) + outArgs.push_back(e->toJSON(symbols)); + return { + {"_type", "ExprCall"}, + {"fun", fun->toJSON(symbols)}, + {"args", outArgs} + }; } -void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const +json ExprLet::toJSON(const SymbolTable & symbols) const { - str << "(let "; - showBindings(symbols, str); - str << "in "; - body->show(symbols, str); - str << ")"; + json out = { + { "_type", "ExprLet" }, + { "body", body->toJSON(symbols) } + }; + addBindingsToJSON(out, symbols); + return out; } -void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const +json ExprWith::toJSON(const SymbolTable & symbols) const { - str << "(with "; - attrs->show(symbols, str); - str << "; "; - body->show(symbols, str); - str << ")"; + return { + {"_type", "ExprWith"}, + {"attrs", attrs->toJSON(symbols)}, + {"body", body->toJSON(symbols)} + }; } -void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const +json ExprIf::toJSON(const SymbolTable & symbols) const { - str << "(if "; - cond->show(symbols, str); - str << " then "; - then->show(symbols, str); - str << " else "; - else_->show(symbols, str); - str << ")"; + return { + {"_type", "ExprIf"}, + {"cond", cond->toJSON(symbols)}, + {"then", then->toJSON(symbols)}, + {"else", else_->toJSON(symbols)} + }; } -void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const +json ExprAssert::toJSON(const SymbolTable & symbols) const { - str << "assert "; - cond->show(symbols, str); - str << "; "; - body->show(symbols, str); + return { + {"_type", "ExprAssert"}, + {"cond", cond->toJSON(symbols)}, + {"body", body->toJSON(symbols)} + }; } -void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const +json ExprOpNot::toJSON(const SymbolTable & symbols) const { - str << "(! "; - e->show(symbols, str); - str << ")"; + return { + {"_type", "ExprOpNot"}, + {"e", e->toJSON(symbols)} + }; } -void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const +json ExprConcatStrings::toJSON(const SymbolTable & symbols) const { - bool first = true; - str << "("; - for (auto & [_pos, part] : es) { - if (first) - first = false; - else - str << " + "; - - if (forceString && !dynamic_cast(part.get())) { - /* Print as a string with an interpolation, to preserve the - * semantics of the value having to be a string. - * Interpolations are weird and someone should eventually - * move them out into their own AST node please. - */ - str << "\"${"; - part->show(symbols, str); - str << "}\""; - } else { - part->show(symbols, str); - } - } - str << ")"; + json parts = json::array(); + for (auto & [_pos, part] : es) + parts.push_back(part->toJSON(symbols)); + return { + {"_type", "ExprConcatStrings"}, + {"forceString", forceString}, + {"es", parts} + }; } -void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const +json ExprPos::toJSON(const SymbolTable & symbols) const { - str << "__curPos"; + return {{ "_type", "ExprPos" }}; } @@ -283,15 +277,24 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) if (!first) out << '.'; else first = false; if (i.symbol) out << symbols[i.symbol]; - else { - out << "\"${"; - i.expr->show(symbols, out); - out << "}\""; - } + else + out << "\"${...}\""; } return out.str(); } +json printAttrPathToJson(const SymbolTable & symbols, const AttrPath & attrPath) +{ + json out = json::array(); + for (auto & i : attrPath) { + if (i.symbol) + out.push_back(symbols[i.symbol]); + else + out.push_back(i.expr->toJSON(symbols)); + } + return out; +} + /* Computing levels/displacements for variables. */ diff --git a/lix/libexpr/nixexpr.hh b/lix/libexpr/nixexpr.hh index 6321a0803..c62984e3e 100644 --- a/lix/libexpr/nixexpr.hh +++ b/lix/libexpr/nixexpr.hh @@ -13,8 +13,9 @@ #include "lix/libexpr/pos-table.hh" #include "lix/libutil/strings.hh" -namespace nix { +#include +namespace nix { struct Env; struct Value; @@ -38,6 +39,7 @@ struct AttrName typedef std::vector AttrPath; std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath); +nlohmann::json printAttrPathToJson(const SymbolTable & symbols, const AttrPath & attrPath); /* Abstract syntax of Nix expressions. */ @@ -59,7 +61,7 @@ public: Expr & operator=(const Expr &) = delete; virtual ~Expr() { }; - virtual void show(const SymbolTable & symbols, std::ostream & str) const; + virtual nlohmann::json toJSON(const SymbolTable & symbols) const; virtual void bindVars(Evaluator & es, const std::shared_ptr & env); virtual void eval(EvalState & state, Env & env, Value & v); virtual Value * maybeThunk(EvalState & state, Env & env); @@ -68,7 +70,7 @@ public: }; #define COMMON_METHODS \ - void show(const SymbolTable & symbols, std::ostream & str) const override; \ + nlohmann::json toJSON(const SymbolTable & symbols) const override; \ void eval(EvalState & state, Env & env, Value & v) override; \ void bindVars(Evaluator & es, const std::shared_ptr & env) override; @@ -163,7 +165,7 @@ struct ExprInheritFrom : ExprVar this->fromWith = nullptr; } - void show(SymbolTable const & symbols, std::ostream & str) const override; + nlohmann::json toJSON(SymbolTable const & symbols) const override; void bindVars(Evaluator & es, const std::shared_ptr & env) override; }; @@ -254,7 +256,7 @@ struct ExprAttrs std::shared_ptr bindInheritSources( Evaluator & es, const std::shared_ptr & env); Env * buildInheritFromEnv(EvalState & state, Env & up); - void showBindings(const SymbolTable & symbols, std::ostream & str) const; + void addBindingsToJSON(nlohmann::json & out, const SymbolTable & symbols) const; }; struct ExprSet : Expr, ExprAttrs { @@ -431,9 +433,13 @@ struct ExprOpNot : Expr std::unique_ptr e1, e2; \ name(std::unique_ptr e1, std::unique_ptr e2) : e1(std::move(e1)), e2(std::move(e2)) { }; \ name(const PosIdx & pos, std::unique_ptr e1, std::unique_ptr e2) : pos(pos), e1(std::move(e1)), e2(std::move(e2)) { }; \ - void show(const SymbolTable & symbols, std::ostream & str) const override \ + nlohmann::json toJSON(const SymbolTable & symbols) const override \ { \ - str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \ + return { \ + {"_type", #name}, \ + {"e1", e1->toJSON(symbols)}, \ + {"e2", e2->toJSON(symbols)} \ + };\ } \ void bindVars(Evaluator & es, const std::shared_ptr & env) override \ { \ @@ -473,7 +479,6 @@ struct ExprPos : Expr /* only used to mark thunks as black holes. */ struct ExprBlackHole : Expr { - void show(const SymbolTable & symbols, std::ostream & str) const override {} void eval(EvalState & state, Env & env, Value & v) override; void bindVars(Evaluator & es, const std::shared_ptr & env) override {} }; diff --git a/package.nix b/package.nix index c3c08a63b..2a5a7143c 100644 --- a/package.nix +++ b/package.nix @@ -53,6 +53,7 @@ util-linuxMinimal ? utillinuxMinimal, utillinuxMinimal ? null, xz, + yq, busybox-sandbox-shell, @@ -270,6 +271,7 @@ stdenv.mkDerivation (finalAttrs: { git mercurial jq + yq lsof ] ++ lib.optional hostPlatform.isLinux util-linuxMinimal diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index cec4f9352..58d5a5910 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -69,6 +69,7 @@ for i in lang/parse-okay-*.nix; do 2> "lang/$i.err" then sed "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + yq --in-place --yaml-output '.' "lang/$i.out" diffAndAccept "$i" out exp diffAndAccept "$i" err err.exp else diff --git a/tests/functional/lang/parse-okay-1.exp b/tests/functional/lang/parse-okay-1.exp index d5ab5f18a..8650b88bc 100644 --- a/tests/functional/lang/parse-okay-1.exp +++ b/tests/functional/lang/parse-okay-1.exp @@ -1 +1,19 @@ -({ x, y, z }: ((x + y) + z)) +_type: ExprLambda +body: + _type: ExprConcatStrings + es: + - _type: ExprConcatStrings + es: + - _type: ExprVar + value: x + - _type: ExprVar + value: y + forceString: false + - _type: ExprVar + value: z + forceString: false +formals: + x: null + y: null + z: null +formalsEllipsis: false diff --git a/tests/functional/lang/parse-okay-arithmetic.exp b/tests/functional/lang/parse-okay-arithmetic.exp index 5e0528669..173c46962 100644 --- a/tests/functional/lang/parse-okay-arithmetic.exp +++ b/tests/functional/lang/parse-okay-arithmetic.exp @@ -1 +1,35 @@ -(__sub (7 + (__div (__mul (__sub 0 5) 12) 3)) 1) +_type: ExprCall +args: + - _type: ExprConcatStrings + es: + - _type: ExprInt + value: 7 + - _type: ExprCall + args: + - _type: ExprCall + args: + - _type: ExprCall + args: + - _type: ExprInt + value: 0 + - _type: ExprInt + value: 5 + fun: + _type: ExprVar + value: __sub + - _type: ExprInt + value: 12 + fun: + _type: ExprVar + value: __mul + - _type: ExprInt + value: 3 + fun: + _type: ExprVar + value: __div + forceString: false + - _type: ExprInt + value: 1 +fun: + _type: ExprVar + value: __sub diff --git a/tests/functional/lang/parse-okay-crlf.exp b/tests/functional/lang/parse-okay-crlf.exp index 4213609fc..db3b8b006 100644 --- a/tests/functional/lang/parse-okay-crlf.exp +++ b/tests/functional/lang/parse-okay-crlf.exp @@ -1 +1,15 @@ -rec { foo = "multi\nline\n string\n test\r"; x = y; y = 123; z = 456; } +_type: ExprSet +attrs: + foo: + _type: ExprString + value: "multi\nline\n string\n test\r" + x: + _type: ExprVar + value: y + y: + _type: ExprInt + value: 123 + z: + _type: ExprInt + value: 456 +recursive: true diff --git a/tests/functional/lang/parse-okay-dup-attrs-5.exp b/tests/functional/lang/parse-okay-dup-attrs-5.exp index 88b0b036f..2946b762c 100644 --- a/tests/functional/lang/parse-okay-dup-attrs-5.exp +++ b/tests/functional/lang/parse-okay-dup-attrs-5.exp @@ -1 +1,17 @@ -{ services = { ssh = { enable = true; port = 23; }; }; } +_type: ExprSet +attrs: + services: + _type: ExprSet + attrs: + ssh: + _type: ExprSet + attrs: + enable: + _type: ExprVar + value: 'true' + port: + _type: ExprInt + value: 23 + recursive: false + recursive: false +recursive: false diff --git a/tests/functional/lang/parse-okay-dup-attrs-6.exp b/tests/functional/lang/parse-okay-dup-attrs-6.exp index 88b0b036f..2946b762c 100644 --- a/tests/functional/lang/parse-okay-dup-attrs-6.exp +++ b/tests/functional/lang/parse-okay-dup-attrs-6.exp @@ -1 +1,17 @@ -{ services = { ssh = { enable = true; port = 23; }; }; } +_type: ExprSet +attrs: + services: + _type: ExprSet + attrs: + ssh: + _type: ExprSet + attrs: + enable: + _type: ExprVar + value: 'true' + port: + _type: ExprInt + value: 23 + recursive: false + recursive: false +recursive: false diff --git a/tests/functional/lang/parse-okay-ind-string.exp b/tests/functional/lang/parse-okay-ind-string.exp index 570785ee2..ab8a7c181 100644 --- a/tests/functional/lang/parse-okay-ind-string.exp +++ b/tests/functional/lang/parse-okay-ind-string.exp @@ -1 +1,375 @@ -(let s1 = "This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\n"; s10 = ""; s11 = ""; s12 = ""; s13 = ("start on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n " + "${(if true then "ln -sf 123 /var/run/opengl-driver" else (if true then "ln -sf 456 /var/run/opengl-driver" else ""))}" + "\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=" + "abc" + "\nenv SLIM_THEMESDIR=" + "def" + "\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=" + "foo" + "/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=" + "libX11" + "/lib:" + "libXext" + "/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\n" + "${(if true then ("env XORG_DRI_DRIVER_PATH=" + "nvidiaDrivers" + "/X11R6/lib/modules/drivers/") else (if true then ("env XORG_DRI_DRIVER_PATH=" + "mesa" + "/lib/modules/dri") else ""))}" + " \n\nexec " + "slim" + "/bin/slim\n"); s14 = "Escaping of ' followed by ': ''\nEscaping of $ followed by {: \${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\n"; s15 = (let x = "bla"; in ("foo\n'" + "${x}" + "'\nbar\n")); s16 = "cut -d $'\\t' -f 1\n"; s17 = (("ending dollar $" + "$") + "\n"); s18 = " Lines without any indentation effectively disable the indentation\n stripping for the entire string:\n\n cat >$out/foo/data <$out/foo/data < 1 == 1 < 2 -> 2 >= 1 != 1 <= 2 && true || false then 1 else err) + # ExprOpUpdate + (foo // {} // bar.baz) + # ExprOpConcatLists + (foo ++ [] ++ optional cond value) + # ExprConcatStrings + (1 + 2 + 3) + "${""}" + "Foo ${3 + "2"}" + # ExprPos + __curPos +] diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp index 89c66f760..2fbcc0d03 100644 --- a/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp @@ -1 +1,16 @@ -{ x = { q = 3; y = 3; z = 3; }; } +_type: ExprSet +attrs: + x: + _type: ExprSet + attrs: + q: + _type: ExprInt + value: 3 + y: + _type: ExprInt + value: 3 + z: + _type: ExprInt + value: 3 + recursive: false +recursive: false diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp index 89c66f760..2fbcc0d03 100644 --- a/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp @@ -1 +1,16 @@ -{ x = { q = 3; y = 3; z = 3; }; } +_type: ExprSet +attrs: + x: + _type: ExprSet + attrs: + q: + _type: ExprInt + value: 3 + y: + _type: ExprInt + value: 3 + z: + _type: ExprInt + value: 3 + recursive: false +recursive: false diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp index b89a59734..3c12ec32e 100644 --- a/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp @@ -1 +1,24 @@ -{ services = { httpd = { enable = true; }; ssh = { enable = true; port = 123; }; }; } +_type: ExprSet +attrs: + services: + _type: ExprSet + attrs: + httpd: + _type: ExprSet + attrs: + enable: + _type: ExprVar + value: 'true' + recursive: false + ssh: + _type: ExprSet + attrs: + enable: + _type: ExprVar + value: 'true' + port: + _type: ExprInt + value: 123 + recursive: false + recursive: false +recursive: false diff --git a/tests/functional/lang/parse-okay-rec-set-override-warning.exp b/tests/functional/lang/parse-okay-rec-set-override-warning.exp index ea17c88f5..cccffd9eb 100644 --- a/tests/functional/lang/parse-okay-rec-set-override-warning.exp +++ b/tests/functional/lang/parse-okay-rec-set-override-warning.exp @@ -1 +1,38 @@ -[ ({ a = rec { __overrides = { }; }; }) (rec { __overrides = { }; }) ({ __overrides = { }; }) (rec { "${("__overrides" + "")}" = { }; }) ] +_type: ExprList +elems: + - _type: ExprSet + attrs: + a: + _type: ExprSet + attrs: + __overrides: + _type: ExprSet + recursive: false + recursive: true + recursive: false + - _type: ExprSet + attrs: + __overrides: + _type: ExprSet + recursive: false + recursive: true + - _type: ExprSet + attrs: + __overrides: + _type: ExprSet + recursive: false + recursive: false + - _type: ExprSet + dynamicAttrs: + - name: + _type: ExprConcatStrings + es: + - _type: ExprString + value: __overrides + - _type: ExprString + value: '' + forceString: false + value: + _type: ExprSet + recursive: false + recursive: true diff --git a/tests/functional/lang/parse-okay-regression-20041027.exp b/tests/functional/lang/parse-okay-regression-20041027.exp index 9df7219e4..bcf1f7156 100644 --- a/tests/functional/lang/parse-okay-regression-20041027.exp +++ b/tests/functional/lang/parse-okay-regression-20041027.exp @@ -1 +1,36 @@ -({ fetchurl, stdenv }: ((stdenv).mkDerivation { name = "libXi-6.0.1"; src = (fetchurl { md5 = "7e935a42428d63a387b3c048be0f2756"; url = "http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2"; }); })) +_type: ExprLambda +body: + _type: ExprCall + args: + - _type: ExprSet + attrs: + name: + _type: ExprString + value: libXi-6.0.1 + src: + _type: ExprCall + args: + - _type: ExprSet + attrs: + md5: + _type: ExprString + value: 7e935a42428d63a387b3c048be0f2756 + url: + _type: ExprString + value: http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2 + recursive: false + fun: + _type: ExprVar + value: fetchurl + recursive: false + fun: + _type: ExprSelect + attrs: + - mkDerivation + e: + _type: ExprVar + value: stdenv +formals: + fetchurl: null + stdenv: null +formalsEllipsis: false diff --git a/tests/functional/lang/parse-okay-regression-751.exp b/tests/functional/lang/parse-okay-regression-751.exp index 0cbf55d49..24a688dcf 100644 --- a/tests/functional/lang/parse-okay-regression-751.exp +++ b/tests/functional/lang/parse-okay-regression-751.exp @@ -1 +1,23 @@ -(let const = (a: "const"); in ("${(const { x = "q"; })}")) +_type: ExprLet +attrs: + const: + _type: ExprLambda + arg: a + body: + _type: ExprString + value: const +body: + _type: ExprConcatStrings + es: + - _type: ExprCall + args: + - _type: ExprSet + attrs: + x: + _type: ExprString + value: q + recursive: false + fun: + _type: ExprVar + value: const + forceString: true diff --git a/tests/functional/lang/parse-okay-subversion.exp b/tests/functional/lang/parse-okay-subversion.exp index 32fbba3c5..8a0aa05cf 100644 --- a/tests/functional/lang/parse-okay-subversion.exp +++ b/tests/functional/lang/parse-okay-subversion.exp @@ -1 +1,346 @@ -({ db4 ? null, expat, fetchurl, httpServer ? false, httpd ? null, j2sdk ? null, javaSwigBindings ? false, javahlBindings ? false, localServer ? false, openssl ? null, pythonBindings ? false, sslSupport ? false, stdenv, swig ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { inherit expat httpServer javaSwigBindings javahlBindings localServer pythonBindings sslSupport; builder = /foo/bar; db4 = (if localServer then db4 else null); httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); swig = (if (pythonBindings || javaSwigBindings) then swig else null); })) +_type: ExprLambda +body: + _type: ExprAssert + body: + _type: ExprAssert + body: + _type: ExprAssert + body: + _type: ExprAssert + body: + _type: ExprAssert + body: + _type: ExprAssert + body: + _type: ExprAssert + body: + _type: ExprCall + args: + - _type: ExprSet + attrs: + builder: + _type: ExprPath + value: /foo/bar + db4: + _type: ExprIf + cond: + _type: ExprVar + value: localServer + else: + _type: ExprVar + value: 'null' + then: + _type: ExprVar + value: db4 + httpd: + _type: ExprIf + cond: + _type: ExprVar + value: httpServer + else: + _type: ExprVar + value: 'null' + then: + _type: ExprVar + value: httpd + j2sdk: + _type: ExprIf + cond: + _type: ExprVar + value: javaSwigBindings + else: + _type: ExprIf + cond: + _type: ExprVar + value: javahlBindings + else: + _type: ExprVar + value: 'null' + then: + _type: ExprVar + value: j2sdk + then: + _type: ExprSelect + attrs: + - j2sdk + e: + _type: ExprVar + value: swig + name: + _type: ExprString + value: subversion-1.1.1 + openssl: + _type: ExprIf + cond: + _type: ExprVar + value: sslSupport + else: + _type: ExprVar + value: 'null' + then: + _type: ExprVar + value: openssl + patches: + _type: ExprIf + cond: + _type: ExprVar + value: javahlBindings + else: + _type: ExprList + elems: [] + then: + _type: ExprList + elems: + - _type: ExprPath + value: /javahl.patch + python: + _type: ExprIf + cond: + _type: ExprVar + value: pythonBindings + else: + _type: ExprVar + value: 'null' + then: + _type: ExprSelect + attrs: + - python + e: + _type: ExprVar + value: swig + src: + _type: ExprCall + args: + - _type: ExprSet + attrs: + md5: + _type: ExprString + value: a180c3fe91680389c210c99def54d9e0 + url: + _type: ExprString + value: http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2 + recursive: false + fun: + _type: ExprVar + value: fetchurl + swig: + _type: ExprIf + cond: + _type: ExprOpOr + e1: + _type: ExprVar + value: pythonBindings + e2: + _type: ExprVar + value: javaSwigBindings + else: + _type: ExprVar + value: 'null' + then: + _type: ExprVar + value: swig + inherit: + expat: + _type: ExprVar + value: expat + httpServer: + _type: ExprVar + value: httpServer + javaSwigBindings: + _type: ExprVar + value: javaSwigBindings + javahlBindings: + _type: ExprVar + value: javahlBindings + localServer: + _type: ExprVar + value: localServer + pythonBindings: + _type: ExprVar + value: pythonBindings + sslSupport: + _type: ExprVar + value: sslSupport + recursive: false + fun: + _type: ExprSelect + attrs: + - mkDerivation + e: + _type: ExprVar + value: stdenv + cond: + _type: ExprOpImpl + e1: + _type: ExprVar + value: javahlBindings + e2: + _type: ExprOpNEq + e1: + _type: ExprVar + value: j2sdk + e2: + _type: ExprVar + value: 'null' + cond: + _type: ExprOpImpl + e1: + _type: ExprVar + value: javaSwigBindings + e2: + _type: ExprOpAnd + e1: + _type: ExprOpNEq + e1: + _type: ExprVar + value: swig + e2: + _type: ExprVar + value: 'null' + e2: + _type: ExprSelect + attrs: + - javaSupport + e: + _type: ExprVar + value: swig + cond: + _type: ExprOpImpl + e1: + _type: ExprVar + value: pythonBindings + e2: + _type: ExprOpAnd + e1: + _type: ExprOpNEq + e1: + _type: ExprVar + value: swig + e2: + _type: ExprVar + value: 'null' + e2: + _type: ExprSelect + attrs: + - pythonSupport + e: + _type: ExprVar + value: swig + cond: + _type: ExprOpImpl + e1: + _type: ExprVar + value: sslSupport + e2: + _type: ExprOpAnd + e1: + _type: ExprOpNEq + e1: + _type: ExprVar + value: openssl + e2: + _type: ExprVar + value: 'null' + e2: + _type: ExprOpImpl + e1: + _type: ExprVar + value: httpServer + e2: + _type: ExprOpEq + e1: + _type: ExprSelect + attrs: + - openssl + e: + _type: ExprVar + value: httpd + e2: + _type: ExprVar + value: openssl + cond: + _type: ExprOpImpl + e1: + _type: ExprVar + value: httpServer + e2: + _type: ExprOpAnd + e1: + _type: ExprOpNEq + e1: + _type: ExprVar + value: httpd + e2: + _type: ExprVar + value: 'null' + e2: + _type: ExprOpEq + e1: + _type: ExprSelect + attrs: + - expat + e: + _type: ExprVar + value: httpd + e2: + _type: ExprVar + value: expat + cond: + _type: ExprOpImpl + e1: + _type: ExprVar + value: localServer + e2: + _type: ExprOpNEq + e1: + _type: ExprVar + value: db4 + e2: + _type: ExprVar + value: 'null' + cond: + _type: ExprOpNEq + e1: + _type: ExprVar + value: expat + e2: + _type: ExprVar + value: 'null' +formals: + db4: + _type: ExprVar + value: 'null' + expat: null + fetchurl: null + httpServer: + _type: ExprVar + value: 'false' + httpd: + _type: ExprVar + value: 'null' + j2sdk: + _type: ExprVar + value: 'null' + javaSwigBindings: + _type: ExprVar + value: 'false' + javahlBindings: + _type: ExprVar + value: 'false' + localServer: + _type: ExprVar + value: 'false' + openssl: + _type: ExprVar + value: 'null' + pythonBindings: + _type: ExprVar + value: 'false' + sslSupport: + _type: ExprVar + value: 'false' + stdenv: null + swig: + _type: ExprVar + value: 'null' +formalsEllipsis: false diff --git a/tests/functional/lang/parse-okay-url.exp b/tests/functional/lang/parse-okay-url.exp index e5f0829b0..f08e9cd33 100644 --- a/tests/functional/lang/parse-okay-url.exp +++ b/tests/functional/lang/parse-okay-url.exp @@ -1 +1,16 @@ -[ ("x:x") ("https://svn.cs.uu.nl:12443/repos/trace/trunk") ("http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2") ("http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz") ("http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz") ("https://ftp5.gwdg.de/pub/linux/archlinux/extra/os/x86_64/unzip-6.0-14-x86_64.pkg.tar.zst") ("ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz") ] +_type: ExprList +elems: + - _type: ExprString + value: x:x + - _type: ExprString + value: https://svn.cs.uu.nl:12443/repos/trace/trunk + - _type: ExprString + value: http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2 + - _type: ExprString + value: http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz + - _type: ExprString + value: http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz + - _type: ExprString + value: https://ftp5.gwdg.de/pub/linux/archlinux/extra/os/x86_64/unzip-6.0-14-x86_64.pkg.tar.zst + - _type: ExprString + value: ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 8ab196e7f..bea2d48b2 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -201,7 +201,6 @@ libexpr_tests_sources = files( 'libexpr/primops.cc', 'libexpr/search-path.cc', 'libexpr/trivial.cc', - 'libexpr/expr-print.cc', 'libexpr/value/context.cc', 'libexpr/value/print.cc', )