store Symbols in a table as well, like positions

this slightly increases the amount of memory used for any given symbol, but this
increase is more than made up for if the symbol is referenced more than once in
the EvalState that holds it. on average every symbol should be referenced at
least twice (once to introduce a binding, once to use it), so we expect no
increase in memory on average.

symbol tables are limited to 2³² entries like position tables, and similar
arguments apply to why overflow is not likely: 2³² symbols would require as many
string instances (at 24 bytes each) and map entries (at 24 bytes or more each,
assuming that the map holds on average at most one item per bucket as the docs
say). a full symbol table would require at least 192GB of memory just for
symbols, which is well out of reach. (an ofborg eval of nixpks today creates
less than a million symbols!)
This commit is contained in:
pennae 2022-03-05 14:40:24 +01:00
parent 00a3280232
commit 8775be3393
32 changed files with 525 additions and 448 deletions

View file

@ -235,7 +235,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
if (v2.type() == nAttrs) { if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) { for (auto & i : *v2.attrs) {
std::string name = i.name; std::string name = state->symbols[i.name];
if (name.find(searchWord) == 0) { if (name.find(searchWord) == 0) {
if (prefix_ == "") if (prefix_ == "")
completions->add(name); completions->add(name);
@ -600,7 +600,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
auto drvInfo = DerivationInfo { auto drvInfo = DerivationInfo {
std::move(drvPath), std::move(drvPath),
attr->getAttr(state->sOutputName)->getString() attr->getAttr("outputName")->getString()
}; };
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)}; return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};

View file

@ -36,7 +36,7 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
{ {
std::vector<Symbol> res; std::vector<Symbol> res;
for (auto & a : parseAttrPath(s)) for (auto & a : parseAttrPath(s))
res.push_back(state.symbols.create(a)); res.emplace_back(a);
return res; return res;
} }
@ -77,7 +77,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
if (a == v->attrs->end()) { if (a == v->attrs->end()) {
std::set<std::string> attrNames; std::set<std::string> attrNames;
for (auto & attr : *v->attrs) for (auto & attr : *v->attrs)
attrNames.insert(attr.name); attrNames.insert(state.symbols[attr.name]);
auto suggestions = Suggestions::bestMatches(attrNames, attr); auto suggestions = Suggestions::bestMatches(attrNames, attr);
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath); throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);

View file

@ -26,7 +26,7 @@ Bindings * EvalState::allocBindings(size_t capacity)
/* Create a new attribute named 'name' on an existing attribute set stored /* Create a new attribute named 'name' on an existing attribute set stored
in 'vAttrs' and return the newly allocated Value which is associated with in 'vAttrs' and return the newly allocated Value which is associated with
this attribute. */ this attribute. */
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name) Value * EvalState::allocAttr(Value & vAttrs, const SymbolIdx & name)
{ {
Value * v = allocValue(); Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v)); vAttrs.attrs->push_back(Attr(name, v));
@ -40,7 +40,7 @@ Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
} }
Value & BindingsBuilder::alloc(const Symbol & name, PosIdx pos) Value & BindingsBuilder::alloc(const SymbolIdx & name, PosIdx pos)
{ {
auto value = state.allocValue(); auto value = state.allocValue();
bindings->push_back(Attr(name, value, pos)); bindings->push_back(Attr(name, value, pos));

View file

@ -15,10 +15,10 @@ struct Value;
/* Map one attribute name to its value. */ /* Map one attribute name to its value. */
struct Attr struct Attr
{ {
Symbol name; SymbolIdx name;
Value * value; Value * value;
PosIdx pos; PosIdx pos;
Attr(Symbol name, Value * value, PosIdx pos = noPos) Attr(SymbolIdx name, Value * value, PosIdx pos = noPos)
: name(name), value(value), pos(pos) { }; : name(name), value(value), pos(pos) { };
Attr() { }; Attr() { };
bool operator < (const Attr & a) const bool operator < (const Attr & a) const
@ -57,7 +57,7 @@ public:
attrs[size_++] = attr; attrs[size_++] = attr;
} }
iterator find(const Symbol & name) iterator find(const SymbolIdx & name)
{ {
Attr key(name, 0); Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key); iterator i = std::lower_bound(begin(), end(), key);
@ -65,7 +65,7 @@ public:
return end(); return end();
} }
Attr * get(const Symbol & name) Attr * get(const SymbolIdx & name)
{ {
Attr key(name, 0); Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key); iterator i = std::lower_bound(begin(), end(), key);
@ -86,14 +86,15 @@ public:
size_t capacity() { return capacity_; } size_t capacity() { return capacity_; }
/* Returns the attributes in lexicographically sorted order. */ /* Returns the attributes in lexicographically sorted order. */
std::vector<const Attr *> lexicographicOrder() const std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
{ {
std::vector<const Attr *> res; std::vector<const Attr *> res;
res.reserve(size_); res.reserve(size_);
for (size_t n = 0; n < size_; n++) for (size_t n = 0; n < size_; n++)
res.emplace_back(&attrs[n]); res.emplace_back(&attrs[n]);
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) { std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
return (const std::string &) a->name < (const std::string &) b->name; std::string_view sa = symbols[a->name], sb = symbols[b->name];
return sa < sb;
}); });
return res; return res;
} }
@ -118,7 +119,7 @@ public:
: bindings(bindings), state(state) : bindings(bindings), state(state)
{ } { }
void insert(Symbol name, Value * value, PosIdx pos = noPos) void insert(SymbolIdx name, Value * value, PosIdx pos = noPos)
{ {
insert(Attr(name, value, pos)); insert(Attr(name, value, pos));
} }
@ -133,7 +134,7 @@ public:
bindings->push_back(attr); bindings->push_back(attr);
} }
Value & alloc(const Symbol & name, PosIdx pos = noPos); Value & alloc(const SymbolIdx & name, PosIdx pos = noPos);
Value & alloc(std::string_view name, PosIdx pos = noPos); Value & alloc(std::string_view name, PosIdx pos = noPos);

View file

@ -253,7 +253,7 @@ struct AttrDb
std::vector<Symbol> attrs; std::vector<Symbol> attrs;
auto queryAttributes(state->queryAttributes.use()(rowId)); auto queryAttributes(state->queryAttributes.use()(rowId));
while (queryAttributes.next()) while (queryAttributes.next())
attrs.push_back(symbols.create(queryAttributes.getStr(0))); attrs.emplace_back(queryAttributes.getStr(0));
return {{rowId, attrs}}; return {{rowId, attrs}};
} }
case AttrType::String: { case AttrType::String: {
@ -325,7 +325,7 @@ AttrCursor::AttrCursor(
AttrKey AttrCursor::getKey() AttrKey AttrCursor::getKey()
{ {
if (!parent) if (!parent)
return {0, root->state.sEpsilon}; return {0, {""}};
if (!parent->first->cachedValue) { if (!parent->first->cachedValue) {
parent->first->cachedValue = root->db->getAttr( parent->first->cachedValue = root->db->getAttr(
parent->first->getKey(), root->state.symbols); parent->first->getKey(), root->state.symbols);
@ -340,7 +340,7 @@ Value & AttrCursor::getValue()
if (parent) { if (parent) {
auto & vParent = parent->first->getValue(); auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos); root->state.forceAttrs(vParent, noPos);
auto attr = vParent.attrs->get(parent->second); auto attr = vParent.attrs->get(root->state.symbols.create(parent->second));
if (!attr) if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
_value = allocRootValue(attr->value); _value = allocRootValue(attr->value);
@ -419,7 +419,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
return Suggestions::bestMatches(strAttrNames, name); return Suggestions::bestMatches(strAttrNames, name);
} }
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name, bool forceErrors)
{ {
if (root->db) { if (root->db) {
if (!cachedValue) if (!cachedValue)
@ -461,10 +461,10 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs) {
if (root->db) if (root->db)
root->db->setPlaceholder({cachedValue->first, attr.name}); root->db->setPlaceholder({cachedValue->first, root->state.symbols[attr.name]});
} }
auto attr = v.attrs->get(name); auto attr = v.attrs->get(root->state.symbols.create(name));
if (!attr) { if (!attr) {
if (root->db) { if (root->db) {
@ -486,12 +486,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
} }
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name) ref<AttrCursor> AttrCursor::getAttr(std::string_view name, bool forceErrors)
{
return maybeGetAttr(root->state.symbols.create(name));
}
ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
{ {
auto p = maybeGetAttr(name, forceErrors); auto p = maybeGetAttr(name, forceErrors);
if (!p) if (!p)
@ -499,11 +494,6 @@ ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
return ref(p); return ref(p);
} }
ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
{
return getAttr(root->state.symbols.create(name));
}
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force) OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
{ {
auto res = shared_from_this(); auto res = shared_from_this();
@ -616,7 +606,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
std::vector<Symbol> attrs; std::vector<Symbol> attrs;
for (auto & attr : *getValue().attrs) for (auto & attr : *getValue().attrs)
attrs.push_back(attr.name); attrs.push_back(root->state.symbols[attr.name]);
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
return (const std::string &) a < (const std::string &) b; return (const std::string &) a < (const std::string &) b;
}); });
@ -635,7 +625,7 @@ bool AttrCursor::isDerivation()
StorePath AttrCursor::forceDerivation() StorePath AttrCursor::forceDerivation()
{ {
auto aDrvPath = getAttr(root->state.sDrvPath, true); auto aDrvPath = getAttr("drvPath", true);
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has /* The eval cache contains 'drvPath', but the actual path has

View file

@ -96,13 +96,9 @@ public:
Suggestions getSuggestionsForAttr(Symbol name); Suggestions getSuggestionsForAttr(Symbol name);
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false); std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name, bool forceErrors = false);
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name); ref<AttrCursor> getAttr(std::string_view name, bool forceErrors = false);
ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
ref<AttrCursor> getAttr(std::string_view name);
/* Get an attribute along a chain of attrsets. Note that this does /* Get an attribute along a chain of attrsets. Note that this does
not auto-call functors or functions. */ not auto-call functors or functions. */

View file

@ -96,7 +96,8 @@ RootValue allocRootValue(Value * v)
} }
void Value::print(std::ostream & str, std::set<const void *> * seen) const void Value::print(const SymbolTable & symbols, std::ostream & str,
std::set<const void *> * seen) const
{ {
checkInterrupt(); checkInterrupt();
@ -129,9 +130,9 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
str << "«repeated»"; str << "«repeated»";
else { else {
str << "{ "; str << "{ ";
for (auto & i : attrs->lexicographicOrder()) { for (auto & i : attrs->lexicographicOrder(symbols)) {
str << i->name << " = "; str << symbols[i->name] << " = ";
i->value->print(str, seen); i->value->print(symbols, str, seen);
str << "; "; str << "; ";
} }
str << "}"; str << "}";
@ -146,7 +147,7 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
else { else {
str << "[ "; str << "[ ";
for (auto v2 : listItems()) { for (auto v2 : listItems()) {
v2->print(str, seen); v2->print(symbols, str, seen);
str << " "; str << " ";
} }
str << "]"; str << "]";
@ -177,17 +178,18 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
} }
void Value::print(std::ostream & str, bool showRepeated) const void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
{ {
std::set<const void *> seen; std::set<const void *> seen;
print(str, showRepeated ? nullptr : &seen); print(symbols, str, showRepeated ? nullptr : &seen);
} }
std::ostream & operator << (std::ostream & str, const Value & v) std::string printValue(const EvalState & state, const Value & v)
{ {
v.print(str, false); std::ostringstream out;
return str; v.print(state.symbols, out);
return out.str();
} }
@ -306,9 +308,9 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
#endif #endif
static Symbol getName(const AttrName & name, EvalState & state, Env & env) static SymbolIdx getName(const AttrName & name, EvalState & state, Env & env)
{ {
if (name.symbol.set()) { if (name.symbol) {
return name.symbol; return name.symbol;
} else { } else {
Value nameValue; Value nameValue;
@ -639,7 +641,7 @@ Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp) size_t arity, PrimOpFun primOp)
{ {
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name; auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
Symbol sym = symbols.create(name2); auto sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of /* Hack to make constants lazy: turn them into a application of
the primop to a dummy value. */ the primop to a dummy value. */
@ -673,7 +675,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
return addConstant(primOp.name, v); return addConstant(primOp.name, v);
} }
Symbol envName = symbols.create(primOp.name); auto envName = symbols.create(primOp.name);
if (hasPrefix(primOp.name, "__")) if (hasPrefix(primOp.name, "__"))
primOp.name = primOp.name.substr(2); primOp.name = primOp.name.substr(2);
@ -767,11 +769,11 @@ void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::stri
}); });
} }
void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol & sym, const PosIdx p2) const void EvalState::throwEvalError(const PosIdx p1, const char * s, const SymbolIdx sym, const PosIdx p2) const
{ {
// p1 is where the error occurred; p2 is a position mentioned in the message. // p1 is where the error occurred; p2 is a position mentioned in the message.
throw EvalError({ throw EvalError({
.msg = hintfmt(s, sym, positions[p2]), .msg = hintfmt(s, symbols[sym], positions[p2]),
.errPos = positions[p1] .errPos = positions[p1]
}); });
} }
@ -785,19 +787,19 @@ void EvalState::throwTypeError(const PosIdx pos, const char * s) const
} }
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
const Symbol & s2) const const SymbolIdx s2) const
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt(s, fun.showNamePos(positions), s2), .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
.errPos = positions[pos] .errPos = positions[pos]
}); });
} }
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const ExprLambda & fun, const Symbol & s2) const const ExprLambda & fun, const SymbolIdx s2) const
{ {
throw TypeError(ErrorInfo { throw TypeError(ErrorInfo {
.msg = hintfmt(s, fun.showNamePos(positions), s2), .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
.errPos = positions[pos], .errPos = positions[pos],
.suggestions = suggestions, .suggestions = suggestions,
}); });
@ -901,7 +903,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
return j->value; return j->value;
} }
if (!env->prevWith) if (!env->prevWith)
throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name); throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name]);
for (size_t l = env->prevWith; l; --l, env = env->up) ; for (size_t l = env->prevWith; l; --l, env = env->up) ;
} }
} }
@ -1187,7 +1189,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (nameVal.type() == nNull) if (nameVal.type() == nNull)
continue; continue;
state.forceStringNoCtx(nameVal); state.forceStringNoCtx(nameVal);
Symbol nameSym = state.symbols.create(nameVal.string.s); auto nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym); Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end()) if (j != v.attrs->end())
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos); state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos);
@ -1243,10 +1245,12 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
for (auto & i : attrPath) { for (auto & i : attrPath) {
if (!first) out << '.'; else first = false; if (!first) out << '.'; else first = false;
try { try {
out << getName(i, state, env); out << state.symbols[getName(i, state, env)];
} catch (Error & e) { } catch (Error & e) {
assert(!i.symbol.set()); assert(!i.symbol);
out << "\"${" << *i.expr << "}\""; out << "\"${";
i.expr->show(state.symbols, out);
out << "}\"";
} }
} }
return out.str(); return out.str();
@ -1266,7 +1270,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) { for (auto & i : attrPath) {
state.nrLookups++; state.nrLookups++;
Bindings::iterator j; Bindings::iterator j;
Symbol name = getName(i, state, env); auto name = getName(i, state, env);
if (def) { if (def) {
state.forceValue(*vAttrs, pos); state.forceValue(*vAttrs, pos);
if (vAttrs->type() != nAttrs || if (vAttrs->type() != nAttrs ||
@ -1280,11 +1284,11 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
std::set<std::string> allAttrNames; std::set<std::string> allAttrNames;
for (auto & attr : *vAttrs->attrs) for (auto & attr : *vAttrs->attrs)
allAttrNames.insert(attr.name); allAttrNames.insert(state.symbols[attr.name]);
state.throwEvalError( state.throwEvalError(
pos, pos,
Suggestions::bestMatches(allAttrNames, name), Suggestions::bestMatches(allAttrNames, state.symbols[name]),
"attribute '%1%' missing", name); "attribute '%1%' missing", state.symbols[name]);
} }
} }
vAttrs = j->value; vAttrs = j->value;
@ -1316,7 +1320,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) { for (auto & i : attrPath) {
state.forceValue(*vAttrs, noPos); state.forceValue(*vAttrs, noPos);
Bindings::iterator j; Bindings::iterator j;
Symbol name = getName(i, state, env); auto name = getName(i, state, env);
if (vAttrs->type() != nAttrs || if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{ {
@ -1366,7 +1370,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
ExprLambda & lambda(*vCur.lambda.fun); ExprLambda & lambda(*vCur.lambda.fun);
auto size = auto size =
(!lambda.arg.set() ? 0 : 1) + (!lambda.arg ? 0 : 1) +
(lambda.hasFormals() ? lambda.formals->formals.size() : 0); (lambda.hasFormals() ? lambda.formals->formals.size() : 0);
Env & env2(allocEnv(size)); Env & env2(allocEnv(size));
env2.up = vCur.lambda.env; env2.up = vCur.lambda.env;
@ -1379,7 +1383,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
else { else {
forceAttrs(*args[0], pos); forceAttrs(*args[0], pos);
if (lambda.arg.set()) if (lambda.arg)
env2.values[displ++] = args[0]; env2.values[displ++] = args[0];
/* For each formal argument, get the actual argument. If /* For each formal argument, get the actual argument. If
@ -1407,10 +1411,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (!lambda.formals->has(i.name)) { if (!lambda.formals->has(i.name)) {
std::set<std::string> formalNames; std::set<std::string> formalNames;
for (auto & formal : lambda.formals->formals) for (auto & formal : lambda.formals->formals)
formalNames.insert(formal.name); formalNames.insert(symbols[formal.name]);
throwTypeError( throwTypeError(
pos, pos,
Suggestions::bestMatches(formalNames, i.name), Suggestions::bestMatches(formalNames, symbols[i.name]),
"%1% called with unexpected argument '%2%'", "%1% called with unexpected argument '%2%'",
lambda, lambda,
i.name); i.name);
@ -1428,8 +1432,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
} catch (Error & e) { } catch (Error & e) {
if (loggerSettings.showTrace.get()) { if (loggerSettings.showTrace.get()) {
addErrorTrace(e, lambda.pos, "while evaluating %s", addErrorTrace(e, lambda.pos, "while evaluating %s",
(lambda.name.set() (lambda.name
? "'" + (const std::string &) lambda.name + "'" ? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda")); : "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", ""); addErrorTrace(e, pos, "from call site%s", "");
} }
@ -1578,7 +1582,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
Nix attempted to evaluate a function as a top level expression; in Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See values, or passed explicitly with '--arg' or '--argstr'. See
https://nixos.org/manual/nix/stable/#ss-functions.)", i.name); https://nixos.org/manual/nix/stable/#ss-functions.)", symbols[i.name]);
} }
} }
@ -1610,7 +1614,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{ {
if (!state.evalBool(env, cond, pos)) { if (!state.evalBool(env, cond, pos)) {
std::ostringstream out; std::ostringstream out;
cond->show(out); cond->show(state.symbols, out);
state.throwAssertionError(pos, "assertion '%1%' failed", out.str()); state.throwAssertionError(pos, "assertion '%1%' failed", out.str());
} }
body->eval(state, env, v); body->eval(state, env, v);
@ -1844,7 +1848,7 @@ void EvalState::forceValueDeep(Value & v)
try { try {
recurse(*i.value); recurse(*i.value);
} catch (Error & e) { } catch (Error & e) {
addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", i.name); addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]);
throw; throw;
} }
} }
@ -2267,7 +2271,7 @@ void EvalState::printStats()
auto list = topObj.list("functions"); auto list = topObj.list("functions");
for (auto & i : functionCalls) { for (auto & i : functionCalls) {
auto obj = list.object(); auto obj = list.object();
if (i.first->name.set()) if (i.first->name)
obj.attr("name", (const std::string &) i.first->name); obj.attr("name", (const std::string &) i.first->name);
else else
obj.attr("name", nullptr); obj.attr("name", nullptr);

View file

@ -53,7 +53,8 @@ void copyContext(const Value & v, PathSet & context);
typedef std::map<Path, StorePath> SrcToStore; typedef std::map<Path, StorePath> SrcToStore;
std::ostream & operator << (std::ostream & str, const Value & v); std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v);
std::string printValue(const EvalState & state, const Value & v);
typedef std::pair<std::string, std::string> SearchPathElem; typedef std::pair<std::string, std::string> SearchPathElem;
@ -77,7 +78,7 @@ public:
static inline std::string derivationNixPath = "//builtin/derivation.nix"; static inline std::string derivationNixPath = "//builtin/derivation.nix";
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, const SymbolIdx sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString, sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
@ -86,7 +87,7 @@ public:
sRecurseForDerivations, sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
sPrefix; sPrefix;
Symbol sDerivationNix; SymbolIdx sDerivationNix;
/* If set, force copying files to the Nix store even if they /* If set, force copying files to the Nix store even if they
already exist there. */ already exist there. */
@ -268,14 +269,14 @@ public:
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const; void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx p1, const char * s, const Symbol & sym, const PosIdx p2) const; void throwEvalError(const PosIdx p1, const char * s, const SymbolIdx sym, const PosIdx p2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s) const; void throwTypeError(const PosIdx pos, const char * s) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol & s2) const; void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const SymbolIdx s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const ExprLambda & fun, const Symbol & s2) const; const ExprLambda & fun, const SymbolIdx s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const char * s, const Value & v) const; void throwTypeError(const char * s, const Value & v) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
@ -391,7 +392,7 @@ public:
inline Value * allocValue(); inline Value * allocValue();
inline Env & allocEnv(size_t size); inline Env & allocEnv(size_t size);
Value * allocAttr(Value & vAttrs, const Symbol & name); Value * allocAttr(Value & vAttrs, const SymbolIdx & name);
Value * allocAttr(Value & vAttrs, std::string_view name); Value * allocAttr(Value & vAttrs, std::string_view name);
Bindings * allocBindings(size_t capacity); Bindings * allocBindings(size_t capacity);

View file

@ -127,21 +127,23 @@ static FlakeInput parseFlakeInput(EvalState & state,
} else { } else {
switch (attr.value->type()) { switch (attr.value->type()) {
case nString: case nString:
attrs.emplace(attr.name, attr.value->string.s); attrs.emplace(state.symbols[attr.name], attr.value->string.s);
break; break;
case nBool: case nBool:
attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean }); attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
break; break;
case nInt: case nInt:
attrs.emplace(attr.name, (long unsigned int)attr.value->integer); attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
break; break;
default: default:
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected", throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
attr.name, showType(*attr.value)); state.symbols[attr.name], showType(*attr.value));
} }
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[attr.pos], hintfmt("in flake attribute '%s'", attr.name)); e.addTrace(
state.positions[attr.pos],
hintfmt("in flake attribute '%s'", state.symbols[attr.name]));
throw; throw;
} }
} }
@ -176,9 +178,9 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
expectType(state, nAttrs, *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(state.symbols[inputAttr.name],
parseFlakeInput(state, parseFlakeInput(state,
inputAttr.name, state.symbols[inputAttr.name],
inputAttr.value, inputAttr.value,
inputAttr.pos, inputAttr.pos,
baseDir, baseDir,
@ -218,7 +220,7 @@ 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, nAttrs, vInfo, state.positions.add({state.symbols.create(flakeFile), foFile}, 0, 0)); expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
if (auto description = vInfo.attrs->get(state.sDescription)) { if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, nString, *description->value, description->pos); expectType(state, nString, *description->value, description->pos);
@ -238,8 +240,8 @@ static Flake getFlake(
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) { if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
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(state.symbols[formal.name], FlakeInput {
.ref = parseFlakeRef(formal.name) .ref = parseFlakeRef(state.symbols[formal.name])
}); });
} }
} }
@ -255,30 +257,36 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) { for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, setting.pos); forceTrivialValue(state, *setting.value, setting.pos);
if (setting.value->type() == nString) if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, setting.pos))}); flake.config.settings.emplace(
state.symbols[setting.name],
std::string(state.forceStringNoCtx(*setting.value, setting.pos)));
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; PathSet emptyContext = {};
flake.config.settings.emplace( flake.config.settings.emplace(
setting.name, state.symbols[setting.name],
state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned()); state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
} }
else if (setting.value->type() == nInt) else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, setting.pos)}); flake.config.settings.emplace(
state.symbols[setting.name],
state.forceInt(*setting.value, setting.pos));
else if (setting.value->type() == nBool) else if (setting.value->type() == nBool)
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, setting.pos) }}); flake.config.settings.emplace(
state.symbols[setting.name],
Explicit<bool> { state.forceBool(*setting.value, setting.pos) });
else if (setting.value->type() == nList) { else if (setting.value->type() == nList) {
std::vector<std::string> ss; std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) { for (auto elem : setting.value->listItems()) {
if (elem->type() != nString) if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value)); state.symbols[setting.name], showType(*setting.value));
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos)); ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
} }
flake.config.settings.insert({setting.name, ss}); flake.config.settings.emplace(state.symbols[setting.name], ss);
} }
else else
throw TypeError("flake configuration setting '%s' is %s", throw TypeError("flake configuration setting '%s' is %s",
setting.name, showType(*setting.value)); state.symbols[setting.name], showType(*setting.value));
} }
} }
@ -288,7 +296,7 @@ static Flake getFlake(
attr.name != sOutputs && attr.name != sOutputs &&
attr.name != sNixConfig) attr.name != sNixConfig)
throw Error("flake '%s' has an unsupported attribute '%s', at %s", throw Error("flake '%s' has an unsupported attribute '%s', at %s",
lockedRef, attr.name, state.positions[attr.pos]); lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
} }
return flake; return flake;

View file

@ -179,7 +179,7 @@ StringSet DrvInfo::queryMetaNames()
StringSet res; StringSet res;
if (!getMeta()) return res; if (!getMeta()) return res;
for (auto & i : *meta) for (auto & i : *meta)
res.insert(i.name); res.emplace(state->symbols[i.name]);
return res; return res;
} }
@ -269,7 +269,7 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
{ {
getMeta(); getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0)); auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
Symbol sym = state->symbols.create(name); auto sym = state->symbols.create(name);
if (meta) if (meta)
for (auto i : *meta) for (auto i : *meta)
if (i.name != sym) if (i.name != sym)
@ -356,11 +356,11 @@ static void getDerivations(EvalState & state, Value & vIn,
there are names clashes between derivations, the derivation there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take bound to the attribute with the "lower" name should take
precedence). */ precedence). */
for (auto & i : v.attrs->lexicographicOrder()) { for (auto & i : v.attrs->lexicographicOrder(state.symbols)) {
debug("evaluating attribute '%1%'", i->name); debug("evaluating attribute '%1%'", state.symbols[i->name]);
if (!std::regex_match(std::string(i->name), attrRegex)) if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
continue; continue;
std::string pathPrefix2 = addToPath(pathPrefix, i->name); std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
if (combineChannels) if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {

View file

@ -1,5 +1,7 @@
#include "nixexpr.hh" #include "nixexpr.hh"
#include "derivations.hh" #include "derivations.hh"
#include "eval.hh"
#include "symbol-table.hh"
#include "util.hh" #include "util.hh"
#include <cstdlib> #include <cstdlib>
@ -10,12 +12,6 @@ namespace nix {
/* Displaying abstract syntax trees. */ /* Displaying abstract syntax trees. */
std::ostream & operator << (std::ostream & str, const Expr & e)
{
e.show(str);
return str;
}
static void showString(std::ostream & str, std::string_view s) static void showString(std::ostream & str, std::string_view s)
{ {
str << '"'; str << '"';
@ -54,81 +50,101 @@ static void showId(std::ostream & str, std::string_view s)
std::ostream & operator << (std::ostream & str, const Symbol & sym) std::ostream & operator << (std::ostream & str, const Symbol & sym)
{ {
showId(str, *sym.s); showId(str, sym.s);
return str; return str;
} }
void Expr::show(std::ostream & str) const void Expr::show(const SymbolTable & symbols, std::ostream & str) const
{ {
abort(); abort();
} }
void ExprInt::show(std::ostream & str) const void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << n; str << n;
} }
void ExprFloat::show(std::ostream & str) const void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << nf; str << nf;
} }
void ExprString::show(std::ostream & str) const void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{ {
showString(str, s); showString(str, s);
} }
void ExprPath::show(std::ostream & str) const void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << s; str << s;
} }
void ExprVar::show(std::ostream & str) const void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << name; str << symbols[name];
} }
void ExprSelect::show(std::ostream & str) const void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "(" << *e << ")." << showAttrPath(attrPath); str << "(";
if (def) str << " or (" << *def << ")"; e->show(symbols, str);
str << ")." << showAttrPath(symbols, attrPath);
if (def) {
str << " or (";
def->show(symbols, str);
str << ")";
}
} }
void ExprOpHasAttr::show(std::ostream & str) const void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")"; str << "((";
e->show(symbols, str);
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
} }
void ExprAttrs::show(std::ostream & str) const void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
{ {
if (recursive) str << "rec "; if (recursive) str << "rec ";
str << "{ "; str << "{ ";
typedef const decltype(attrs)::value_type * Attr; typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted; std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i); for (auto & i : attrs) sorted.push_back(&i);
std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) { std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
return (const std::string &) a->first < (const std::string &) b->first; std::string_view sa = symbols[a->first], sb = symbols[b->first];
return sa < sb;
}); });
for (auto & i : sorted) { for (auto & i : sorted) {
if (i->second.inherited) if (i->second.inherited)
str << "inherit " << i->first << " " << "; "; str << "inherit " << symbols[i->first] << " " << "; ";
else else {
str << i->first << " = " << *i->second.e << "; "; str << symbols[i->first] << " = ";
i->second.e->show(symbols, str);
str << "; ";
}
}
for (auto & i : dynamicAttrs) {
str << "\"${";
i.nameExpr->show(symbols, str);
str << "}\" = ";
i.valueExpr->show(symbols, str);
str << "; ";
} }
for (auto & i : dynamicAttrs)
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
str << "}"; str << "}";
} }
void ExprList::show(std::ostream & str) const void ExprList::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "[ "; str << "[ ";
for (auto & i : elems) for (auto & i : elems) {
str << "(" << *i << ") "; str << "(";
i->show(symbols, str);
str << ") ";
}
str << "]"; str << "]";
} }
void ExprLambda::show(std::ostream & str) const void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "("; str << "(";
if (hasFormals()) { if (hasFormals()) {
@ -136,74 +152,100 @@ void ExprLambda::show(std::ostream & str) const
bool first = true; bool first = true;
for (auto & i : formals->formals) { for (auto & i : formals->formals) {
if (first) first = false; else str << ", "; if (first) first = false; else str << ", ";
str << i.name; str << symbols[i.name];
if (i.def) str << " ? " << *i.def; if (i.def) {
str << " ? ";
i.def->show(symbols, str);
}
} }
if (formals->ellipsis) { if (formals->ellipsis) {
if (!first) str << ", "; if (!first) str << ", ";
str << "..."; str << "...";
} }
str << " }"; str << " }";
if (arg.set()) str << " @ "; if (arg) str << " @ ";
} }
if (arg.set()) str << arg; if (arg) str << symbols[arg];
str << ": " << *body << ")"; str << ": ";
body->show(symbols, str);
str << ")";
} }
void ExprCall::show(std::ostream & str) const void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << '(' << *fun; str << '(';
fun->show(symbols, str);
for (auto e : args) { for (auto e : args) {
str << ' '; str << ' ';
str << *e; e->show(symbols, str);
} }
str << ')'; str << ')';
} }
void ExprLet::show(std::ostream & str) const void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "(let "; str << "(let ";
for (auto & i : attrs->attrs) for (auto & i : attrs->attrs)
if (i.second.inherited) { if (i.second.inherited) {
str << "inherit " << i.first << "; "; str << "inherit " << symbols[i.first] << "; ";
} }
else else {
str << i.first << " = " << *i.second.e << "; "; str << symbols[i.first] << " = ";
str << "in " << *body << ")"; i.second.e->show(symbols, str);
str << "; ";
}
str << "in ";
body->show(symbols, str);
str << ")";
} }
void ExprWith::show(std::ostream & str) const void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "(with " << *attrs << "; " << *body << ")"; str << "(with ";
attrs->show(symbols, str);
str << "; ";
body->show(symbols, str);
str << ")";
} }
void ExprIf::show(std::ostream & str) const void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "(if " << *cond << " then " << *then << " else " << *else_ << ")"; str << "(if ";
cond->show(symbols, str);
str << " then ";
then->show(symbols, str);
str << " else ";
else_->show(symbols, str);
str << ")";
} }
void ExprAssert::show(std::ostream & str) const void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "assert " << *cond << "; " << *body; str << "assert ";
cond->show(symbols, str);
str << "; ";
body->show(symbols, str);
} }
void ExprOpNot::show(std::ostream & str) const void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "(! " << *e << ")"; str << "(! ";
e->show(symbols, str);
str << ")";
} }
void ExprConcatStrings::show(std::ostream & str) const void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const
{ {
bool first = true; bool first = true;
str << "("; str << "(";
for (auto & i : *es) { for (auto & i : *es) {
if (first) first = false; else str << " + "; if (first) first = false; else str << " + ";
str << *i.second; i.second->show(symbols, str);
} }
str << ")"; str << ")";
} }
void ExprPos::show(std::ostream & str) const void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << "__curPos"; str << "__curPos";
} }
@ -234,16 +276,19 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
} }
std::string showAttrPath(const AttrPath & attrPath) std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
{ {
std::ostringstream out; std::ostringstream out;
bool first = true; bool first = true;
for (auto & i : attrPath) { for (auto & i : attrPath) {
if (!first) out << '.'; else first = false; if (!first) out << '.'; else first = false;
if (i.symbol.set()) if (i.symbol)
out << i.symbol; out << symbols[i.symbol];
else else {
out << "\"${" << *i.expr << "}\""; out << "\"${";
i.expr->show(symbols, out);
out << "}\"";
}
} }
return out.str(); return out.str();
} }
@ -252,28 +297,28 @@ std::string showAttrPath(const AttrPath & attrPath)
/* Computing levels/displacements for variables. */ /* Computing levels/displacements for variables. */
void Expr::bindVars(const PosTable & pt, const StaticEnv & env) void Expr::bindVars(const EvalState & es, const StaticEnv & env)
{ {
abort(); abort();
} }
void ExprInt::bindVars(const PosTable & pt, const StaticEnv & env) void ExprInt::bindVars(const EvalState & es, const StaticEnv & env)
{ {
} }
void ExprFloat::bindVars(const PosTable & pt, const StaticEnv & env) void ExprFloat::bindVars(const EvalState & es, const StaticEnv & env)
{ {
} }
void ExprString::bindVars(const PosTable & pt, const StaticEnv & env) void ExprString::bindVars(const EvalState & es, const StaticEnv & env)
{ {
} }
void ExprPath::bindVars(const PosTable & pt, const StaticEnv & env) void ExprPath::bindVars(const EvalState & es, const StaticEnv & env)
{ {
} }
void ExprVar::bindVars(const PosTable & pt, const StaticEnv & env) void ExprVar::bindVars(const EvalState & es, const StaticEnv & env)
{ {
/* Check whether the variable appears in the environment. If so, /* Check whether the variable appears in the environment. If so,
set its level and displacement. */ set its level and displacement. */
@ -299,31 +344,31 @@ void ExprVar::bindVars(const PosTable & pt, const StaticEnv & env)
"undefined variable" error now. */ "undefined variable" error now. */
if (withLevel == -1) if (withLevel == -1)
throw UndefinedVarError({ throw UndefinedVarError({
.msg = hintfmt("undefined variable '%1%'", name), .msg = hintfmt("undefined variable '%1%'", es.symbols[name]),
.errPos = pt[pos] .errPos = es.positions[pos]
}); });
fromWith = true; fromWith = true;
this->level = withLevel; this->level = withLevel;
} }
void ExprSelect::bindVars(const PosTable & pt, const StaticEnv & env) void ExprSelect::bindVars(const EvalState & es, const StaticEnv & env)
{ {
e->bindVars(pt, env); e->bindVars(es, env);
if (def) def->bindVars(pt, env); if (def) def->bindVars(es, env);
for (auto & i : attrPath) for (auto & i : attrPath)
if (!i.symbol.set()) if (!i.symbol)
i.expr->bindVars(pt, env); i.expr->bindVars(es, env);
} }
void ExprOpHasAttr::bindVars(const PosTable & pt, const StaticEnv & env) void ExprOpHasAttr::bindVars(const EvalState & es, const StaticEnv & env)
{ {
e->bindVars(pt, env); e->bindVars(es, env);
for (auto & i : attrPath) for (auto & i : attrPath)
if (!i.symbol.set()) if (!i.symbol)
i.expr->bindVars(pt, env); i.expr->bindVars(es, env);
} }
void ExprAttrs::bindVars(const PosTable & pt, const StaticEnv & env) void ExprAttrs::bindVars(const EvalState & es, const StaticEnv & env)
{ {
const StaticEnv * dynamicEnv = &env; const StaticEnv * dynamicEnv = &env;
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0); StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
@ -338,35 +383,35 @@ void ExprAttrs::bindVars(const PosTable & pt, const StaticEnv & env)
// No need to sort newEnv since attrs is in sorted order. // No need to sort newEnv since attrs is in sorted order.
for (auto & i : attrs) for (auto & i : attrs)
i.second.e->bindVars(pt, i.second.inherited ? env : newEnv); i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
} }
else else
for (auto & i : attrs) for (auto & i : attrs)
i.second.e->bindVars(pt, env); i.second.e->bindVars(es, env);
for (auto & i : dynamicAttrs) { for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(pt, *dynamicEnv); i.nameExpr->bindVars(es, *dynamicEnv);
i.valueExpr->bindVars(pt, *dynamicEnv); i.valueExpr->bindVars(es, *dynamicEnv);
} }
} }
void ExprList::bindVars(const PosTable & pt, const StaticEnv & env) void ExprList::bindVars(const EvalState & es, const StaticEnv & env)
{ {
for (auto & i : elems) for (auto & i : elems)
i->bindVars(pt, env); i->bindVars(es, env);
} }
void ExprLambda::bindVars(const PosTable & pt, const StaticEnv & env) void ExprLambda::bindVars(const EvalState & es, const StaticEnv & env)
{ {
StaticEnv newEnv( StaticEnv newEnv(
false, &env, false, &env,
(hasFormals() ? formals->formals.size() : 0) + (hasFormals() ? formals->formals.size() : 0) +
(!arg.set() ? 0 : 1)); (!arg ? 0 : 1));
Displacement displ = 0; Displacement displ = 0;
if (arg.set()) newEnv.vars.emplace_back(arg, displ++); if (arg) newEnv.vars.emplace_back(arg, displ++);
if (hasFormals()) { if (hasFormals()) {
for (auto & i : formals->formals) for (auto & i : formals->formals)
@ -375,20 +420,20 @@ void ExprLambda::bindVars(const PosTable & pt, const StaticEnv & env)
newEnv.sort(); newEnv.sort();
for (auto & i : formals->formals) for (auto & i : formals->formals)
if (i.def) i.def->bindVars(pt, newEnv); if (i.def) i.def->bindVars(es, newEnv);
} }
body->bindVars(pt, newEnv); body->bindVars(es, newEnv);
} }
void ExprCall::bindVars(const PosTable & pt, const StaticEnv & env) void ExprCall::bindVars(const EvalState & es, const StaticEnv & env)
{ {
fun->bindVars(pt, env); fun->bindVars(es, env);
for (auto e : args) for (auto e : args)
e->bindVars(pt, env); e->bindVars(es, env);
} }
void ExprLet::bindVars(const PosTable & pt, const StaticEnv & env) void ExprLet::bindVars(const EvalState & es, const StaticEnv & env)
{ {
StaticEnv newEnv(false, &env, attrs->attrs.size()); StaticEnv newEnv(false, &env, attrs->attrs.size());
@ -399,12 +444,12 @@ void ExprLet::bindVars(const PosTable & pt, const StaticEnv & env)
// No need to sort newEnv since attrs->attrs is in sorted order. // No need to sort newEnv since attrs->attrs is in sorted order.
for (auto & i : attrs->attrs) for (auto & i : attrs->attrs)
i.second.e->bindVars(pt, i.second.inherited ? env : newEnv); i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
body->bindVars(pt, newEnv); body->bindVars(es, newEnv);
} }
void ExprWith::bindVars(const PosTable & pt, const StaticEnv & env) void ExprWith::bindVars(const EvalState & es, const StaticEnv & env)
{ {
/* Does this `with' have an enclosing `with'? If so, record its /* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous level so that `lookupVar' can look up variables in the previous
@ -418,57 +463,60 @@ void ExprWith::bindVars(const PosTable & pt, const StaticEnv & env)
break; break;
} }
attrs->bindVars(pt, env); attrs->bindVars(es, env);
StaticEnv newEnv(true, &env); StaticEnv newEnv(true, &env);
body->bindVars(pt, newEnv); body->bindVars(es, newEnv);
} }
void ExprIf::bindVars(const PosTable & pt, const StaticEnv & env) void ExprIf::bindVars(const EvalState & es, const StaticEnv & env)
{ {
cond->bindVars(pt, env); cond->bindVars(es, env);
then->bindVars(pt, env); then->bindVars(es, env);
else_->bindVars(pt, env); else_->bindVars(es, env);
} }
void ExprAssert::bindVars(const PosTable & pt, const StaticEnv & env) void ExprAssert::bindVars(const EvalState & es, const StaticEnv & env)
{ {
cond->bindVars(pt, env); cond->bindVars(es, env);
body->bindVars(pt, env); body->bindVars(es, env);
} }
void ExprOpNot::bindVars(const PosTable & pt, const StaticEnv & env) void ExprOpNot::bindVars(const EvalState & es, const StaticEnv & env)
{ {
e->bindVars(pt, env); e->bindVars(es, env);
} }
void ExprConcatStrings::bindVars(const PosTable & pt, const StaticEnv & env) void ExprConcatStrings::bindVars(const EvalState & es, const StaticEnv & env)
{ {
for (auto & i : *es) for (auto & i : *this->es)
i.second->bindVars(pt, env); i.second->bindVars(es, env);
} }
void ExprPos::bindVars(const PosTable & pt, const StaticEnv & env) void ExprPos::bindVars(const EvalState & es, const StaticEnv & env)
{ {
} }
/* Storing function names. */ /* Storing function names. */
void Expr::setName(Symbol & name) void Expr::setName(SymbolIdx name)
{ {
} }
void ExprLambda::setName(Symbol & name) void ExprLambda::setName(SymbolIdx name)
{ {
this->name = name; this->name = name;
body->setName(name); body->setName(name);
} }
std::string ExprLambda::showNamePos(const PosTable & pt) const std::string ExprLambda::showNamePos(const EvalState & state) const
{ {
return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pt[pos]); std::string id(name
? concatStrings("'", state.symbols[name], "'")
: "anonymous function");
return fmt("%1% at %2%", id, state.positions[pos]);
} }
@ -478,8 +526,7 @@ std::string ExprLambda::showNamePos(const PosTable & pt) const
size_t SymbolTable::totalSize() const size_t SymbolTable::totalSize() const
{ {
size_t n = 0; size_t n = 0;
for (auto & i : store) dump([&] (const Symbol & s) { n += std::string_view(s).size(); });
n += i.size();
return n; return n;
} }

View file

@ -125,15 +125,15 @@ struct StaticEnv;
/* An attribute path is a sequence of attribute names. */ /* An attribute path is a sequence of attribute names. */
struct AttrName struct AttrName
{ {
Symbol symbol; SymbolIdx symbol;
Expr * expr; Expr * expr;
AttrName(const Symbol & s) : symbol(s) {}; AttrName(const SymbolIdx & s) : symbol(s) {};
AttrName(Expr * e) : expr(e) {}; AttrName(Expr * e) : expr(e) {};
}; };
typedef std::vector<AttrName> AttrPath; typedef std::vector<AttrName> AttrPath;
std::string showAttrPath(const AttrPath & attrPath); std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
/* Abstract syntax of Nix expressions. */ /* Abstract syntax of Nix expressions. */
@ -141,19 +141,17 @@ std::string showAttrPath(const AttrPath & attrPath);
struct Expr struct Expr
{ {
virtual ~Expr() { }; virtual ~Expr() { };
virtual void show(std::ostream & str) const; virtual void show(const SymbolTable & symbols, std::ostream & str) const;
virtual void bindVars(const PosTable & pt, const StaticEnv & env); virtual void bindVars(const EvalState & es, const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v); virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol & name); virtual void setName(SymbolIdx name);
}; };
std::ostream & operator << (std::ostream & str, const Expr & e);
#define COMMON_METHODS \ #define COMMON_METHODS \
void show(std::ostream & str) const; \ void show(const SymbolTable & symbols, std::ostream & str) const; \
void eval(EvalState & state, Env & env, Value & v); \ void eval(EvalState & state, Env & env, Value & v); \
void bindVars(const PosTable & pt, const StaticEnv & env); void bindVars(const EvalState & es, const StaticEnv & env);
struct ExprInt : Expr struct ExprInt : Expr
{ {
@ -197,7 +195,7 @@ typedef uint32_t Displacement;
struct ExprVar : Expr struct ExprVar : Expr
{ {
PosIdx pos; PosIdx pos;
Symbol name; SymbolIdx name;
/* Whether the variable comes from an environment (e.g. a rec, let /* Whether the variable comes from an environment (e.g. a rec, let
or function argument) or from a "with". */ or function argument) or from a "with". */
@ -212,8 +210,8 @@ struct ExprVar : Expr
Level level; Level level;
Displacement displ; Displacement displ;
ExprVar(const Symbol & name) : name(name) { }; ExprVar(const SymbolIdx & name) : name(name) { };
ExprVar(const PosIdx & pos, const Symbol & name) : pos(pos), name(name) { }; ExprVar(const PosIdx & pos, const SymbolIdx & name) : pos(pos), name(name) { };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@ -224,7 +222,7 @@ struct ExprSelect : Expr
Expr * e, * def; Expr * e, * def;
AttrPath attrPath; AttrPath attrPath;
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { }; ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
ExprSelect(const PosIdx & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; ExprSelect(const PosIdx & pos, Expr * e, const SymbolIdx & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
COMMON_METHODS COMMON_METHODS
}; };
@ -249,7 +247,7 @@ struct ExprAttrs : Expr
: inherited(inherited), e(e), pos(pos) { }; : inherited(inherited), e(e), pos(pos) { };
AttrDef() { }; AttrDef() { };
}; };
typedef std::map<Symbol, AttrDef> AttrDefs; typedef std::map<SymbolIdx, AttrDef> AttrDefs;
AttrDefs attrs; AttrDefs attrs;
struct DynamicAttrDef { struct DynamicAttrDef {
Expr * nameExpr, * valueExpr; Expr * nameExpr, * valueExpr;
@ -274,9 +272,8 @@ struct ExprList : Expr
struct Formal struct Formal
{ {
PosIdx pos; PosIdx pos;
Symbol name; SymbolIdx name;
Expr * def; Expr * def;
Formal(const PosIdx & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { };
}; };
struct Formals struct Formals
@ -285,18 +282,19 @@ struct Formals
Formals_ formals; Formals_ formals;
bool ellipsis; bool ellipsis;
bool has(Symbol arg) const { bool has(SymbolIdx arg) const {
auto it = std::lower_bound(formals.begin(), formals.end(), arg, auto it = std::lower_bound(formals.begin(), formals.end(), arg,
[] (const Formal & f, const Symbol & sym) { return f.name < sym; }); [] (const Formal & f, const SymbolIdx & sym) { return f.name < sym; });
return it != formals.end() && it->name == arg; return it != formals.end() && it->name == arg;
} }
std::vector<Formal> lexicographicOrder() const std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
{ {
std::vector<Formal> result(formals.begin(), formals.end()); std::vector<Formal> result(formals.begin(), formals.end());
std::sort(result.begin(), result.end(), std::sort(result.begin(), result.end(),
[] (const Formal & a, const Formal & b) { [&] (const Formal & a, const Formal & b) {
return std::string_view(a.name) < std::string_view(b.name); std::string_view sa = symbols[a.name], sb = symbols[b.name];
return sa < sb;
}); });
return result; return result;
} }
@ -305,16 +303,20 @@ struct Formals
struct ExprLambda : Expr struct ExprLambda : Expr
{ {
PosIdx pos; PosIdx pos;
Symbol name; SymbolIdx name;
Symbol arg; SymbolIdx arg;
Formals * formals; Formals * formals;
Expr * body; Expr * body;
ExprLambda(const PosIdx & pos, const Symbol & arg, Formals * formals, Expr * body) ExprLambda(PosIdx pos, SymbolIdx arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body) : pos(pos), arg(arg), formals(formals), body(body)
{ {
}; };
void setName(Symbol & name); ExprLambda(PosIdx pos, Formals * formals, Expr * body)
std::string showNamePos(const PosTable & pt) const; : pos(pos), formals(formals), body(body)
{
}
void setName(SymbolIdx name);
std::string showNamePos(const EvalState & state) const;
inline bool hasFormals() const { return formals != nullptr; } inline bool hasFormals() const { return formals != nullptr; }
COMMON_METHODS COMMON_METHODS
}; };
@ -377,13 +379,13 @@ struct ExprOpNot : Expr
Expr * e1, * e2; \ Expr * e1, * e2; \
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \ name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \ name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
void show(std::ostream & str) const \ void show(const SymbolTable & symbols, std::ostream & str) const \
{ \ { \
str << "(" << *e1 << " " s " " << *e2 << ")"; \ str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
} \ } \
void bindVars(const PosTable & pt, const StaticEnv & env) \ void bindVars(const EvalState & es, const StaticEnv & env) \
{ \ { \
e1->bindVars(pt, env); e2->bindVars(pt, env); \ e1->bindVars(es, env); e2->bindVars(es, env); \
} \ } \
void eval(EvalState & state, Env & env, Value & v); \ void eval(EvalState & state, Env & env, Value & v); \
}; };
@ -423,7 +425,7 @@ struct StaticEnv
const StaticEnv * up; const StaticEnv * up;
// Note: these must be in sorted order. // Note: these must be in sorted order.
typedef std::vector<std::pair<Symbol, Displacement>> Vars; typedef std::vector<std::pair<SymbolIdx, Displacement>> Vars;
Vars vars; Vars vars;
StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) { StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
@ -447,7 +449,7 @@ struct StaticEnv
vars.erase(it, end); vars.erase(it, end);
} }
Vars::const_iterator find(const Symbol & name) const Vars::const_iterator find(const SymbolIdx & name) const
{ {
Vars::value_type key(name, 0); Vars::value_type key(name, 0);
auto i = std::lower_bound(vars.begin(), vars.end(), key); auto i = std::lower_bound(vars.begin(), vars.end(), key);

View file

@ -77,26 +77,26 @@ using namespace nix;
namespace nix { namespace nix {
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos) static void dupAttr(const EvalState & state, const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{ {
throw ParseError({ throw ParseError({
.msg = hintfmt("attribute '%1%' already defined at %2%", .msg = hintfmt("attribute '%1%' already defined at %2%",
showAttrPath(attrPath), prevPos), showAttrPath(state.symbols, attrPath), state.positions[prevPos]),
.errPos = pos .errPos = state.positions[pos]
}); });
} }
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos) static void dupAttr(const EvalState & state, SymbolIdx attr, const PosIdx pos, const PosIdx prevPos)
{ {
throw ParseError({ throw ParseError({
.msg = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos), .msg = hintfmt("attribute '%1%' already defined at %2%", state.symbols[attr], state.positions[prevPos]),
.errPos = pos .errPos = state.positions[pos]
}); });
} }
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const PosIdx pos, const nix::PosTable & pt) Expr * e, const PosIdx pos, const nix::EvalState & state)
{ {
AttrPath::iterator i; AttrPath::iterator i;
// All attrpaths have at least one attr // All attrpaths have at least one attr
@ -104,15 +104,15 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
// Checking attrPath validity. // Checking attrPath validity.
// =========================== // ===========================
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) { for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
if (i->symbol.set()) { if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
if (!j->second.inherited) { if (!j->second.inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2) dupAttr(attrPath, pt[pos], pt[j->second.pos]); if (!attrs2) dupAttr(state, attrPath, pos, j->second.pos);
attrs = attrs2; attrs = attrs2;
} else } else
dupAttr(attrPath, pt[pos], pt[j->second.pos]); dupAttr(state, attrPath, pos, j->second.pos);
} else { } else {
ExprAttrs * nested = new ExprAttrs; ExprAttrs * nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
@ -126,7 +126,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
} }
// Expr insertion. // Expr insertion.
// ========================== // ==========================
if (i->symbol.set()) { if (i->symbol) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
// This attr path is already defined. However, if both // This attr path is already defined. However, if both
@ -139,11 +139,11 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
for (auto & ad : ae->attrs) { for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first); auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
dupAttr(ad.first, pt[j2->second.pos], pt[ad.second.pos]); dupAttr(state, ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second); jAttrs->attrs.emplace(ad.first, ad.second);
} }
} else { } else {
dupAttr(attrPath, pt[pos], pt[j->second.pos]); dupAttr(state, attrPath, pos, j->second.pos);
} }
} else { } else {
// This attr path is not defined. Let's create it. // This attr path is not defined. Let's create it.
@ -157,14 +157,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
static Formals * toFormals(ParseData & data, ParserFormals * formals, static Formals * toFormals(ParseData & data, ParserFormals * formals,
PosIdx pos = noPos, Symbol arg = {}) PosIdx pos = noPos, SymbolIdx arg = {})
{ {
std::sort(formals->formals.begin(), formals->formals.end(), std::sort(formals->formals.begin(), formals->formals.end(),
[] (const auto & a, const auto & b) { [] (const auto & a, const auto & b) {
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos); return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
}); });
std::optional<std::pair<Symbol, PosIdx>> duplicate; std::optional<std::pair<SymbolIdx, PosIdx>> duplicate;
for (size_t i = 0; i + 1 < formals->formals.size(); i++) { for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
if (formals->formals[i].name != formals->formals[i + 1].name) if (formals->formals[i].name != formals->formals[i + 1].name)
continue; continue;
@ -173,7 +173,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
} }
if (duplicate) if (duplicate)
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first), .msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[duplicate->first]),
.errPos = data.state.positions[duplicate->second] .errPos = data.state.positions[duplicate->second]
}); });
@ -181,9 +181,9 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
result.ellipsis = formals->ellipsis; result.ellipsis = formals->ellipsis;
result.formals = std::move(formals->formals); result.formals = std::move(formals->formals);
if (arg.set() && result.has(arg)) if (arg && result.has(arg))
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", arg), .msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[arg]),
.errPos = data.state.positions[pos] .errPos = data.state.positions[pos]
}); });
@ -369,15 +369,15 @@ expr_function
: ID ':' expr_function : ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); } { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function | '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, {}, toFormals(*data, $2), $5); } { $$ = new ExprLambda(CUR_POS, toFormals(*data, $2), $5); }
| '{' formals '}' '@' ID ':' expr_function | '{' formals '}' '@' ID ':' expr_function
{ {
Symbol arg = data->symbols.create($5); auto arg = data->symbols.create($5);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7); $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
} }
| ID '@' '{' formals '}' ':' expr_function | ID '@' '{' formals '}' ':' expr_function
{ {
Symbol arg = data->symbols.create($1); auto arg = data->symbols.create($1);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7); $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
} }
| ASSERT expr ';' expr_function | ASSERT expr ';' expr_function
@ -532,13 +532,12 @@ ind_string_parts
; ;
binds binds
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state.positions); } : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
| binds INHERIT attrs ';' | binds INHERIT attrs ';'
{ $$ = $1; { $$ = $1;
for (auto & i : *$3) { for (auto & i : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end()) if ($$->attrs.find(i.symbol) != $$->attrs.end())
dupAttr(i.symbol, data->state.positions[makeCurPos(@3, data)], dupAttr(data->state, i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
data->state.positions[$$->attrs[i.symbol].pos]);
auto pos = makeCurPos(@3, data); auto pos = makeCurPos(@3, data);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true)); $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
} }
@ -548,8 +547,7 @@ binds
/* !!! Should ensure sharing of the expression in $4. */ /* !!! Should ensure sharing of the expression in $4. */
for (auto & i : *$6) { for (auto & i : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end()) if ($$->attrs.find(i.symbol) != $$->attrs.end())
dupAttr(i.symbol, data->state.positions[makeCurPos(@6, data)], dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
data->state.positions[$$->attrs[i.symbol].pos]);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data))); $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
} }
} }
@ -623,8 +621,8 @@ formals
; ;
formal formal
: ID { $$ = new Formal(CUR_POS, data->symbols.create($1), 0); } : ID { $$ = new Formal{CUR_POS, data->symbols.create($1), 0}; }
| ID '?' expr { $$ = new Formal(CUR_POS, data->symbols.create($1), $3); } | ID '?' expr { $$ = new Formal{CUR_POS, data->symbols.create($1), $3}; }
; ;
%% %%
@ -670,7 +668,7 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
if (res) throw ParseError(data.error.value()); if (res) throw ParseError(data.error.value());
data.result->bindVars(positions, staticEnv); data.result->bindVars(*this, staticEnv);
return data.result; return data.result;
} }

View file

@ -403,7 +403,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val
case nFloat: t = "float"; break; case nFloat: t = "float"; break;
case nThunk: abort(); case nThunk: abort();
} }
v.mkString(state.symbols.create(t)); v.mkString(t);
} }
static RegisterPrimOp primop_typeOf({ static RegisterPrimOp primop_typeOf({
@ -584,7 +584,7 @@ typedef std::list<Value *> ValueList;
static Bindings::iterator getAttr( static Bindings::iterator getAttr(
EvalState & state, EvalState & state,
std::string_view funcName, std::string_view funcName,
Symbol attrSym, SymbolIdx attrSym,
Bindings * attrSet, Bindings * attrSet,
const PosIdx pos) const PosIdx pos)
{ {
@ -592,7 +592,7 @@ static Bindings::iterator getAttr(
if (value == attrSet->end()) { if (value == attrSet->end()) {
hintformat errorMsg = hintfmt( hintformat errorMsg = hintfmt(
"attribute '%s' missing for call to '%s'", "attribute '%s' missing for call to '%s'",
attrSym, state.symbols[attrSym],
funcName funcName
); );
@ -919,7 +919,7 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu
if (args[0]->type() == nString) if (args[0]->type() == nString)
printError("trace: %1%", args[0]->string.s); printError("trace: %1%", args[0]->string.s);
else else
printError("trace: %1%", *args[0]); printError("trace: %1%", printValue(state, *args[0]));
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
} }
@ -998,9 +998,9 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
StringSet outputs; StringSet outputs;
outputs.insert("out"); outputs.insert("out");
for (auto & i : args[0]->attrs->lexicographicOrder()) { for (auto & i : args[0]->attrs->lexicographicOrder(state.symbols)) {
if (i->name == state.sIgnoreNulls) continue; if (i->name == state.sIgnoreNulls) continue;
const std::string & key = i->name; const std::string & key = state.symbols[i->name];
vomit("processing attribute '%1%'", key); vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string_view s) { auto handleHashMode = [&](const std::string_view s) {
@ -2046,7 +2046,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
PathSet context; PathSet context;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
auto & n(attr.name); auto & n(state.symbols[attr.name]);
if (n == "path") if (n == "path")
path = state.coerceToPath(attr.pos, *attr.value, context); path = state.coerceToPath(attr.pos, *attr.value, context);
else if (attr.name == state.sName) else if (attr.name == state.sName)
@ -2060,7 +2060,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256); expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), .msg = hintfmt("unsupported argument '%1%' to 'addPath'", state.symbols[attr.name]),
.errPos = state.positions[attr.pos] .errPos = state.positions[attr.pos]
}); });
} }
@ -2126,7 +2126,7 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args,
size_t n = 0; size_t n = 0;
for (auto & i : *args[0]->attrs) for (auto & i : *args[0]->attrs)
(v.listElems()[n++] = state.allocValue())->mkString(i.name); (v.listElems()[n++] = state.allocValue())->mkString(state.symbols[i.name]);
std::sort(v.listElems(), v.listElems() + n, std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; }); [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
@ -2156,8 +2156,9 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args,
v.listElems()[n++] = (Value *) &i; v.listElems()[n++] = (Value *) &i;
std::sort(v.listElems(), v.listElems() + n, std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { [&](Value * v1, Value * v2) {
std::string_view s1 = ((Attr *) v1)->name, s2 = ((Attr *) v2)->name; std::string_view s1 = state.symbols[((Attr *) v1)->name],
s2 = state.symbols[((Attr *) v2)->name];
return s1 < s2; return s1 < s2;
}); });
@ -2312,7 +2313,7 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args
auto attrs = state.buildBindings(args[0]->listSize()); auto attrs = state.buildBindings(args[0]->listSize());
std::set<Symbol> seen; std::set<SymbolIdx> seen;
for (auto v2 : args[0]->listItems()) { for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos); state.forceAttrs(*v2, pos);
@ -2327,7 +2328,7 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args
auto name = state.forceStringNoCtx(*j->value, j->pos); auto name = state.forceStringNoCtx(*j->value, j->pos);
Symbol sym = state.symbols.create(name); auto sym = state.symbols.create(name);
if (seen.insert(sym).second) { if (seen.insert(sym).second) {
Bindings::iterator j2 = getAttr( Bindings::iterator j2 = getAttr(
state, state,
@ -2396,7 +2397,7 @@ static RegisterPrimOp primop_intersectAttrs({
static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos)); auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
Value * res[args[1]->listSize()]; Value * res[args[1]->listSize()];
@ -2483,7 +2484,7 @@ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, V
for (auto & i : *args[1]->attrs) { for (auto & i : *args[1]->attrs) {
Value * vName = state.allocValue(); Value * vName = state.allocValue();
Value * vFun2 = state.allocValue(); Value * vFun2 = state.allocValue();
vName->mkString(i.name); vName->mkString(state.symbols[i.name]);
vFun2->mkApp(args[0], vName); vFun2->mkApp(args[0], vName);
attrs.alloc(i.name).mkApp(vFun2, i.value); attrs.alloc(i.name).mkApp(vFun2, i.value);
} }
@ -2515,7 +2516,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
// attribute with the merge function application. this way we need not // attribute with the merge function application. this way we need not
// use (slightly slower) temporary storage the GC does not know about. // use (slightly slower) temporary storage the GC does not know about.
std::map<Symbol, std::pair<size_t, Value * *>> attrsSeen; std::map<SymbolIdx, std::pair<size_t, Value * *>> attrsSeen;
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -2550,7 +2551,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs) {
auto name = state.allocValue(); auto name = state.allocValue();
name->mkString(attr.name); name->mkString(state.symbols[attr.name]);
auto call1 = state.allocValue(); auto call1 = state.allocValue();
call1->mkApp(args[0], name); call1->mkApp(args[0], name);
auto call2 = state.allocValue(); auto call2 = state.allocValue();
@ -3058,7 +3059,7 @@ static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Va
Value res; Value res;
state.callFunction(*args[0], *vElem, res, pos); state.callFunction(*args[0], *vElem, res, pos);
auto name = state.forceStringNoCtx(res, pos); auto name = state.forceStringNoCtx(res, pos);
Symbol sym = state.symbols.create(name); auto sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first; auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem); vector->second.push_back(vElem);
} }
@ -3932,7 +3933,7 @@ void EvalState::createBaseEnv()
// the parser needs two NUL bytes as terminators; one of them // the parser needs two NUL bytes as terminators; one of them
// is implied by being a C string. // is implied by being a C string.
"\0"; "\0";
eval(parse(code, sizeof(code), foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation); eval(parse(code, sizeof(code), foFile, derivationNixPath, "/", staticBaseEnv), *vDerivation);
} }

View file

@ -144,45 +144,46 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
auto sPath = state.symbols.create("path"); auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs"); auto sAllOutputs = state.symbols.create("allOutputs");
for (auto & i : *args[1]->attrs) { for (auto & i : *args[1]->attrs) {
if (!state.store->isStorePath(i.name)) const auto & name = state.symbols[i.name];
if (!state.store->isStorePath(name))
throw EvalError({ throw EvalError({
.msg = hintfmt("Context key '%s' is not a store path", i.name), .msg = hintfmt("Context key '%s' is not a store path", name),
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(i.name)); state.store->ensurePath(state.store->parseStorePath(name));
state.forceAttrs(*i.value, i.pos); state.forceAttrs(*i.value, i.pos);
auto iter = i.value->attrs->find(sPath); auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, iter->pos)) if (state.forceBool(*iter->value, iter->pos))
context.insert(i.name); context.emplace(name);
} }
iter = i.value->attrs->find(sAllOutputs); iter = i.value->attrs->find(sAllOutputs);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, iter->pos)) { if (state.forceBool(*iter->value, iter->pos)) {
if (!isDerivation(i.name)) { if (!isDerivation(name)) {
throw EvalError({ throw EvalError({
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name),
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
} }
context.insert("=" + std::string(i.name)); context.insert(concatStrings("=", name));
} }
} }
iter = i.value->attrs->find(state.sOutputs); iter = i.value->attrs->find(state.sOutputs);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, iter->pos); state.forceList(*iter->value, iter->pos);
if (iter->value->listSize() && !isDerivation(i.name)) { if (iter->value->listSize() && !isDerivation(name)) {
throw EvalError({ throw EvalError({
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name),
.errPos = state.positions[i.pos] .errPos = state.positions[i.pos]
}); });
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, iter->pos); auto outputName = state.forceStringNoCtx(*elem, iter->pos);
context.insert(concatStrings("!", name, "!", i.name)); context.insert(concatStrings("!", outputName, "!", name));
} }
} }
} }

View file

@ -15,12 +15,14 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
std::optional<StorePath> toPath; std::optional<StorePath> toPath;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
if (attr.name == "fromPath") { const auto & attrName = state.symbols[attr.name];
if (attrName == "fromPath") {
PathSet context; PathSet context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context); fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
} }
else if (attr.name == "toPath") { else if (attrName == "toPath") {
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
toCA = true; toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) { if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
@ -29,12 +31,12 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
} }
} }
else if (attr.name == "fromStore") else if (attrName == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos); fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
else else
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name), .msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
.errPos = state.positions[pos] .errPos = state.positions[pos]
}); });
} }

View file

@ -22,7 +22,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
std::string_view n(attr.name); std::string_view n(state.symbols[attr.name]);
if (n == "url") if (n == "url")
url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned(); url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
else if (n == "rev") { else if (n == "rev") {
@ -38,7 +38,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
name = state.forceStringNoCtx(*attr.value, attr.pos); name = state.forceStringNoCtx(*attr.value, attr.pos);
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
.errPos = state.positions[attr.pos] .errPos = state.positions[attr.pos]
}); });
} }

View file

@ -126,20 +126,20 @@ static void fetchTree(
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned(); auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
attrs.emplace(attr.name, attrs.emplace(state.symbols[attr.name],
attr.name == "url" state.symbols[attr.name] == "url"
? type == "git" ? type == "git"
? fixURIForGit(s, state) ? fixURIForGit(s, state)
: fixURI(s, state) : fixURI(s, state)
: s); : s);
} }
else if (attr.value->type() == nBool) else if (attr.value->type() == nBool)
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean}); attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean});
else if (attr.value->type() == nInt) else if (attr.value->type() == nInt)
attrs.emplace(attr.name, uint64_t(attr.value->integer)); attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
else else
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected", throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
attr.name, showType(*attr.value)); state.symbols[attr.name], showType(*attr.value));
} }
if (!params.allowNameArgument) if (!params.allowNameArgument)
@ -198,7 +198,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
std::string n(attr.name); std::string_view n(state.symbols[attr.name]);
if (n == "url") if (n == "url")
url = state.forceStringNoCtx(*attr.value, attr.pos); url = state.forceStringNoCtx(*attr.value, attr.pos);
else if (n == "sha256") else if (n == "sha256")
@ -207,7 +207,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
name = state.forceStringNoCtx(*attr.value, attr.pos); name = state.forceStringNoCtx(*attr.value, attr.pos);
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), .msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
.errPos = state.positions[attr.pos] .errPos = state.positions[attr.pos]
}); });
} }

View file

@ -16,46 +16,25 @@ namespace nix {
class Symbol class Symbol
{ {
private:
const std::string * s; // pointer into SymbolTable
Symbol(const std::string * s) : s(s) { };
friend class SymbolTable; friend class SymbolTable;
private:
std::string s;
public: public:
Symbol() : s(0) { }; Symbol(std::string_view s) : s(s) { }
bool operator == (const Symbol & s2) const
{
return s == s2.s;
}
// FIXME: remove // FIXME: remove
bool operator == (std::string_view s2) const bool operator == (std::string_view s2) const
{ {
return s->compare(s2) == 0; return s == s2;
}
bool operator != (const Symbol & s2) const
{
return s != s2.s;
}
bool operator < (const Symbol & s2) const
{
return s < s2.s;
} }
operator const std::string & () const operator const std::string & () const
{ {
return *s; return s;
} }
operator const std::string_view () const operator const std::string_view () const
{
return *s;
}
bool set() const
{ {
return s; return s;
} }
@ -63,38 +42,64 @@ public:
friend std::ostream & operator << (std::ostream & str, const Symbol & sym); friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
}; };
class SymbolIdx
{
friend class SymbolTable;
private:
uint32_t id;
explicit SymbolIdx(uint32_t id): id(id) {}
public:
SymbolIdx() : id(0) {}
explicit operator bool() const { return id > 0; }
bool operator<(const SymbolIdx other) const { return id < other.id; }
bool operator==(const SymbolIdx other) const { return id == other.id; }
bool operator!=(const SymbolIdx other) const { return id != other.id; }
};
class SymbolTable class SymbolTable
{ {
private: private:
std::unordered_map<std::string_view, Symbol> symbols; std::unordered_map<std::string_view, std::pair<const Symbol *, uint32_t>> symbols;
std::list<std::string> store; ChunkedVector<Symbol, 8192> store{16};
public: public:
Symbol create(std::string_view s) SymbolIdx create(std::string_view s)
{ {
// Most symbols are looked up more than once, so we trade off insertion performance // Most symbols are looked up more than once, so we trade off insertion performance
// for lookup performance. // for lookup performance.
// TODO: could probably be done more efficiently with transparent Hash and Equals // TODO: could probably be done more efficiently with transparent Hash and Equals
// on the original implementation using unordered_set // on the original implementation using unordered_set
auto it = symbols.find(s); auto it = symbols.find(s);
if (it != symbols.end()) return it->second; if (it != symbols.end()) return SymbolIdx(it->second.second + 1);
auto & rawSym = store.emplace_back(s); const auto & [rawSym, idx] = store.add(s);
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second; symbols.emplace(rawSym, std::make_pair(&rawSym, idx));
return SymbolIdx(idx + 1);
}
const Symbol & operator[](SymbolIdx s) const
{
if (s.id == 0 || s.id > store.size())
abort();
return store[s.id - 1];
} }
size_t size() const size_t size() const
{ {
return symbols.size(); return store.size();
} }
size_t totalSize() const; size_t totalSize() const;
template<typename T> template<typename T>
void dump(T callback) void dump(T callback) const
{ {
for (auto & s : store) store.forEach(callback);
callback(s);
} }
}; };

View file

@ -50,7 +50,7 @@ void printValueAsJSON(EvalState & state, bool strict,
auto obj(out.object()); auto obj(out.object());
StringSet names; StringSet names;
for (auto & j : *v.attrs) for (auto & j : *v.attrs)
names.insert(j.name); names.emplace(state.symbols[j.name]);
for (auto & j : names) { for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j))); Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j)); auto placeholder(obj.placeholder(j));

View file

@ -22,7 +22,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
const PosIdx pos); const PosIdx pos);
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos) static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
{ {
xmlAttrs["path"] = pos.file; xmlAttrs["path"] = pos.file;
xmlAttrs["line"] = (format("%1%") % pos.line).str(); xmlAttrs["line"] = (format("%1%") % pos.line).str();
@ -36,14 +36,14 @@ static void showAttrs(EvalState & state, bool strict, bool location,
StringSet names; StringSet names;
for (auto & i : attrs) for (auto & i : attrs)
names.insert(i.name); names.emplace(state.symbols[i.name]);
for (auto & i : names) { for (auto & i : names) {
Attr & a(*attrs.find(state.symbols.create(i))); Attr & a(*attrs.find(state.symbols.create(i)));
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
xmlAttrs["name"] = i; xmlAttrs["name"] = i;
if (location && a.pos) posToXML(xmlAttrs, state.positions[a.pos]); if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]);
XMLOpenElement _(doc, "attr", xmlAttrs); XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location, printValueAsXML(state, strict, location,
@ -134,18 +134,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
} }
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, state.positions[v.lambda.fun->pos]); if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]);
XMLOpenElement _(doc, "function", xmlAttrs); XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->hasFormals()) { if (v.lambda.fun->hasFormals()) {
XMLAttrs attrs; XMLAttrs attrs;
if (v.lambda.fun->arg.set()) attrs["name"] = v.lambda.fun->arg; if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs); XMLOpenElement _(doc, "attrspat", attrs);
for (auto & i : v.lambda.fun->formals->lexicographicOrder()) for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
doc.writeEmptyElement("attr", singletonAttrs("name", i.name)); doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
} else } else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg)); doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
break; break;
} }

View file

@ -55,7 +55,7 @@ struct Env;
struct Expr; struct Expr;
struct ExprLambda; struct ExprLambda;
struct PrimOp; struct PrimOp;
class Symbol; class SymbolIdx;
class PosIdx; class PosIdx;
struct Pos; struct Pos;
class StorePath; class StorePath;
@ -121,11 +121,11 @@ private:
friend std::string showType(const Value & v); friend std::string showType(const Value & v);
void print(std::ostream & str, std::set<const void *> * seen) const; void print(const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen) const;
public: public:
void print(std::ostream & str, bool showRepeated = false) const; void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const;
// Functions needed to distinguish the type // Functions needed to distinguish the type
// These should be removed eventually, by putting the functionality that's // These should be removed eventually, by putting the functionality that's
@ -253,7 +253,7 @@ public:
inline void mkString(const Symbol & s) inline void mkString(const Symbol & s)
{ {
mkString(((const std::string &) s).c_str()); mkString(std::string_view(s).data());
} }
inline void mkPath(const char * s) inline void mkPath(const char * s)
@ -410,12 +410,12 @@ public:
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector; typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap; typedef std::map<SymbolIdx, Value *, std::less<SymbolIdx>, traceable_allocator<std::pair<const SymbolIdx, Value *> > > ValueMap;
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap; typedef std::map<SymbolIdx, ValueVector, std::less<SymbolIdx>, traceable_allocator<std::pair<const SymbolIdx, ValueVector> > > ValueVectorMap;
#else #else
typedef std::vector<Value *> ValueVector; typedef std::vector<Value *> ValueVector;
typedef std::map<Symbol, Value *> ValueMap; typedef std::map<SymbolIdx, Value *> ValueMap;
typedef std::map<Symbol, ValueVector> ValueVectorMap; typedef std::map<SymbolIdx, ValueVector> ValueVectorMap;
#endif #endif

View file

@ -152,6 +152,14 @@ public:
{ {
return chunks[idx / ChunkSize][idx % ChunkSize]; return chunks[idx / ChunkSize][idx % ChunkSize];
} }
template<typename Fn>
void forEach(Fn fn) const
{
for (const auto & c : chunks)
for (const auto & e : c)
fn(e);
}
}; };
} }

View file

@ -1241,7 +1241,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
Attr & a(*attrs.find(i.name)); Attr & a(*attrs.find(i.name));
if(a.value->type() != nString) continue; if(a.value->type() != nString) continue;
XMLAttrs attrs3; XMLAttrs attrs3;
attrs3["type"] = i.name; attrs3["type"] = globals.state->symbols[i.name];
attrs3["value"] = a.value->string.s; attrs3["value"] = a.value->string.s;
xml.writeEmptyElement("string", attrs3); xml.writeEmptyElement("string", attrs3);
} }

View file

@ -106,7 +106,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
the store; we need it for future modifications of the the store; we need it for future modifications of the
environment. */ environment. */
std::ostringstream str; std::ostringstream str;
manifest.print(str, true); manifest.print(state.symbols, str, true);
auto manifestFile = state.store->addTextToStore("env-manifest.nix", auto manifestFile = state.store->addTextToStore("env-manifest.nix",
str.str(), references); str.str(), references);

View file

@ -31,7 +31,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
bool evalOnly, OutputKind output, bool location, Expr * e) bool evalOnly, OutputKind output, bool location, Expr * e)
{ {
if (parseOnly) { if (parseOnly) {
std::cout << format("%1%\n") % *e; e->show(state.symbols, std::cout);
std::cout << "\n";
return; return;
} }
@ -55,7 +56,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context); printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
else { else {
if (strict) state.forceValueDeep(vRes); if (strict) state.forceValueDeep(vRes);
std::cout << vRes << std::endl; vRes.print(state.symbols, std::cout);
std::cout << std::endl;
} }
} else { } else {
DrvInfos drvs; DrvInfos drvs;

View file

@ -85,9 +85,9 @@ UnresolvedApp Installable::toApp(EvalState & state)
else if (type == "derivation") { else if (type == "derivation") {
auto drvPath = cursor->forceDerivation(); auto drvPath = cursor->forceDerivation();
auto outPath = cursor->getAttr(state.sOutPath)->getString(); auto outPath = cursor->getAttr("outPath")->getString();
auto outputName = cursor->getAttr(state.sOutputName)->getString(); auto outputName = cursor->getAttr("outputName")->getString();
auto name = cursor->getAttr(state.sName)->getString(); auto name = cursor->getAttr("name")->getString();
auto aPname = cursor->maybeGetAttr("pname"); auto aPname = cursor->maybeGetAttr("pname");
auto aMeta = cursor->maybeGetAttr("meta"); auto aMeta = cursor->maybeGetAttr("meta");
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr; auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;

View file

@ -88,18 +88,20 @@ struct CmdEval : MixJSON, InstallableCommand
else if (v.type() == nAttrs) { else if (v.type() == nAttrs) {
if (mkdir(path.c_str(), 0777) == -1) if (mkdir(path.c_str(), 0777) == -1)
throw SysError("creating directory '%s'", path); throw SysError("creating directory '%s'", path);
for (auto & attr : *v.attrs) for (auto & attr : *v.attrs) {
std::string_view name = state->symbols[attr.name];
try { try {
if (attr.name == "." || attr.name == "..") if (name == "." || name == "..")
throw Error("invalid file name '%s'", attr.name); throw Error("invalid file name '%s'", name);
recurse(*attr.value, attr.pos, path + "/" + std::string(attr.name)); recurse(*attr.value, attr.pos, concatStrings(path, "/", name));
} catch (Error & e) { } catch (Error & e) {
e.addTrace( e.addTrace(
state->positions[attr.pos], state->positions[attr.pos],
hintfmt("while evaluating the attribute '%s'", attr.name)); hintfmt("while evaluating the attribute '%s'", name));
throw; throw;
} }
} }
}
else else
throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]); throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]);
}; };
@ -119,7 +121,7 @@ struct CmdEval : MixJSON, InstallableCommand
else { else {
state->forceValueDeep(*v); state->forceValueDeep(*v);
logger->cout("%s", *v); logger->cout("%s", printValue(*state, *v));
} }
} }
}; };

View file

@ -139,11 +139,11 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
else. This way we can disable IFD for hydraJobs and then enable else. This way we can disable IFD for hydraJobs and then enable
it for other outputs. */ it for other outputs. */
if (auto attr = aOutputs->value->attrs->get(sHydraJobs)) if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
callback(attr->name, *attr->value, attr->pos); callback(state.symbols[attr->name], *attr->value, attr->pos);
for (auto & attr : *aOutputs->value->attrs) { for (auto & attr : *aOutputs->value->attrs) {
if (attr.name != sHydraJobs) if (attr.name != sHydraJobs)
callback(attr.name, *attr.value, attr.pos); callback(state.symbols[attr.name], *attr.value, attr.pos);
} }
} }
@ -254,14 +254,6 @@ struct CmdFlakeInfo : CmdFlakeMetadata
} }
}; };
static bool argHasName(std::string_view arg, std::string_view expected)
{
return
arg == expected
|| arg == "_"
|| (hasPrefix(arg, "_") && arg.substr(1) == expected);
}
struct CmdFlakeCheck : FlakeCommand struct CmdFlakeCheck : FlakeCommand
{ {
bool build = true; bool build = true;
@ -319,6 +311,14 @@ struct CmdFlakeCheck : FlakeCommand
return state->positions[p]; return state->positions[p];
}; };
auto argHasName = [&] (SymbolIdx arg, std::string_view expected) {
std::string_view name = state->symbols[arg];
return
name == expected
|| name == "_"
|| (hasPrefix(name, "_") && name.substr(1) == expected);
};
auto checkSystemName = [&](const std::string & system, const PosIdx pos) { auto checkSystemName = [&](const std::string & system, const PosIdx pos) {
// FIXME: what's the format of "system"? // FIXME: what's the format of "system"?
if (system.find('-') == std::string::npos) if (system.find('-') == std::string::npos)
@ -390,7 +390,7 @@ struct CmdFlakeCheck : FlakeCommand
} catch (Error & e) { } catch (Error & e) {
e.addTrace( e.addTrace(
state->positions[attr.pos], state->positions[attr.pos],
hintfmt("while evaluating the option '%s'", attr.name)); hintfmt("while evaluating the option '%s'", state->symbols[attr.name]));
throw; throw;
} }
} else } else
@ -414,7 +414,7 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs) {
state->forceAttrs(*attr.value, attr.pos); state->forceAttrs(*attr.value, attr.pos);
auto attrPath2 = attrPath + "." + (std::string) attr.name; auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
if (state->isDerivation(*attr.value)) { if (state->isDerivation(*attr.value)) {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking Hydra job '%s'", attrPath2)); fmt("checking Hydra job '%s'", attrPath2));
@ -468,7 +468,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("template '%s' lacks attribute 'description'", attrPath); throw Error("template '%s' lacks attribute 'description'", attrPath);
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs) {
std::string name(attr.name); std::string_view name(state->symbols[attr.name]);
if (name != "path" && name != "description" && name != "welcomeText") if (name != "path" && name != "description" && name != "welcomeText")
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
} }
@ -522,13 +522,14 @@ struct CmdFlakeCheck : FlakeCommand
if (name == "checks") { if (name == "checks") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) { for (auto & attr2 : *attr.value->attrs) {
auto drvPath = checkDerivation( auto drvPath = checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
if (drvPath && (std::string) attr.name == settings.thisSystem.get()) if (drvPath && attr_name == settings.thisSystem.get())
drvPaths.push_back(DerivedPath::Built{*drvPath}); drvPaths.push_back(DerivedPath::Built{*drvPath});
} }
} }
@ -537,9 +538,10 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "formatter") { else if (name == "formatter") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
checkApp( checkApp(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr_name),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
} }
@ -547,11 +549,12 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "packages" || name == "devShells") { else if (name == "packages" || name == "devShells") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) for (auto & attr2 : *attr.value->attrs)
checkDerivation( checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
} }
} }
@ -559,11 +562,12 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "apps") { else if (name == "apps") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) for (auto & attr2 : *attr.value->attrs)
checkApp( checkApp(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
} }
} }
@ -571,9 +575,10 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "defaultPackage" || name == "devShell") { else if (name == "defaultPackage" || name == "devShell") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
checkDerivation( checkDerivation(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr_name),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
} }
@ -581,9 +586,10 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "defaultApp") { else if (name == "defaultApp") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
checkApp( checkApp(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr_name),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
} }
@ -591,7 +597,7 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "legacyPackages") { else if (name == "legacyPackages") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); checkSystemName(state->symbols[attr.name], attr.pos);
// FIXME: do getDerivations? // FIXME: do getDerivations?
} }
} }
@ -602,7 +608,7 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "overlays") { else if (name == "overlays") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkOverlay(fmt("%s.%s", name, attr.name), checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
@ -612,14 +618,14 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "nixosModules") { else if (name == "nixosModules") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkModule(fmt("%s.%s", name, attr.name), checkModule(fmt("%s.%s", name, state->symbols[attr.name]),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
else if (name == "nixosConfigurations") { else if (name == "nixosConfigurations") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkNixOSConfiguration(fmt("%s.%s", name, attr.name), checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
@ -632,16 +638,17 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "templates") { else if (name == "templates") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkTemplate(fmt("%s.%s", name, attr.name), checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
else if (name == "defaultBundler") { else if (name == "defaultBundler") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
checkBundler( checkBundler(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr_name),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
} }
@ -649,11 +656,12 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "bundlers") { else if (name == "bundlers") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, attr.pos); const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) { for (auto & attr2 : *attr.value->attrs) {
checkBundler( checkBundler(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
} }
} }
@ -1000,7 +1008,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
auto showDerivation = [&]() auto showDerivation = [&]()
{ {
auto name = visitor.getAttr(state->sName)->getString(); auto name = visitor.getAttr("name")->getString();
if (json) { if (json) {
std::optional<std::string> description; std::optional<std::string> description;
if (auto aMeta = visitor.maybeGetAttr("meta")) { if (auto aMeta = visitor.maybeGetAttr("meta")) {

View file

@ -302,7 +302,7 @@ void mainWrapped(int argc, char * * argv)
b["arity"] = primOp->arity; b["arity"] = primOp->arity;
b["args"] = primOp->args; b["args"] = primOp->args;
b["doc"] = trim(stripIndentation(primOp->doc)); b["doc"] = trim(stripIndentation(primOp->doc));
res[(std::string) builtin.name] = std::move(b); res[state.symbols[builtin.name]] = std::move(b);
} }
std::cout << res.dump() << "\n"; std::cout << res.dump() << "\n";
return; return;

View file

@ -73,7 +73,7 @@ struct NixRepl
void initEnv(); void initEnv();
void reloadFiles(); void reloadFiles();
void addAttrsToScope(Value & attrs); void addAttrsToScope(Value & attrs);
void addVarToScope(const Symbol & name, Value & v); void addVarToScope(const SymbolIdx name, Value & v);
Expr * parseString(std::string s); Expr * parseString(std::string s);
void evalString(std::string s, Value & v); void evalString(std::string s, Value & v);
@ -347,9 +347,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
state->forceAttrs(v, noPos); state->forceAttrs(v, noPos);
for (auto & i : *v.attrs) { for (auto & i : *v.attrs) {
std::string name = i.name; std::string_view name = state->symbols[i.name];
if (name.substr(0, cur2.size()) != cur2) continue; if (name.substr(0, cur2.size()) != cur2) continue;
completions.insert(prev + expr + "." + name); completions.insert(concatStrings(prev, expr, ".", name));
} }
} catch (ParseError & e) { } catch (ParseError & e) {
@ -464,8 +464,9 @@ bool NixRepl::processLine(std::string line)
const auto [file, line] = [&] () -> std::pair<std::string, uint32_t> { const auto [file, line] = [&] () -> std::pair<std::string, uint32_t> {
if (v.type() == nPath || v.type() == nString) { if (v.type() == nPath || v.type() == nString) {
PathSet context; PathSet context;
auto filename = state->coerceToString(noPos, v, context); auto filename = state->coerceToString(noPos, v, context).toOwned();
return {state->symbols.create(*filename), 0}; state->symbols.create(filename);
return {filename, 0};
} else if (v.isLambda()) { } else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos]; auto pos = state->positions[v.lambda.fun->pos];
return {pos.file, pos.line}; return {pos.file, pos.line};
@ -672,7 +673,7 @@ void NixRepl::initEnv()
varNames.clear(); varNames.clear();
for (auto & i : state->staticBaseEnv.vars) for (auto & i : state->staticBaseEnv.vars)
varNames.insert(i.first); varNames.emplace(state->symbols[i.first]);
} }
@ -702,7 +703,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
for (auto & i : *attrs.attrs) { for (auto & i : *attrs.attrs) {
staticEnv.vars.emplace_back(i.name, displ); staticEnv.vars.emplace_back(i.name, displ);
env->values[displ++] = i.value; env->values[displ++] = i.value;
varNames.insert((std::string) i.name); varNames.emplace(state->symbols[i.name]);
} }
staticEnv.sort(); staticEnv.sort();
staticEnv.deduplicate(); staticEnv.deduplicate();
@ -710,7 +711,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
} }
void NixRepl::addVarToScope(const Symbol & name, Value & v) void NixRepl::addVarToScope(const SymbolIdx name, Value & v)
{ {
if (displ >= envSize) if (displ >= envSize)
throw Error("environment full; cannot add more variables"); throw Error("environment full; cannot add more variables");
@ -719,7 +720,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
staticEnv.vars.emplace_back(name, displ); staticEnv.vars.emplace_back(name, displ);
staticEnv.sort(); staticEnv.sort();
env->values[displ++] = &v; env->values[displ++] = &v;
varNames.insert((std::string) name); varNames.emplace(state->symbols[name]);
} }
@ -812,7 +813,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
typedef std::map<std::string, Value *> Sorted; typedef std::map<std::string, Value *> Sorted;
Sorted sorted; Sorted sorted;
for (auto & i : *v.attrs) for (auto & i : *v.attrs)
sorted[i.name] = i.value; sorted.emplace(state->symbols[i.name], i.value);
for (auto & i : sorted) { for (auto & i : sorted) {
if (isVarName(i.first)) if (isVarName(i.first))

View file

@ -154,7 +154,7 @@ struct CmdSearch : InstallableCommand, MixJSON
recurse(); recurse();
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) { else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations); auto attr = cursor.maybeGetAttr("recurseForDerivations");
if (attr && attr->getBool()) if (attr && attr->getBool())
recurse(); recurse();
} }