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
This commit is contained in:
eldritch horrors 2024-06-16 23:10:09 +02:00
parent 9592a9fd57
commit dad8bc679e
3 changed files with 64 additions and 37 deletions

View file

@ -32,6 +32,12 @@
using namespace nix; using namespace nix;
#define THROW(...) \
do { \
state->error.reset(new auto(__VA_ARGS__)); \
return YYerror; \
} while (0)
namespace nix { namespace nix {
#define CUR_POS state->at(*yylloc) #define CUR_POS state->at(*yylloc)
@ -135,20 +141,20 @@ or { return OR_KW; }
if (numMay.has_value()) { if (numMay.has_value()) {
yylval->n = *numMay; yylval->n = *numMay;
} else { } else {
throw ParseError(ErrorInfo{ THROW(ParseError(ErrorInfo{
.msg = HintFmt("invalid integer '%1%'", yytext), .msg = HintFmt("invalid integer '%1%'", yytext),
.pos = state->positions[CUR_POS], .pos = state->positions[CUR_POS],
}); }));
} }
return INT; return INT;
} }
{FLOAT} { errno = 0; {FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0); yylval->nf = strtod(yytext, 0);
if (errno != 0) if (errno != 0)
throw ParseError(ErrorInfo{ THROW(ParseError(ErrorInfo{
.msg = HintFmt("invalid float '%1%'", yytext), .msg = HintFmt("invalid float '%1%'", yytext),
.pos = state->positions[CUR_POS], .pos = state->positions[CUR_POS],
}); }));
return FLOAT; return FLOAT;
} }
@ -274,10 +280,10 @@ or { return OR_KW; }
<INPATH_SLASH>{ANY} | <INPATH_SLASH>{ANY} |
<INPATH_SLASH><<EOF>> { <INPATH_SLASH><<EOF>> {
throw ParseError(ErrorInfo{ THROW(ParseError(ErrorInfo{
.msg = HintFmt("path has a trailing slash"), .msg = HintFmt("path has a trailing slash"),
.pos = state->positions[CUR_POS], .pos = state->positions[CUR_POS],
}); }));
} }
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } {SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }

View file

@ -45,34 +45,35 @@ struct ParserState
SourcePath basePath; SourcePath basePath;
PosTable::Origin origin; PosTable::Origin origin;
const Expr::AstSymbols & s; const Expr::AstSymbols & s;
std::unique_ptr<Error> error;
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); [[nodiscard]] ParseError dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); [[nodiscard]] ParseError dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos); [[nodiscard]] std::optional<ParseError> addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); [[nodiscard]] std::optional<ParseError> validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos, Expr * stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es); std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
PosIdx at(const ParserLocation & loc); 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%", .msg = HintFmt("attribute '%1%' already defined at %2%",
showAttrPath(symbols, attrPath), positions[prevPos]), showAttrPath(symbols, attrPath), positions[prevPos]),
.pos = positions[pos] .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]), .msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
.pos = positions[pos] .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; AttrPath::iterator i;
// All attrpaths have at least one attr // 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 != attrs->attrs.end()) {
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) { if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); 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; attrs = attrs2;
} else } else
dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); return dupAttr({attrPath.begin(), i + 1}, 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);
@ -117,7 +118,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
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, j2->second.pos, ad.second.pos); return dupAttr(ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second); jAttrs->attrs.emplace(ad.first, ad.second);
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e); 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()); ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
} }
} else { } else {
dupAttr(attrPath, pos, j->second.pos); return dupAttr(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.
@ -141,9 +142,11 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
} else { } else {
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); 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(), std::sort(formals->formals.begin(), formals->formals.end(),
[] (const auto & a, const auto & b) { [] (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)); duplicate = std::min(thisDup, duplicate.value_or(thisDup));
} }
if (duplicate) if (duplicate)
throw ParseError({ return ParseError({
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]), .msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
.pos = positions[duplicate->second] .pos = positions[duplicate->second]
}); });
if (arg && formals->has(arg)) if (arg && formals->has(arg))
throw ParseError({ return ParseError({
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
.pos = positions[pos] .pos = positions[pos]
}); });
return formals; return {};
} }
inline Expr * ParserState::stripIndentation(const PosIdx pos, inline Expr * ParserState::stripIndentation(const PosIdx pos,

View file

@ -62,6 +62,13 @@ using namespace nix;
// otherwise destructors cause compiler errors // otherwise destructors cause compiler errors
#pragma GCC diagnostic ignored "-Wswitch-enum" #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) 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")) { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) {
@ -153,16 +160,19 @@ expr_function
: ID ':' expr_function : ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); } { $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function | '{' 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 | '{' formals '}' '@' ID ':' expr_function
{ {
auto arg = state->symbols.create($5); 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 | ID '@' '{' formals '}' ':' expr_function
{ {
auto arg = state->symbols.create($1); 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 | ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); } { $$ = new ExprAssert(CUR_POS, $2, $4); }
@ -170,10 +180,10 @@ expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); } { $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function | LET binds IN expr_function
{ if (!$2->dynamicAttrs.empty()) { if (!$2->dynamicAttrs.empty())
throw ParseError({ THROW(ParseError({
.msg = HintFmt("dynamic attributes not allowed in let"), .msg = HintFmt("dynamic attributes not allowed in let"),
.pos = state->positions[CUR_POS] .pos = state->positions[CUR_POS]
}); }), $2, $4);
$$ = new ExprLet($2, $4); $$ = new ExprLet($2, $4);
} }
| expr_if | expr_if
@ -260,10 +270,10 @@ expr_simple
| URI { | URI {
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
if (noURLLiterals) if (noURLLiterals)
throw ParseError({ THROW(ParseError({
.msg = HintFmt("URL literals are disabled"), .msg = HintFmt("URL literals are disabled"),
.pos = state->positions[CUR_POS] .pos = state->positions[CUR_POS]
}); }));
$$ = new ExprString(std::string($1)); $$ = new ExprString(std::string($1));
} }
| '(' expr ')' { $$ = $2; } | '(' expr ')' { $$ = $2; }
@ -306,10 +316,10 @@ path_start
} }
| HPATH { | HPATH {
if (evalSettings.pureEval) { if (evalSettings.pureEval) {
throw Error( THROW(Error(
"the path '%s' can not be resolved in pure mode", "the path '%s' can not be resolved in pure mode",
std::string_view($1.p, $1.l) std::string_view($1.p, $1.l)
); ));
} }
Path path(getHome() + std::string($1.p + 1, $1.l - 1)); Path path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path); $$ = new ExprPath(path);
@ -323,12 +333,16 @@ ind_string_parts
; ;
binds 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 ';' | binds INHERIT attrs ';'
{ $$ = $1; { $$ = $1;
for (auto & [i, iPos] : *$3) { for (auto & [i, iPos] : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end()) 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( $$->attrs.emplace(
i.symbol, i.symbol,
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); 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); auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1);
for (auto & [i, iPos] : *$6) { for (auto & [i, iPos] : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end()) 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( $$->attrs.emplace(
i.symbol, i.symbol,
ExprAttrs::AttrDef( ExprAttrs::AttrDef(
@ -365,10 +379,10 @@ attrs
$$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2)); $$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
delete str; delete str;
} else } else
throw ParseError({ THROW(ParseError({
.msg = HintFmt("dynamic attributes not allowed in inherit"), .msg = HintFmt("dynamic attributes not allowed in inherit"),
.pos = state->positions[state->at(@2)] .pos = state->positions[state->at(@2)]
}); }), $1, $2);
} }
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; } | { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
; ;
@ -457,6 +471,10 @@ Expr * parseExprFromBuf(
yy_scan_buffer(text, length, scanner); yy_scan_buffer(text, length, scanner);
yyparse(scanner, &state); yyparse(scanner, &state);
if (state.error) {
delete state.result;
throw *state.error;
}
return state.result; return state.result;
} }