aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/parser.y
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr/parser.y')
-rw-r--r--src/libexpr/parser.y175
1 files changed, 110 insertions, 65 deletions
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 7a8e93c12..f0c80ebd5 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -16,6 +16,8 @@
#ifndef BISON_HEADER
#define BISON_HEADER
+#include <variant>
+
#include "util.hh"
#include "nixexpr.hh"
@@ -39,8 +41,22 @@ namespace nix {
{ };
};
+ struct ParserFormals {
+ std::vector<Formal> formals;
+ bool ellipsis = false;
+ };
+
}
+// using C a struct allows us to avoid having to define the special
+// members that using string_view here would implicitly delete.
+struct StringToken {
+ const char * p;
+ size_t l;
+ bool hasIndentation;
+ operator std::string_view() const { return {p, l}; }
+};
+
#define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
@@ -140,21 +156,46 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
}
-static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
+static Formals * toFormals(ParseData & data, ParserFormals * formals,
+ Pos pos = noPos, Symbol arg = {})
{
- if (!formals->argNames.insert(formal.name).second)
+ std::sort(formals->formals.begin(), formals->formals.end(),
+ [] (const auto & a, const auto & b) {
+ return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
+ });
+
+ std::optional<std::pair<Symbol, Pos>> duplicate;
+ for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
+ if (formals->formals[i].name != formals->formals[i + 1].name)
+ continue;
+ std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
+ duplicate = std::min(thisDup, duplicate.value_or(thisDup));
+ }
+ if (duplicate)
+ throw ParseError({
+ .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
+ .errPos = duplicate->second
+ });
+
+ Formals result;
+ result.ellipsis = formals->ellipsis;
+ result.formals = std::move(formals->formals);
+
+ if (arg.set() && result.has(arg))
throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'",
- formal.name),
+ .msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos
});
- formals->formals.push_front(formal);
+
+ delete formals;
+ return new Formals(std::move(result));
}
-static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
+static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
+ vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
{
- if (es.empty()) return new ExprString(symbols.create(""));
+ if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -163,20 +204,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
size_t minIndent = 1000000;
size_t curIndent = 0;
for (auto & [i_pos, i] : es) {
- ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
- if (!e) {
- /* Anti-quotations end the current start-of-line whitespace. */
+ auto * str = std::get_if<StringToken>(&i);
+ if (!str || !str->hasIndentation) {
+ /* Anti-quotations and escaped characters end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
continue;
}
- for (size_t j = 0; j < e->s.size(); ++j) {
+ for (size_t j = 0; j < str->l; ++j) {
if (atStartOfLine) {
- if (e->s[j] == ' ')
+ if (str->p[j] == ' ')
curIndent++;
- else if (e->s[j] == '\n') {
+ else if (str->p[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@@ -184,7 +225,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
- } else if (e->s[j] == '\n') {
+ } else if (str->p[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@@ -196,33 +237,31 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
- for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
- ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
- if (!e) {
- atStartOfLine = false;
- curDropped = 0;
- es2->push_back(*i);
- continue;
- }
-
+ auto i = es.begin();
+ const auto trimExpr = [&] (Expr * e) {
+ atStartOfLine = false;
+ curDropped = 0;
+ es2->emplace_back(i->first, e);
+ };
+ const auto trimString = [&] (const StringToken & t) {
string s2;
- for (size_t j = 0; j < e->s.size(); ++j) {
+ for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) {
- if (e->s[j] == ' ') {
+ if (t.p[j] == ' ') {
if (curDropped++ >= minIndent)
- s2 += e->s[j];
+ s2 += t.p[j];
}
- else if (e->s[j] == '\n') {
+ else if (t.p[j] == '\n') {
curDropped = 0;
- s2 += e->s[j];
+ s2 += t.p[j];
} else {
atStartOfLine = false;
curDropped = 0;
- s2 += e->s[j];
+ s2 += t.p[j];
}
} else {
- s2 += e->s[j];
- if (e->s[j] == '\n') atStartOfLine = true;
+ s2 += t.p[j];
+ if (t.p[j] == '\n') atStartOfLine = true;
}
}
@@ -234,7 +273,10 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
s2 = string(s2, 0, p + 1);
}
- es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
+ es2->emplace_back(i->first, new ExprString(s2));
+ };
+ for (; i != es.end(); ++i, --n) {
+ std::visit(overloaded { trimExpr, trimString }, i->second);
}
/* If this is a single string, then don't do a concatenation. */
@@ -269,22 +311,17 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
- nix::Formals * formals;
+ nix::ParserFormals * formals;
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
- // using C a struct allows us to avoid having to define the special
- // members that using string_view here would implicitly delete.
- struct StringToken {
- const char * p;
- size_t l;
- operator std::string_view() const { return {p, l}; }
- };
StringToken id; // !!! -> Symbol
StringToken path;
StringToken uri;
+ StringToken str;
std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
+ std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -294,11 +331,12 @@ 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_interpolated ind_string_parts
+%type <string_parts> string_parts_interpolated
+%type <ind_string_parts> ind_string_parts
%type <e> path_start string_parts string_attr
%type <id> attr
%token <id> ID ATTRPATH
-%token <e> STR IND_STR
+%token <str> STR IND_STR
%token <n> INT
%token <nf> FLOAT
%token <path> PATH HPATH SPATH PATH_END
@@ -331,11 +369,17 @@ expr_function
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); }
| '{' formals '}' '@' ID ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
+ {
+ Symbol arg = data->symbols.create($5);
+ $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
+ }
| ID '@' '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
+ {
+ Symbol arg = data->symbols.create($1);
+ $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
+ }
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
@@ -426,7 +470,7 @@ expr_simple
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
- new ExprString(data->symbols.create(path))});
+ new ExprString(path)});
}
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
@@ -435,7 +479,7 @@ expr_simple
.msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS
});
- $$ = new ExprString(data->symbols.create($1));
+ $$ = new ExprString(string($1));
}
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
@@ -450,18 +494,19 @@ expr_simple
;
string_parts
- : STR
+ : STR { $$ = new ExprString(string($1)); }
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
- | { $$ = new ExprString(data->symbols.create("")); }
+ | { $$ = new ExprString(""); }
;
string_parts_interpolated
- : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
+ : string_parts_interpolated STR
+ { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
$$ = new vector<std::pair<Pos, Expr *> >;
- $$->emplace_back(makeCurPos(@1, data), $1);
+ $$->emplace_back(makeCurPos(@1, data), new ExprString(string($1)));
$$->emplace_back(makeCurPos(@2, data), $3);
}
;
@@ -483,7 +528,7 @@ path_start
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | { $$ = new vector<std::pair<Pos, Expr *> >; }
+ | { $$ = new vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
;
binds
@@ -515,7 +560,7 @@ attrs
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($2);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
throw ParseError({
@@ -532,7 +577,7 @@ attrpath
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($3);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
$$->push_back(AttrName($3));
@@ -542,7 +587,7 @@ attrpath
{ $$ = new vector<AttrName>;
ExprString *str = dynamic_cast<ExprString *>($1);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
$$->push_back(AttrName($1));
@@ -566,13 +611,13 @@ expr_list
formals
: formal ',' formals
- { $$ = $3; addFormal(CUR_POS, $$, *$1); }
+ { $$ = $3; $$->formals.push_back(*$1); }
| formal
- { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
+ { $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
- { $$ = new Formals; $$->ellipsis = false; }
+ { $$ = new ParserFormals; $$->ellipsis = false; }
| ELLIPSIS
- { $$ = new Formals; $$->ellipsis = true; }
+ { $$ = new ParserFormals; $$->ellipsis = true; }
;
formal
@@ -598,7 +643,7 @@ namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
- const Path & path, const Path & basePath, StaticEnv & staticEnv)
+ const PathView path, const PathView basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
ParseData data(*this);
@@ -709,24 +754,24 @@ void EvalState::addToSearchPath(const string & s)
}
-Path EvalState::findFile(const string & path)
+Path EvalState::findFile(const std::string_view path)
{
return findFile(searchPath, path);
}
-Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos)
+Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
{
for (auto & i : searchPath) {
std::string suffix;
if (i.first.empty())
- suffix = "/" + path;
+ suffix = concatStrings("/", path);
else {
auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 ||
(path.size() > s && path[s] != '/'))
continue;
- suffix = path.size() == s ? "" : "/" + string(path, s);
+ suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
}
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
@@ -735,7 +780,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
}
if (hasPrefix(path, "nix/"))
- return corepkgsPrefix + path.substr(4);
+ return concatStrings(corepkgsPrefix, path.substr(4));
throw ThrownError({
.msg = hintfmt(evalSettings.pureEval