aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoreldritch horrors <pennae@lix.systems>2024-06-16 23:10:09 +0200
committereldritch horrors <pennae@lix.systems>2024-06-17 19:46:44 +0000
commitdad8bc679e9f7bd97442249293138c7a2af311e4 (patch)
tree0fd0a1bc720f851e40b8683abaf6733fc20dd34e /src
parent9592a9fd5740ffa0cdbbba7409b0e0aaee369d96 (diff)
libexpr: don't immediately throw parser errors
now that destructors are hooked up we want to give the C skeleton every real chance to actually run them. since bison does not call destructors on values that have been passed to semantic actions even when an action causes an abort we will also have to delete some things manually still. Change-Id: Ia22bdaa9e969b74e17a6c496e35e6c2d86b7d750
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/lexer.l18
-rw-r--r--src/libexpr/parser-state.hh37
-rw-r--r--src/libexpr/parser.y46
3 files changed, 64 insertions, 37 deletions
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 1c1cdd673..5bc815f00 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -32,6 +32,12 @@
using namespace nix;
+#define THROW(...) \
+ do { \
+ state->error.reset(new auto(__VA_ARGS__)); \
+ return YYerror; \
+ } while (0)
+
namespace nix {
#define CUR_POS state->at(*yylloc)
@@ -135,20 +141,20 @@ or { return OR_KW; }
if (numMay.has_value()) {
yylval->n = *numMay;
} else {
- throw ParseError(ErrorInfo{
+ THROW(ParseError(ErrorInfo{
.msg = HintFmt("invalid integer '%1%'", yytext),
.pos = state->positions[CUR_POS],
- });
+ }));
}
return INT;
}
{FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0);
if (errno != 0)
- throw ParseError(ErrorInfo{
+ THROW(ParseError(ErrorInfo{
.msg = HintFmt("invalid float '%1%'", yytext),
.pos = state->positions[CUR_POS],
- });
+ }));
return FLOAT;
}
@@ -274,10 +280,10 @@ or { return OR_KW; }
<INPATH_SLASH>{ANY} |
<INPATH_SLASH><<EOF>> {
- throw ParseError(ErrorInfo{
+ THROW(ParseError(ErrorInfo{
.msg = HintFmt("path has a trailing slash"),
.pos = state->positions[CUR_POS],
- });
+ }));
}
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh
index 3305e05b6..94f10064f 100644
--- a/src/libexpr/parser-state.hh
+++ b/src/libexpr/parser-state.hh
@@ -45,34 +45,35 @@ struct ParserState
SourcePath basePath;
PosTable::Origin origin;
const Expr::AstSymbols & s;
+ std::unique_ptr<Error> error;
- void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
- void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
- void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
- Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
+ [[nodiscard]] ParseError dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
+ [[nodiscard]] ParseError dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
+ [[nodiscard]] std::optional<ParseError> addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
+ [[nodiscard]] std::optional<ParseError> validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
PosIdx at(const ParserLocation & loc);
};
-inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
+inline ParseError ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{
- throw ParseError({
+ return ParseError({
.msg = HintFmt("attribute '%1%' already defined at %2%",
showAttrPath(symbols, attrPath), positions[prevPos]),
.pos = positions[pos]
});
}
-inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
+inline ParseError ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
{
- throw ParseError({
+ return ParseError({
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
.pos = positions[pos]
});
}
-inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos)
+inline std::optional<ParseError> ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos)
{
AttrPath::iterator i;
// All attrpaths have at least one attr
@@ -85,10 +86,10 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
if (j != attrs->attrs.end()) {
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
- if (!attrs2) dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
+ if (!attrs2) return dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
attrs = attrs2;
} else
- dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
+ return dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
} else {
ExprAttrs * nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
@@ -117,7 +118,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
- dupAttr(ad.first, j2->second.pos, ad.second.pos);
+ return dupAttr(ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
@@ -131,7 +132,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
}
} else {
- dupAttr(attrPath, pos, j->second.pos);
+ return dupAttr(attrPath, pos, j->second.pos);
}
} else {
// This attr path is not defined. Let's create it.
@@ -141,9 +142,11 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
} else {
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
}
+
+ return {};
}
-inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
+inline std::optional<ParseError> ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
{
std::sort(formals->formals.begin(), formals->formals.end(),
[] (const auto & a, const auto & b) {
@@ -158,18 +161,18 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
}
if (duplicate)
- throw ParseError({
+ return ParseError({
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
.pos = positions[duplicate->second]
});
if (arg && formals->has(arg))
- throw ParseError({
+ return ParseError({
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
.pos = positions[pos]
});
- return formals;
+ return {};
}
inline Expr * ParserState::stripIndentation(const PosIdx pos,
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 594026028..7783d2c20 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -62,6 +62,13 @@ using namespace nix;
// otherwise destructors cause compiler errors
#pragma GCC diagnostic ignored "-Wswitch-enum"
+#define THROW(err, ...) \
+ do { \
+ state->error.reset(new auto(err)); \
+ [](auto... d) { (delete d, ...); }(__VA_ARGS__); \
+ YYABORT; \
+ } while (0)
+
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
{
if (std::string_view(error).starts_with("syntax error, unexpected end of file")) {
@@ -153,16 +160,19 @@ expr_function
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); }
+ { if (auto e = state->validateFormals($2)) THROW(*e);
+ $$ = new ExprLambda(CUR_POS, $2, $5); }
| '{' formals '}' '@' ID ':' expr_function
{
auto arg = state->symbols.create($5);
- $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7);
+ if (auto e = state->validateFormals($2, CUR_POS, arg)) THROW(*e, $2, $7);
+ $$ = new ExprLambda(CUR_POS, arg, $2, $7);
}
| ID '@' '{' formals '}' ':' expr_function
{
auto arg = state->symbols.create($1);
- $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7);
+ if (auto e = state->validateFormals($4, CUR_POS, arg)) THROW(*e, $4, $7);
+ $$ = new ExprLambda(CUR_POS, arg, $4, $7);
}
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
@@ -170,10 +180,10 @@ expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function
{ if (!$2->dynamicAttrs.empty())
- throw ParseError({
+ THROW(ParseError({
.msg = HintFmt("dynamic attributes not allowed in let"),
.pos = state->positions[CUR_POS]
- });
+ }), $2, $4);
$$ = new ExprLet($2, $4);
}
| expr_if
@@ -260,10 +270,10 @@ expr_simple
| URI {
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
if (noURLLiterals)
- throw ParseError({
+ THROW(ParseError({
.msg = HintFmt("URL literals are disabled"),
.pos = state->positions[CUR_POS]
- });
+ }));
$$ = new ExprString(std::string($1));
}
| '(' expr ')' { $$ = $2; }
@@ -306,10 +316,10 @@ path_start
}
| HPATH {
if (evalSettings.pureEval) {
- throw Error(
+ THROW(Error(
"the path '%s' can not be resolved in pure mode",
std::string_view($1.p, $1.l)
- );
+ ));
}
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path);
@@ -323,12 +333,16 @@ ind_string_parts
;
binds
- : binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; }
+ : binds attrpath '=' expr ';'
+ { $$ = $1;
+ if (auto e = state->addAttr($$, std::move(*$2), $4, state->at(@2))) THROW(*e, $1, $2);
+ delete $2;
+ }
| binds INHERIT attrs ';'
{ $$ = $1;
for (auto & [i, iPos] : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
- state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
+ THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1);
$$->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
@@ -343,7 +357,7 @@ binds
auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1);
for (auto & [i, iPos] : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
- state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
+ THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1);
$$->attrs.emplace(
i.symbol,
ExprAttrs::AttrDef(
@@ -365,10 +379,10 @@ attrs
$$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
delete str;
} else
- throw ParseError({
+ THROW(ParseError({
.msg = HintFmt("dynamic attributes not allowed in inherit"),
.pos = state->positions[state->at(@2)]
- });
+ }), $1, $2);
}
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
;
@@ -457,6 +471,10 @@ Expr * parseExprFromBuf(
yy_scan_buffer(text, length, scanner);
yyparse(scanner, &state);
+ if (state.error) {
+ delete state.result;
+ throw *state.error;
+ }
return state.result;
}