aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval.cc55
-rw-r--r--src/libexpr/lexer.l2
-rw-r--r--src/libexpr/nixexpr.cc39
-rw-r--r--src/libexpr/nixexpr.hh28
-rw-r--r--src/libexpr/parser.y167
-rw-r--r--src/libexpr/primops.cc6
6 files changed, 231 insertions, 66 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 12c6aa8dc..2087c7c43 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -137,6 +137,18 @@ static void * oomHandler(size_t requested)
}
+static Symbol getName(const AttrName & name, EvalState & state, Env & env) {
+ if (name.symbol.set()) {
+ return name.symbol;
+ } else {
+ Value nameValue;
+ name.expr->eval(state, env, nameValue);
+ state.forceStringNoCtx(nameValue);
+ return state.symbols.create(nameValue.string.s);
+ }
+}
+
+
EvalState::EvalState()
: sWith(symbols.create("<with>"))
, sOutPath(symbols.create("outPath"))
@@ -264,6 +276,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, con
throw EvalError(format(s) % s2 % s3);
}
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const Symbol & sym, const Pos & p1, const Pos & p2))
+{
+ throw EvalError(format(s) % sym % p1 % p2);
+}
+
LocalNoInlineNoReturn(void throwTypeError(const char * s))
{
throw TypeError(s);
@@ -574,12 +591,14 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{
state.mkAttrs(v, attrs.size());
+ Env *dynamicEnv = &env;
if (recursive) {
/* Create a new environment that contains the attributes in
this `rec'. */
Env & env2(state.allocEnv(attrs.size()));
env2.up = &env;
+ dynamicEnv = &env2;
AttrDefs::iterator overrides = attrs.find(state.sOverrides);
bool hasOverrides = overrides != attrs.end();
@@ -622,9 +641,24 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
}
}
- else {
+ else
foreach (AttrDefs::iterator, i, attrs)
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
+
+ /* dynamic attrs apply *after* rec and __overrides */
+ foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
+ Value nameVal;
+ i->nameExpr->eval(state, *dynamicEnv, nameVal);
+ state.forceStringNoCtx(nameVal);
+ Symbol nameSym = state.symbols.create(nameVal.string.s);
+ Bindings::iterator j = v.attrs->find(nameSym);
+ if (j != v.attrs->end())
+ throwEvalError("dynamic attribute `%1%' at %2% already defined at %3%", nameSym, i->pos, *j->pos);
+
+ i->valueExpr->setName(nameSym);
+ /* Keep sorted order so find can catch duplicates */
+ v.attrs->insert(lower_bound(v.attrs->begin(), v.attrs->end(), Attr(nameSym, 0)),
+ Attr(nameSym, i->valueExpr->maybeThunk(state, *dynamicEnv), &i->pos));
}
}
@@ -678,17 +712,18 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
foreach (AttrPath::const_iterator, i, attrPath) {
nrLookups++;
Bindings::iterator j;
+ Symbol name = getName(*i, state, env);
if (def) {
state.forceValue(*vAttrs);
if (vAttrs->type != tAttrs ||
- (j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
+ (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
def->eval(state, env, v);
return;
}
} else {
state.forceAttrs(*vAttrs);
- if ((j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
+ if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError("attribute `%1%' missing", showAttrPath(attrPath));
}
vAttrs = j->value;
@@ -719,8 +754,9 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
foreach (AttrPath::const_iterator, i, attrPath) {
state.forceValue(*vAttrs);
Bindings::iterator j;
+ Symbol name = getName(*i, state, env);
if (vAttrs->type != tAttrs ||
- (j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end())
+ (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
mkBool(v, false);
return;
@@ -925,6 +961,17 @@ void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
}
+void ExprBuiltin::eval(EvalState & state, Env & env, Value & v)
+{
+ // Not a hot path at all, but would be nice to access state.baseEnv directly
+ Env *baseEnv = &env;
+ while (baseEnv->up) baseEnv = baseEnv->up;
+ Bindings::iterator binding = baseEnv->values[0]->attrs->find(name);
+ assert(binding != baseEnv->values[0]->attrs->end());
+ v = *binding->value;
+}
+
+
void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{
Value v1; e1->eval(state, env, v1);
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 5d0360401..911850cc5 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -117,6 +117,8 @@ or { return OR_KW; }
return INT;
}
+\$\{ { return DOLLAR_CURLY; }
+
\" { BEGIN(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
/* !!! Not quite right: we want a follow restriction on
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index f4b4295e2..9f0bc2630 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -61,6 +61,8 @@ void ExprAttrs::show(std::ostream & str)
str << "inherit " << i->first << " " << "; ";
else
str << i->first << " = " << *i->second.e << "; ";
+ foreach (DynamicAttrDefs::iterator, i, dynamicAttrs)
+ str << "\"${" << *i->nameExpr << "}\" = " << *i->valueExpr << "; ";
str << "}";
}
@@ -121,6 +123,11 @@ void ExprOpNot::show(std::ostream & str)
str << "! " << *e;
}
+void ExprBuiltin::show(std::ostream & str)
+{
+ str << "builtins." << name;
+}
+
void ExprConcatStrings::show(std::ostream & str)
{
bool first = true;
@@ -148,12 +155,19 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
string showAttrPath(const AttrPath & attrPath)
{
- string s;
+ std::ostringstream out;
+ bool first = true;
foreach (AttrPath::const_iterator, i, attrPath) {
- if (!s.empty()) s += '.';
- s += *i;
+ if (!first)
+ out << '.';
+ else
+ first = false;
+ if (i->symbol.set())
+ out << i->symbol;
+ else
+ out << "\"${" << *i->expr << "}\"";
}
- return s;
+ return out.str();
}
@@ -213,17 +227,25 @@ void ExprSelect::bindVars(const StaticEnv & env)
{
e->bindVars(env);
if (def) def->bindVars(env);
+ foreach (AttrPath::iterator, i, attrPath)
+ if (!i->symbol.set())
+ i->expr->bindVars(env);
}
void ExprOpHasAttr::bindVars(const StaticEnv & env)
{
e->bindVars(env);
+ foreach (AttrPath::iterator, i, attrPath)
+ if (!i->symbol.set())
+ i->expr->bindVars(env);
}
void ExprAttrs::bindVars(const StaticEnv & env)
{
+ const StaticEnv *dynamicEnv = &env;
if (recursive) {
StaticEnv newEnv(false, &env);
+ dynamicEnv = &newEnv;
unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs)
@@ -236,6 +258,11 @@ void ExprAttrs::bindVars(const StaticEnv & env)
else
foreach (AttrDefs::iterator, i, attrs)
i->second.e->bindVars(env);
+
+ foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
+ i->nameExpr->bindVars(*dynamicEnv);
+ i->valueExpr->bindVars(*dynamicEnv);
+ }
}
void ExprList::bindVars(const StaticEnv & env)
@@ -314,6 +341,10 @@ void ExprOpNot::bindVars(const StaticEnv & env)
e->bindVars(env);
}
+void ExprBuiltin::bindVars(const StaticEnv & env)
+{
+}
+
void ExprConcatStrings::bindVars(const StaticEnv & env)
{
foreach (vector<Expr *>::iterator, i, *es)
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 61eb81fab..bc6993477 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -50,10 +50,19 @@ struct Env;
struct Value;
struct EvalState;
struct StaticEnv;
+struct Expr;
/* An attribute path is a sequence of attribute names. */
-typedef vector<Symbol> AttrPath;
+struct AttrName
+{
+ Symbol symbol;
+ Expr *expr;
+ AttrName(const Symbol & s) : symbol(s) {};
+ AttrName(Expr *e) : expr(e) {};
+};
+
+typedef std::vector<AttrName> AttrPath;
string showAttrPath(const AttrPath & attrPath);
@@ -138,7 +147,7 @@ struct ExprSelect : Expr
Expr * e, * def;
AttrPath attrPath;
ExprSelect(Expr * e, const AttrPath & attrPath, Expr * def) : e(e), def(def), attrPath(attrPath) { };
- ExprSelect(Expr * e, const Symbol & name) : e(e), def(0) { attrPath.push_back(name); };
+ ExprSelect(Expr * e, const Symbol & name) : e(e), def(0) { attrPath.push_back(AttrName(name)); };
COMMON_METHODS
};
@@ -163,6 +172,14 @@ struct ExprAttrs : Expr
};
typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs;
+ struct DynamicAttrDef {
+ Expr * nameExpr;
+ Expr * valueExpr;
+ Pos pos;
+ DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const Pos & pos) : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
+ };
+ typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
+ DynamicAttrDefs dynamicAttrs;
ExprAttrs() : recursive(false) { };
COMMON_METHODS
};
@@ -248,6 +265,13 @@ struct ExprOpNot : Expr
COMMON_METHODS
};
+struct ExprBuiltin : Expr
+{
+ Symbol name;
+ ExprBuiltin(const Symbol & name) : name(name) { };
+ COMMON_METHODS
+};
+
#define MakeBinOp(name, s) \
struct Expr##name : Expr \
{ \
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 7699cf502..55a42fcab 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -74,37 +74,49 @@ static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prev
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
- AttrPath attrPath; attrPath.push_back(attr);
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
- % showAttrPath(attrPath) % pos % prevPos);
+ % attr % pos % prevPos);
}
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const Pos & pos)
{
- unsigned int n = 0;
- foreach (AttrPath::const_iterator, i, attrPath) {
- n++;
- ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i);
- if (j != attrs->attrs.end()) {
- if (!j->second.inherited) {
- ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
- if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.pos);
- attrs = attrs2;
- } else
- dupAttr(attrPath, pos, j->second.pos);
- } else {
- if (n == attrPath.size())
- attrs->attrs[*i] = ExprAttrs::AttrDef(e, pos);
- else {
+ AttrPath::iterator i;
+ // All attrpaths have at least one attr
+ assert(!attrPath.empty());
+ for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
+ if (i->symbol.set()) {
+ ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
+ if (j != attrs->attrs.end()) {
+ if (!j->second.inherited) {
+ ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
+ if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
+ attrs = attrs2;
+ } else
+ dupAttr(attrPath, pos, j->second.pos);
+ } else {
ExprAttrs * nested = new ExprAttrs;
- attrs->attrs[*i] = ExprAttrs::AttrDef(nested, pos);
+ attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
attrs = nested;
}
+ } else {
+ ExprAttrs *nested = new ExprAttrs;
+ attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
+ attrs = nested;
}
}
- e->setName(attrPath.back());
+ if (i->symbol.set()) {
+ ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
+ if (j != attrs->attrs.end()) {
+ dupAttr(attrPath, pos, j->second.pos);
+ } else {
+ attrs->attrs[i->symbol] = ExprAttrs::AttrDef(e, pos);
+ e->setName(i->symbol);
+ }
+ } else {
+ attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
+ }
}
@@ -243,7 +255,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
char * id; // !!! -> Symbol
char * path;
char * uri;
- std::vector<nix::Symbol> * attrNames;
+ std::vector<nix::AttrName> * attrNames;
std::vector<nix::Expr *> * string_parts;
}
@@ -254,7 +266,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type <formals> formals
%type <formal> formal
%type <attrNames> attrs attrpath
-%type <string_parts> string_parts ind_string_parts
+%type <string_parts> string_parts_interpolated ind_string_parts
+%type <e> string_parts string_attr
%type <id> attr
%token <id> ID ATTRPATH
%token <e> STR IND_STR
@@ -300,7 +313,11 @@ expr_function
| WITH expr ';' expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function
- { $$ = new ExprLet($2, $4); }
+ { if (!$2->dynamicAttrs.empty())
+ throw ParseError(format("dynamic attributes not allowed in let at %1%")
+ % CUR_POS);
+ $$ = new ExprLet($2, $4);
+ }
| expr_if
;
@@ -311,13 +328,13 @@ expr_if
expr_op
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
-| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), new ExprInt(0)), $2); }
+| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("sub")), new ExprInt(0)), $2); }
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
- | expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3); }
- | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1)); }
- | expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1); }
- | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3)); }
+ | expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $1), $3); }
+ | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $3), $1)); }
+ | expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $3), $1); }
+ | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $1), $3)); }
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
@@ -329,9 +346,9 @@ expr_op
l->push_back($3);
$$ = new ExprConcatStrings(false, l);
}
- | expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), $1), $3); }
- | expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__mul")), $1), $3); }
- | expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__div")), $1), $3); }
+ | expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("sub")), $1), $3); }
+ | expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("mul")), $1), $3); }
+ | expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("div")), $1), $3); }
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
| expr_app
;
@@ -362,12 +379,7 @@ expr_simple
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
}
| INT { $$ = new ExprInt($1); }
- | '"' string_parts '"' {
- /* For efficiency, and to simplify parse trees a bit. */
- if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
- else if ($2->size() == 1 && dynamic_cast<ExprString *>($2->front())) $$ = $2->front();
- else $$ = new ExprConcatStrings(true, $2);
- }
+ | '"' string_parts '"' { $$ = $2; }
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(data->symbols, *$2);
}
@@ -381,7 +393,7 @@ expr_simple
‘throw’. */
$$ = path2 == ""
? (Expr * ) new ExprApp(
- new ExprVar(noPos, data->symbols.create("throw")),
+ new ExprBuiltin(data->symbols.create("throw")),
new ExprString(data->symbols.create(
(format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str())))
: (Expr * ) new ExprPath(path2);
@@ -400,9 +412,27 @@ expr_simple
;
string_parts
- : string_parts STR { $$ = $1; $1->push_back($2); }
- | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
- | { $$ = new vector<Expr *>; }
+ : STR
+ | string_parts_interpolated { $$ = new ExprConcatStrings(true, $1); }
+ | { $$ = new ExprString(data->symbols.create("")) }
+ ;
+
+string_parts_interpolated
+ : string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
+ | string_parts_interpolated DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
+ | STR DOLLAR_CURLY expr '}'
+ {
+ backToString(scanner);
+ $$ = new vector<Expr *>;
+ $$->push_back($1);
+ $$->push_back($3);
+ }
+ | DOLLAR_CURLY expr '}'
+ {
+ backToString(scanner);
+ $$ = new vector<Expr *>;
+ $$->push_back($2);
+ }
;
ind_string_parts
@@ -416,39 +446,70 @@ binds
| binds INHERIT attrs ';'
{ $$ = $1;
foreach (AttrPath::iterator, i, *$3) {
- if ($$->attrs.find(*i) != $$->attrs.end())
- dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
+ if ($$->attrs.find(i->symbol) != $$->attrs.end())
+ dupAttr(i->symbol, makeCurPos(@3, data), $$->attrs[i->symbol].pos);
Pos pos = makeCurPos(@3, data);
- $$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, *i), pos, true);
+ $$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i->symbol), pos, true);
}
}
| binds INHERIT '(' expr ')' attrs ';'
{ $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */
- foreach (vector<Symbol>::iterator, i, *$6) {
- if ($$->attrs.find(*i) != $$->attrs.end())
- dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos);
- $$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data));
+ foreach (AttrPath::iterator, i, *$6) {
+ if ($$->attrs.find(i->symbol) != $$->attrs.end())
+ dupAttr(i->symbol, makeCurPos(@6, data), $$->attrs[i->symbol].pos);
+ $$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprSelect($4, i->symbol), makeCurPos(@6, data));
}
}
| { $$ = new ExprAttrs; }
;
attrs
- : attrs attr { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
- | { $$ = new vector<Symbol>; }
+ : attrs attr { $$ = $1; $1->push_back(AttrName(data->symbols.create($2))); }
+ | attrs string_attr
+ { $$ = $1;
+ ExprString *str = dynamic_cast<ExprString *>($2);
+ if (str) {
+ $$->push_back(AttrName(str->s));
+ delete str;
+ } else
+ throw ParseError(format("dynamic attributes not allowed in inherit at %1%")
+ % makeCurPos(@2, data));
+ }
+ | { $$ = new AttrPath; }
;
attrpath
- : attrpath '.' attr { $$ = $1; $1->push_back(data->symbols.create($3)); }
- | attr { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
+ : attrpath '.' attr { $$ = $1; $1->push_back(AttrName(data->symbols.create($3))); }
+ | attrpath '.' string_attr
+ { $$ = $1;
+ ExprString *str = dynamic_cast<ExprString *>($3);
+ if (str) {
+ $$->push_back(AttrName(str->s));
+ delete str;
+ } else
+ $$->push_back(AttrName($3));
+ }
+ | attr { $$ = new vector<AttrName>; $$->push_back(AttrName(data->symbols.create($1))); }
+ | string_attr
+ { $$ = new vector<AttrName>;
+ ExprString *str = dynamic_cast<ExprString *>($1);
+ if (str) {
+ $$->push_back(AttrName(str->s));
+ delete str;
+ } else
+ $$->push_back(AttrName($1));
+ }
;
attr
: ID { $$ = $1; }
| OR_KW { $$ = "or"; }
- | '"' STR '"'
- { $$ = strdup(((string) ((ExprString *) $2)->s).c_str()); delete $2; }
+ ;
+
+string_attr
+ : '"' string_parts '"' { $$ = $2; }
+ | DOLLAR_CURLY expr '}' { $$ = new ExprConcatStrings(true, new vector<Expr*>(1, $2)); }
;
expr_list
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index bb6739d20..ca316f08a 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -582,8 +582,8 @@ static void prim_storePath(EvalState & state, Value * * args, Value & v)
if (!isInStore(path))
throw EvalError(format("path `%1%' is not in the Nix store") % path);
Path path2 = toStorePath(path);
- if (!store->isValidPath(path2))
- throw EvalError(format("store path `%1%' is not valid") % path2);
+ if (!settings.readOnlyMode)
+ store->ensurePath(path2);
context.insert(path2);
mkString(v, path, context);
}
@@ -1242,7 +1242,7 @@ void EvalState::createBaseEnv()
language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ?
primOp' to check. */
- mkInt(v, 1);
+ mkInt(v, 2);
addConstant("__langVersion", v);
// Miscellaneous