Introduce NormalType for the normal type of a Value

This will be useful to abstract over the ValueType implementation
details

Make use of it already to replace the showType(ValueType) function
This commit is contained in:
Silvan Mosberger 2020-12-11 23:32:45 +01:00
parent 9c143c411b
commit fa307875e9
No known key found for this signature in database
GPG key ID: E8F1E9EAD284E17D
5 changed files with 68 additions and 34 deletions

View file

@ -513,7 +513,7 @@ std::string AttrCursor::getString()
auto & v = forceValue(); auto & v = forceValue();
if (v.type != tString && v.type != tPath) if (v.type != tString && v.type != tPath)
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type)); throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.normalType()));
return v.type == tString ? v.string.s : v.path; return v.type == tString ? v.string.s : v.path;
} }
@ -548,7 +548,7 @@ string_t AttrCursor::getStringWithContext()
else if (v.type == tPath) else if (v.type == tPath)
return {v.path, {}}; return {v.path, {}};
else else
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type)); throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.normalType()));
} }
bool AttrCursor::getBool() bool AttrCursor::getBool()

View file

@ -165,25 +165,20 @@ const Value *getPrimOp(const Value &v) {
return primOp; return primOp;
} }
string showType(NormalType type)
string showType(ValueType type)
{ {
switch (type) { switch (type) {
case tInt: return "an integer"; case nInt: return "an integer";
case tBool: return "a Boolean"; case nBool: return "a Boolean";
case tString: return "a string"; case nString: return "a string";
case tPath: return "a path"; case nPath: return "a path";
case tNull: return "null"; case nNull: return "null";
case tAttrs: return "a set"; case nAttrs: return "a set";
case tList1: case tList2: case tListN: return "a list"; case nList: return "a list";
case tThunk: return "a thunk"; case nFunction: return "a function";
case tApp: return "a function application"; case nExternal: return "an external value";
case tLambda: return "a function"; case nFloat: return "a float";
case tBlackhole: return "a black hole"; case nThunk: return "a thunk";
case tPrimOp: return "a built-in function";
case tPrimOpApp: return "a partially applied built-in function";
case tExternal: return "an external value";
case tFloat: return "a float";
} }
abort(); abort();
} }
@ -198,8 +193,11 @@ string showType(const Value & v)
case tPrimOpApp: case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name)); return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType(); case tExternal: return v.external->showType();
case tThunk: return "a thunk";
case tApp: return "a function application";
case tBlackhole: return "a black hole";
default: default:
return showType(v.type); return showType(v.normalType());
} }
} }

View file

@ -346,7 +346,7 @@ private:
/* Return a string representing the type of the value `v'. */ /* Return a string representing the type of the value `v'. */
string showType(ValueType type); string showType(NormalType type);
string showType(const Value & v); string showType(const Value & v);
/* Decode a context string !<name>!<path> into a pair <path, /* Decode a context string !<name>!<path> into a pair <path,

View file

@ -78,13 +78,13 @@ static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
} }
static void expectType(EvalState & state, ValueType type, static void expectType(EvalState & state, NormalType type,
Value & value, const Pos & pos) Value & value, const Pos & pos)
{ {
forceTrivialValue(state, value, pos); forceTrivialValue(state, value, pos);
if (value.type != type) if (value.normalType() != type)
throw Error("expected %s but got %s at %s", throw Error("expected %s but got %s at %s",
showType(type), showType(value.type), pos); showType(type), showType(value.normalType()), pos);
} }
static std::map<FlakeId, FlakeInput> parseFlakeInputs( static std::map<FlakeId, FlakeInput> parseFlakeInputs(
@ -93,7 +93,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
static FlakeInput parseFlakeInput(EvalState & state, static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos) const std::string & inputName, Value * value, const Pos & pos)
{ {
expectType(state, tAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
FlakeInput input; FlakeInput input;
@ -108,16 +108,16 @@ static FlakeInput parseFlakeInput(EvalState & state,
for (nix::Attr attr : *(value->attrs)) { for (nix::Attr attr : *(value->attrs)) {
try { try {
if (attr.name == sUrl) { if (attr.name == sUrl) {
expectType(state, tString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, *attr.pos);
url = attr.value->string.s; url = attr.value->string.s;
attrs.emplace("url", *url); attrs.emplace("url", *url);
} else if (attr.name == sFlake) { } else if (attr.name == sFlake) {
expectType(state, tBool, *attr.value, *attr.pos); expectType(state, nBool, *attr.value, *attr.pos);
input.isFlake = attr.value->boolean; input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) { } else if (attr.name == sInputs) {
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos); input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
} else if (attr.name == sFollows) { } else if (attr.name == sFollows) {
expectType(state, tString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, *attr.pos);
input.follows = parseInputPath(attr.value->string.s); input.follows = parseInputPath(attr.value->string.s);
} else { } else {
if (attr.value->type == tString) if (attr.value->type == tString)
@ -158,7 +158,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
{ {
std::map<FlakeId, FlakeInput> inputs; std::map<FlakeId, FlakeInput> inputs;
expectType(state, tAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
for (nix::Attr & inputAttr : *(*value).attrs) { for (nix::Attr & inputAttr : *(*value).attrs) {
inputs.emplace(inputAttr.name, inputs.emplace(inputAttr.name,
@ -199,10 +199,10 @@ static Flake getFlake(
Value vInfo; Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
expectType(state, tAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0)); expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
if (auto description = vInfo.attrs->get(state.sDescription)) { if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, tString, *description->value, *description->pos); expectType(state, nString, *description->value, *description->pos);
flake.description = description->value->string.s; flake.description = description->value->string.s;
} }
@ -214,9 +214,9 @@ static Flake getFlake(
auto sOutputs = state.symbols.create("outputs"); auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) { if (auto outputs = vInfo.attrs->get(sOutputs)) {
expectType(state, tLambda, *outputs->value, *outputs->pos); expectType(state, nFunction, *outputs->value, *outputs->pos);
if (outputs->value->lambda.fun->matchAttrs) { if (outputs->value->type == tLambda && outputs->value->lambda.fun->matchAttrs) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) { for (auto & formal : outputs->value->lambda.fun->formals->formals) {
if (formal.name != state.sSelf) if (formal.name != state.sSelf)
flake.inputs.emplace(formal.name, FlakeInput { flake.inputs.emplace(formal.name, FlakeInput {
@ -231,7 +231,7 @@ static Flake getFlake(
auto sNixConfig = state.symbols.create("nixConfig"); auto sNixConfig = state.symbols.create("nixConfig");
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) { if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
expectType(state, tAttrs, *nixConfig->value, *nixConfig->pos); expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
for (auto & setting : *nixConfig->value->attrs) { for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos); forceTrivialValue(state, *setting.value, *setting.pos);

View file

@ -29,6 +29,22 @@ typedef enum {
tFloat tFloat
} ValueType; } ValueType;
// This type abstracts over all actual value types in the language,
// grouping together implementation details like tList*, different function
// types, and types in non-normal form (so thunks and co.)
typedef enum {
nThunk,
nInt,
nFloat,
nBool,
nString,
nPath,
nNull,
nAttrs,
nList,
nFunction,
nExternal
} NormalType;
class Bindings; class Bindings;
struct Env; struct Env;
@ -147,6 +163,26 @@ struct Value
NixFloat fpoint; NixFloat fpoint;
}; };
// Returns the normal type of a Value. This only returns nThunk if the
// Value hasn't been forceValue'd
inline NormalType normalType() const
{
switch (type) {
case tInt: return nInt;
case tBool: return nBool;
case tString: return nString;
case tPath: return nPath;
case tNull: return nNull;
case tAttrs: return nAttrs;
case tList1: case tList2: case tListN: return nList;
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction;
case tExternal: return nExternal;
case tFloat: return nFloat;
case tThunk: case tApp: case tBlackhole: return nThunk;
}
abort();
}
bool isList() const bool isList() const
{ {
return type == tList1 || type == tList2 || type == tListN; return type == tList1 || type == tList2 || type == tListN;