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.
This commit is contained in:
Shea Levy 2014-11-30 13:16:19 -05:00
parent 5f04da905f
commit 320659b0cd
5 changed files with 108 additions and 0 deletions

View file

@ -104,6 +104,9 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
case tPrimOpApp: case tPrimOpApp:
str << "<PRIMOP-APP>"; str << "<PRIMOP-APP>";
break; break;
case tExternal:
str << *v.external;
break;
default: default:
throw Error("invalid value"); throw Error("invalid value");
} }
@ -136,6 +139,7 @@ string showType(const Value & v)
case tBlackhole: return "a black hole"; case tBlackhole: return "a black hole";
case tPrimOp: return "a built-in function"; case tPrimOp: return "a built-in function";
case tPrimOpApp: return "a partially applied built-in function"; case tPrimOpApp: return "a partially applied built-in function";
case tExternal: return v.external->showType();
} }
abort(); abort();
} }
@ -1314,6 +1318,9 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
return coerceToString(pos, *i->value, context, coerceMore, copyToStore); return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
} }
if (v.type == tExternal)
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
if (coerceMore) { if (coerceMore) {
/* Note that `false' is represented as an empty string for /* Note that `false' is represented as an empty string for
@ -1434,6 +1441,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tPrimOpApp: case tPrimOpApp:
return false; return false;
case tExternal:
return *v1.external == *v2.external;
default: default:
throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2)); 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.left);
sz += doValue(*v.primOpApp.right); sz += doValue(*v.primOpApp.right);
break; break;
case tExternal:
if (seen.find(v.external) != seen.end()) break;
seen.insert(v.external);
sz += v.external->valueSize(seen);
break;
default: 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);
}
} }

View file

@ -187,6 +187,9 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tPrimOpApp: case tPrimOpApp:
t = "lambda"; t = "lambda";
break; break;
case tExternal:
t = args[0]->external->typeOf();
break;
default: abort(); default: abort();
} }
mkString(v, state.symbols.create(t)); mkString(v, state.symbols.create(t));

View file

@ -80,10 +80,21 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
} }
case tExternal:
v.external->printValueAsJSON(state, strict, str, context);
break;
default: default:
throw TypeError(format("cannot convert %1% to JSON") % showType(v)); 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());
}
} }

View file

@ -144,12 +144,23 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
} }
case tExternal:
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break;
default: default:
doc.writeEmptyElement("unevaluated"); 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, void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context) Value & v, std::ostream & out, PathSet & context)
{ {

View file

@ -19,6 +19,7 @@ typedef enum {
tBlackhole, tBlackhole,
tPrimOp, tPrimOp,
tPrimOpApp, tPrimOpApp,
tExternal,
} ValueType; } ValueType;
@ -29,10 +30,58 @@ struct ExprLambda;
struct PrimOp; struct PrimOp;
struct PrimOp; struct PrimOp;
class Symbol; class Symbol;
struct Pos;
class EvalState;
class XMLWriter;
typedef long NixInt; 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<const void *> & 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 struct Value
{ {
@ -88,6 +137,7 @@ struct Value
struct { struct {
Value * left, * right; Value * left, * right;
} primOpApp; } primOpApp;
ExternalValueBase * external;
}; };
}; };