tvl-depot/third_party/nix/src/libexpr/value.hh

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

189 lines
4 KiB
C++
Raw Normal View History

#pragma once
#include <tuple>
#include <vector>
#include "libexpr/symbol-table.hh"
#include "libutil/types.hh"
2016-08-30 13:12:12 +02:00
namespace nix {
using ValueType = enum {
tInt = 1,
tBool,
tString,
tPath,
tNull,
tAttrs,
tList,
tThunk,
tApp,
tLambda,
tBlackhole,
tPrimOp,
tPrimOpApp,
_reserved1, // formerly tExternal
tFloat
};
2014-01-21 18:29:55 +01:00
class Bindings;
struct Env;
struct Expr;
struct ExprLambda;
struct PrimOp;
struct PrimOp;
2014-01-21 18:29:55 +01:00
class Symbol;
libexpr: Use int64_t for NixInt Using a 64bit integer on 32bit systems will come with a bit of a performance overhead, but given that Nix doesn't use a lot of integers compared to other types, I think the overhead is negligible also considering that 32bit systems are in decline. The biggest advantage however is that when we use a consistent integer size across all platforms it's less likely that we miss things that we break due to that. One example would be: https://github.com/NixOS/nixpkgs/pull/44233 On Hydra it will evaluate, because the evaluator runs on a 64bit machine, but when evaluating the same on a 32bit machine it will fail, so using 64bit integers should make that consistent. While the change of the type in value.hh is rather easy to do, we have a few more options available for doing the conversion in the lexer: * Via an #ifdef on the architecture and using strtol() or strtoll() accordingly depending on which architecture we are. For the #ifdef we would need another AX_COMPILE_CHECK_SIZEOF in configure.ac. * Using istringstream, which would involve copying the value. * As we're already using boost, lexical_cast might be a good idea. Spoiler: I went for the latter, first of all because lexical_cast does have an overload for const char* and second of all, because it doesn't involve copying around the input string. Also, because istringstream seems to come with a bigger overhead than boost::lexical_cast: https://www.boost.org/doc/libs/release/doc/html/boost_lexical_cast/performance.html The first method (still using strtol/strtoll) also wasn't something I pursued further, because it is also locale-aware which I doubt is what we want, given that the regex for int is [0-9]+. Signed-off-by: aszlig <aszlig@nix.build> Fixes: #2339
2018-08-29 00:23:51 +02:00
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;
Bindings* attrs;
NixList* list;
NixThunk thunk;
NixApp app; // TODO(tazjin): "app"?
NixLambda lambda;
PrimOp* primOp;
NixPrimOpApp primOpApp;
NixFloat fpoint;
};
Value() : type(tInt), integer(0) {}
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