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:
		
							parent
							
								
									5f04da905f
								
							
						
					
					
						commit
						320659b0cd
					
				
					 5 changed files with 108 additions and 0 deletions
				
			
		|  | @ -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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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)); | ||||||
|  |  | ||||||
|  | @ -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()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue