Skip to content

Commit

Permalink
Merge pull request #8566 from inclyc/nixd/value-print-depth
Browse files Browse the repository at this point in the history
libexpr: extend `Value::print` to allow limited depth
  • Loading branch information
roberth committed Jul 1, 2023
2 parents d05d175 + 1400fde commit 7b39a38
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 10 deletions.
20 changes: 12 additions & 8 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,16 @@ RootValue allocRootValue(Value * v)
#endif
}

void Value::print(const SymbolTable & symbols, std::ostream & str,
std::set<const void *> * seen) const
void Value::print(const SymbolTable &symbols, std::ostream &str,
std::set<const void *> *seen, int depth) const

{
checkInterrupt();

if (depth <= 0) {
str << "«too deep»";
return;
}
switch (internalType) {
case tInt:
str << integer;
Expand All @@ -123,7 +128,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << "{ ";
for (auto & i : attrs->lexicographicOrder(symbols)) {
str << symbols[i->name] << " = ";
i->value->print(symbols, str, seen);
i->value->print(symbols, str, seen, depth - 1);
str << "; ";
}
str << "}";
Expand All @@ -139,7 +144,7 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
str << "[ ";
for (auto v2 : listItems()) {
if (v2)
v2->print(symbols, str, seen);
v2->print(symbols, str, seen, depth - 1);
else
str << "(nullptr)";
str << " ";
Expand Down Expand Up @@ -181,11 +186,10 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
}
}


void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
{
void Value::print(const SymbolTable &symbols, std::ostream &str,
bool showRepeated, int depth) const {
std::set<const void *> seen;
print(symbols, str, showRepeated ? nullptr : &seen);
print(symbols, str, showRepeated ? nullptr : &seen, depth);
}

// Pretty print types for assertion errors
Expand Down
236 changes: 236 additions & 0 deletions src/libexpr/tests/value/print.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#include "tests/libexpr.hh"

#include "value.hh"

namespace nix {

using namespace testing;

struct ValuePrintingTests : LibExprTest
{
template<class... A>
void test(Value v, std::string_view expected, A... args)
{
std::stringstream out;
v.print(state.symbols, out, args...);
ASSERT_EQ(out.str(), expected);
}
};

TEST_F(ValuePrintingTests, tInt)
{
Value vInt;
vInt.mkInt(10);
test(vInt, "10");
}

TEST_F(ValuePrintingTests, tBool)
{
Value vBool;
vBool.mkBool(true);
test(vBool, "true");
}

TEST_F(ValuePrintingTests, tString)
{
Value vString;
vString.mkString("some-string");
test(vString, "\"some-string\"");
}

TEST_F(ValuePrintingTests, tPath)
{
Value vPath;
vPath.mkString("/foo");
test(vPath, "\"/foo\"");
}

TEST_F(ValuePrintingTests, tNull)
{
Value vNull;
vNull.mkNull();
test(vNull, "null");
}

TEST_F(ValuePrintingTests, tAttrs)
{
Value vOne;
vOne.mkInt(1);

Value vTwo;
vTwo.mkInt(2);

BindingsBuilder builder(state, state.allocBindings(10));
builder.insert(state.symbols.create("one"), &vOne);
builder.insert(state.symbols.create("two"), &vTwo);

Value vAttrs;
vAttrs.mkAttrs(builder.finish());

test(vAttrs, "{ one = 1; two = 2; }");
}

TEST_F(ValuePrintingTests, tList)
{
Value vOne;
vOne.mkInt(1);

Value vTwo;
vTwo.mkInt(2);

Value vList;
state.mkList(vList, 5);
vList.bigList.elems[0] = &vOne;
vList.bigList.elems[1] = &vTwo;
vList.bigList.size = 3;

test(vList, "[ 1 2 (nullptr) ]");
}

TEST_F(ValuePrintingTests, vThunk)
{
Value vThunk;
vThunk.mkThunk(nullptr, nullptr);

test(vThunk, "<CODE>");
}

TEST_F(ValuePrintingTests, vApp)
{
Value vApp;
vApp.mkApp(nullptr, nullptr);

test(vApp, "<CODE>");
}

TEST_F(ValuePrintingTests, vLambda)
{
Value vLambda;
vLambda.mkLambda(nullptr, nullptr);

test(vLambda, "<LAMBDA>");
}

TEST_F(ValuePrintingTests, vPrimOp)
{
Value vPrimOp;
vPrimOp.mkPrimOp(nullptr);

test(vPrimOp, "<PRIMOP>");
}

TEST_F(ValuePrintingTests, vPrimOpApp)
{
Value vPrimOpApp;
vPrimOpApp.mkPrimOpApp(nullptr, nullptr);

test(vPrimOpApp, "<PRIMOP-APP>");
}

TEST_F(ValuePrintingTests, vExternal)
{
struct MyExternal : ExternalValueBase
{
public:
std::string showType() const override
{
return "";
}
std::string typeOf() const override
{
return "";
}
virtual std::ostream & print(std::ostream & str) const override
{
str << "testing-external!";
return str;
}
} myExternal;
Value vExternal;
vExternal.mkExternal(&myExternal);

test(vExternal, "testing-external!");
}

TEST_F(ValuePrintingTests, vFloat)
{
Value vFloat;
vFloat.mkFloat(2.0);

test(vFloat, "2");
}

TEST_F(ValuePrintingTests, vBlackhole)
{
Value vBlackhole;
vBlackhole.mkBlackhole();
test(vBlackhole, "«potential infinite recursion»");
}

TEST_F(ValuePrintingTests, depthAttrs)
{
Value vOne;
vOne.mkInt(1);

Value vTwo;
vTwo.mkInt(2);

BindingsBuilder builder(state, state.allocBindings(10));
builder.insert(state.symbols.create("one"), &vOne);
builder.insert(state.symbols.create("two"), &vTwo);

Value vAttrs;
vAttrs.mkAttrs(builder.finish());

BindingsBuilder builder2(state, state.allocBindings(10));
builder2.insert(state.symbols.create("one"), &vOne);
builder2.insert(state.symbols.create("two"), &vTwo);
builder2.insert(state.symbols.create("nested"), &vAttrs);

Value vNested;
vNested.mkAttrs(builder2.finish());

test(vNested, "{ nested = «too deep»; one = «too deep»; two = «too deep»; }", false, 1);
test(vNested, "{ nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; }", false, 2);
test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 3);
test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 4);
}

TEST_F(ValuePrintingTests, depthList)
{
Value vOne;
vOne.mkInt(1);

Value vTwo;
vTwo.mkInt(2);

BindingsBuilder builder(state, state.allocBindings(10));
builder.insert(state.symbols.create("one"), &vOne);
builder.insert(state.symbols.create("two"), &vTwo);

Value vAttrs;
vAttrs.mkAttrs(builder.finish());

BindingsBuilder builder2(state, state.allocBindings(10));
builder2.insert(state.symbols.create("one"), &vOne);
builder2.insert(state.symbols.create("two"), &vTwo);
builder2.insert(state.symbols.create("nested"), &vAttrs);

Value vNested;
vNested.mkAttrs(builder2.finish());

Value vList;
state.mkList(vList, 5);
vList.bigList.elems[0] = &vOne;
vList.bigList.elems[1] = &vTwo;
vList.bigList.elems[2] = &vNested;
vList.bigList.size = 3;

test(vList, "[ «too deep» «too deep» «too deep» ]", false, 1);
test(vList, "[ 1 2 { nested = «too deep»; one = «too deep»; two = «too deep»; } ]", false, 2);
test(vList, "[ 1 2 { nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; } ]", false, 3);
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 4);
test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 5);
}

} // namespace nix
5 changes: 3 additions & 2 deletions src/libexpr/value.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
///@file

#include <cassert>
#include <climits>

#include "symbol-table.hh"
#include "value/context.hh"
Expand Down Expand Up @@ -137,11 +138,11 @@ private:

friend std::string showType(const Value & v);

void print(const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen) const;
void print(const SymbolTable &symbols, std::ostream &str, std::set<const void *> *seen, int depth) const;

public:

void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const;
void print(const SymbolTable &symbols, std::ostream &str, bool showRepeated = false, int depth = INT_MAX) const;

// Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's
Expand Down

0 comments on commit 7b39a38

Please sign in to comment.