#pragma once
#include <tuple>
#include <vector>
#include "libexpr/symbol-table.hh"
#include "libutil/types.hh"
namespace nix {
using ValueType = enum {
tInt = 1,
tBool,
tString,
tPath,
tNull,
tAttrs,
tList,
tThunk,
tApp,
tLambda,
tBlackhole,
tPrimOp,
tPrimOpApp,
_reserved1, // formerly tExternal
tFloat
};
class Bindings;
struct Env;
struct Expr;
struct ExprLambda;
struct PrimOp;
struct PrimOp;
class Symbol;
typedef int64_t NixInt;
typedef double NixFloat;
// Forward declaration of Value is required because the following
// types are mutually recursive.
//
// TODO(tazjin): Really, these types need some serious refactoring.
struct Value;
/* Strings in the evaluator carry a so-called `context' which
is a list of strings representing store paths. This is to
allow users to write things like
"--with-freetype2-library=" + freetype + "/lib"
where `freetype' is a derivation (or a source to be copied
to the store). If we just concatenated the strings without
keeping track of the referenced store paths, then if the
string is used as a derivation attribute, the derivation
will not have the correct dependencies in its inputDrvs and
inputSrcs.
The semantics of the context is as follows: when a string
with context C is used as a derivation attribute, then the
derivations in C will be added to the inputDrvs of the
derivation, and the other store paths in C will be added to
the inputSrcs of the derivations.
For canonicity, the store paths should be in sorted order. */
struct NixString {
const char* s;
const char** context; // must be in sorted order
};
struct NixThunk {
Env* env;
Expr* expr;
};
struct NixApp {
Value *left, *right;
};
struct NixLambda {
Env* env;
ExprLambda* fun;
};
struct NixPrimOpApp {
Value *left, *right;
};
using NixList = std::vector<Value*>;
struct Value {
ValueType type;
union { // TODO(tazjin): std::variant
NixInt integer;
bool boolean;
NixString string;
const char* path;
std::shared_ptr<Bindings> attrs;
std::shared_ptr<NixList> list;
NixThunk thunk;
NixApp app; // TODO(tazjin): "app"?
NixLambda lambda;
std::shared_ptr<PrimOp> primOp;
NixPrimOpApp primOpApp;
NixFloat fpoint;
};
Value() : type(tInt), attrs(nullptr) {
static_assert(offsetof(Value, attrs) + sizeof(attrs) == sizeof(Value));
}
Value(const Value& copy);
Value(Value&& move);
~Value() {}
Value& operator=(const Value& copy);
Value& operator=(Value&& move);
bool isList() const { return type == tList; }
size_t listSize() const { return list->size(); }
};
/* After overwriting an app node, be sure to clear pointers in the
Value to ensure that the target isn't kept alive unnecessarily. */
static inline void clearValue(Value& v) { v.app.left = v.app.right = 0; }
static inline void mkInt(Value& v, NixInt n) {
clearValue(v);
v.type = tInt;
v.integer = n;
}
static inline void mkFloat(Value& v, NixFloat n) {
clearValue(v);
v.type = tFloat;
v.fpoint = n;
}
static inline void mkBool(Value& v, bool b) {
clearValue(v);
v.type = tBool;
v.boolean = b;
}
static inline void mkNull(Value& v) {
clearValue(v);
v.type = tNull;
}
static inline void mkApp(Value& v, Value& left, Value& right) {
v.type = tApp;
v.app.left = &left;
v.app.right = &right;
}
static inline void mkPrimOpApp(Value& v, Value& left, Value& right) {
v.type = tPrimOpApp;
v.app.left = &left;
v.app.right = &right;
}
static inline void mkStringNoCopy(Value& v, const char* s) {
v.type = tString;
v.string.s = s;
v.string.context = 0;
}
static inline void mkString(Value& v, const Symbol& s) {
mkStringNoCopy(v, ((const std::string&)s).c_str());
}
void mkString(Value& v, const char* s);
static inline void mkPathNoCopy(Value& v, const char* s) {
clearValue(v);
v.type = tPath;
v.path = s;
}
void mkPath(Value& v, const char* s);
/* Compute the size in bytes of the given value, including all values
and environments reachable from it. Static expressions (Exprs) are
not included. */
size_t valueSize(const Value& v);
using ValueMap = std::map<Symbol, Value*>;
std::shared_ptr<Value*> allocRootValue(Value* v);
} // namespace nix