From 320659b0cd161249c95e736c3fb309b1a73ea728 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Sun, 30 Nov 2014 13:16:19 -0500 Subject: [PATCH 1/2] Allow external code using libnixexpr to add types Code that links to libnixexpr (e.g. plugins loaded with importNative, or nix-exec) may want to provide custom value types and operations on values of those types. For example, nix-exec is currently using sets where a custom IO value type would be more appropriate. This commit provides a generic hook for such types in the form of tExternal and the ExternalBase virtual class, which contains all functions necessary for libnixexpr's type-polymorphic functions (e.g. `showType`) to be implemented. --- src/libexpr/eval.cc | 33 ++++++++++++++++++++++++ src/libexpr/primops.cc | 3 +++ src/libexpr/value-to-json.cc | 11 ++++++++ src/libexpr/value-to-xml.cc | 11 ++++++++ src/libexpr/value.hh | 50 ++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ebb28021c..b0afccdfb 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -104,6 +104,9 @@ static void printValue(std::ostream & str, std::set & active, con case tPrimOpApp: str << ""; break; + case tExternal: + str << *v.external; + break; default: throw Error("invalid value"); } @@ -136,6 +139,7 @@ string showType(const Value & v) case tBlackhole: return "a black hole"; case tPrimOp: return "a built-in function"; case tPrimOpApp: return "a partially applied built-in function"; + case tExternal: return v.external->showType(); } abort(); } @@ -1314,6 +1318,9 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, return coerceToString(pos, *i->value, context, coerceMore, copyToStore); } + if (v.type == tExternal) + return v.external->coerceToString(pos, context, coerceMore, copyToStore); + if (coerceMore) { /* Note that `false' is represented as an empty string for @@ -1434,6 +1441,9 @@ bool EvalState::eqValues(Value & v1, Value & v2) case tPrimOpApp: return false; + case tExternal: + return *v1.external == *v2.external; + default: throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2)); } @@ -1575,6 +1585,11 @@ size_t valueSize(Value & v) sz += doValue(*v.primOpApp.left); sz += doValue(*v.primOpApp.right); break; + case tExternal: + if (seen.find(v.external) != seen.end()) break; + seen.insert(v.external); + sz += v.external->valueSize(seen); + break; default: ; } @@ -1601,4 +1616,22 @@ size_t valueSize(Value & v) } +string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) +{ + throw TypeError(format("cannot coerce %1% to a string, at %2%") % + showType() % pos); +} + + +bool ExternalValueBase::operator==(const ExternalValueBase & b) +{ + return false; +} + + +std::ostream & operator << (std::ostream & str, ExternalValueBase & v) { + return v.print(str); +} + + } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b0596dad9..e7b796046 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -187,6 +187,9 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu case tPrimOpApp: t = "lambda"; break; + case tExternal: + t = args[0]->external->typeOf(); + break; default: abort(); } mkString(v, state.symbols.create(t)); diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index d1ec9b566..b9f3e6551 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -80,10 +80,21 @@ void printValueAsJSON(EvalState & state, bool strict, break; } + case tExternal: + v.external->printValueAsJSON(state, strict, str, context); + break; + default: throw TypeError(format("cannot convert %1% to JSON") % showType(v)); } } +void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, + std::ostream & str, PathSet & context) +{ + throw TypeError(format("cannot convert %1% to JSON") % showType()); +} + + } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 3934a83ee..3cecc33dc 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -144,12 +144,23 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, break; } + case tExternal: + v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen); + break; + default: doc.writeEmptyElement("unevaluated"); } } +void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, + bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) +{ + doc.writeEmptyElement("unevaluated"); +} + + void printValueAsXML(EvalState & state, bool strict, bool location, Value & v, std::ostream & out, PathSet & context) { diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 5f18f629e..227a81f2b 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -19,6 +19,7 @@ typedef enum { tBlackhole, tPrimOp, tPrimOpApp, + tExternal, } ValueType; @@ -29,10 +30,58 @@ struct ExprLambda; struct PrimOp; struct PrimOp; class Symbol; +struct Pos; +class EvalState; +class XMLWriter; typedef long NixInt; +/* External values must descend from ExternalValueBase, so that + * type-agnostic nix functions (e.g. showType) can be implemented + */ +class ExternalValueBase +{ + friend std::ostream & operator << (std::ostream & str, ExternalValueBase & v); + protected: + /* Print out the value */ + virtual std::ostream & print(std::ostream & str) = 0; + + public: + /* Return a simple string describing the type */ + virtual string showType() = 0; + + /* Return a string to be used in builtins.typeOf */ + virtual string typeOf() = 0; + + /* How much space does this value take up */ + virtual size_t valueSize(std::set & seen) = 0; + + /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an + * error + */ + virtual string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore); + + /* Compare to another value of the same type. Defaults to uncomparable, + * i.e. always false. + */ + virtual bool operator==(const ExternalValueBase & b); + + /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ + virtual void printValueAsJSON(EvalState & state, bool strict, + std::ostream & str, PathSet & context); + + /* Print the value as XML. Defaults to unevaluated */ + virtual void printValueAsXML(EvalState & state, bool strict, bool location, + XMLWriter & doc, PathSet & context, PathSet & drvsSeen); + + virtual ~ExternalValueBase() + { + }; +}; + +std::ostream & operator << (std::ostream & str, ExternalValueBase & v); + struct Value { @@ -88,6 +137,7 @@ struct Value struct { Value * left, * right; } primOpApp; + ExternalValueBase * external; }; }; From 608110804cc753eee31418fda1b33cb77a83d0fc Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Tue, 2 Dec 2014 10:02:03 -0500 Subject: [PATCH 2/2] Make all ExternalValueBase functions const --- src/libexpr/eval.cc | 6 +++--- src/libexpr/value-to-json.cc | 2 +- src/libexpr/value-to-xml.cc | 2 +- src/libexpr/value.hh | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b0afccdfb..2ff975610 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1616,20 +1616,20 @@ size_t valueSize(Value & v) } -string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) +string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const { throw TypeError(format("cannot coerce %1% to a string, at %2%") % showType() % pos); } -bool ExternalValueBase::operator==(const ExternalValueBase & b) +bool ExternalValueBase::operator==(const ExternalValueBase & b) const { return false; } -std::ostream & operator << (std::ostream & str, ExternalValueBase & v) { +std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) { return v.print(str); } diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index b9f3e6551..cdb713418 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -91,7 +91,7 @@ void printValueAsJSON(EvalState & state, bool strict, void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, - std::ostream & str, PathSet & context) + std::ostream & str, PathSet & context) const { throw TypeError(format("cannot convert %1% to JSON") % showType()); } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 3cecc33dc..bbbb7039b 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -155,7 +155,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, - bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) + bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const { doc.writeEmptyElement("unevaluated"); } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 227a81f2b..c06b5a6d1 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -42,45 +42,45 @@ typedef long NixInt; */ class ExternalValueBase { - friend std::ostream & operator << (std::ostream & str, ExternalValueBase & v); + friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); protected: /* Print out the value */ - virtual std::ostream & print(std::ostream & str) = 0; + virtual std::ostream & print(std::ostream & str) const = 0; public: /* Return a simple string describing the type */ - virtual string showType() = 0; + virtual string showType() const = 0; /* Return a string to be used in builtins.typeOf */ - virtual string typeOf() = 0; + virtual string typeOf() const = 0; /* How much space does this value take up */ - virtual size_t valueSize(std::set & seen) = 0; + virtual size_t valueSize(std::set & seen) const = 0; /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * error */ - virtual string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore); + virtual string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const; /* Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. */ - virtual bool operator==(const ExternalValueBase & b); + virtual bool operator==(const ExternalValueBase & b) const; /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ virtual void printValueAsJSON(EvalState & state, bool strict, - std::ostream & str, PathSet & context); + std::ostream & str, PathSet & context) const; /* Print the value as XML. Defaults to unevaluated */ virtual void printValueAsXML(EvalState & state, bool strict, bool location, - XMLWriter & doc, PathSet & context, PathSet & drvsSeen); + XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const; virtual ~ExternalValueBase() { }; }; -std::ostream & operator << (std::ostream & str, ExternalValueBase & v); +std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); struct Value