1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-08 19:46:02 +01:00

libexpr: Switch parser.y to %skeleton lalr1.cc

Since the parser is now LALR we can easily switch
over to the less ugly sketelon than the default C one.
This would allow us to switch from %union to %define api.value.type variant
in the future to avoid the need for triviall POD types.
This commit is contained in:
Sergei Zimmerman 2025-09-28 21:44:54 +03:00
parent 241c7cd1f9
commit a8715a2d6e
No known key found for this signature in database
6 changed files with 51 additions and 33 deletions

View file

@ -24,7 +24,6 @@ struct StringToken
} }
}; };
// This type must be trivially copyable; see YYLTYPE_IS_TRIVIAL in parser.y.
struct ParserLocation struct ParserLocation
{ {
int beginOffset; int beginOffset;

View file

@ -1,11 +1,11 @@
#include "lexer-helpers.hh" #include "lexer-helpers.hh"
void nix::lexer::internal::initLoc(YYLTYPE * loc) void nix::lexer::internal::initLoc(Parser::location_type * loc)
{ {
loc->beginOffset = loc->endOffset = 0; loc->beginOffset = loc->endOffset = 0;
} }
void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, Parser::location_type * loc, const char * s, size_t len)
{ {
loc->stash(); loc->stash();

View file

@ -2,16 +2,12 @@
#include <cstddef> #include <cstddef>
// including the generated headers twice leads to errors #include "parser-scanner-decls.hh"
#ifndef BISON_HEADER
# include "lexer-tab.hh"
# include "parser-tab.hh"
#endif
namespace nix::lexer::internal { namespace nix::lexer::internal {
void initLoc(YYLTYPE * loc); void initLoc(Parser::location_type * loc);
void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len); void adjustLoc(yyscan_t yyscanner, Parser::location_type * loc, const char * s, size_t len);
} // namespace nix::lexer::internal } // namespace nix::lexer::internal

View file

@ -82,6 +82,10 @@ static void requireExperimentalFeature(const ExperimentalFeature & feature, cons
} }
using enum nix::Parser::token::token_kind_type;
using YYSTYPE = nix::Parser::value_type;
using YYLTYPE = nix::Parser::location_type;
// yacc generates code that uses unannotated fallthrough. // yacc generates code that uses unannotated fallthrough.
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"

View file

@ -0,0 +1,17 @@
#pragma once
#ifndef BISON_HEADER
# include "parser-tab.hh"
using YYSTYPE = nix::parser::BisonParser::value_type;
using YYLTYPE = nix::parser::BisonParser::location_type;
# include "lexer-tab.hh" // IWYU pragma: export
#endif
namespace nix {
class Parser : public parser::BisonParser
{
using BisonParser::BisonParser;
};
} // namespace nix

View file

@ -1,5 +1,7 @@
%skeleton "lalr1.cc"
%define api.location.type { ::nix::ParserLocation } %define api.location.type { ::nix::ParserLocation }
%define api.pure %define api.namespace { ::nix::parser }
%define api.parser.class { BisonParser }
%locations %locations
%define parse.error verbose %define parse.error verbose
%defines %defines
@ -26,19 +28,12 @@
#include "nix/expr/eval-settings.hh" #include "nix/expr/eval-settings.hh"
#include "nix/expr/parser-state.hh" #include "nix/expr/parser-state.hh"
// Bison seems to have difficulty growing the parser stack when using C++ with #define YY_DECL \
// a custom location type. This undocumented macro tells Bison that our int yylex( \
// location type is "trivially copyable" in C++-ese, so it is safe to use the nix::Parser::value_type * yylval_param, \
// same memcpy macro it uses to grow the stack that it uses with its own nix::Parser::location_type * yylloc_param, \
// default location type. Without this, we get "error: memory exhausted" when yyscan_t yyscanner, \
// parsing some large Nix files. Our other options are to increase the initial nix::ParserState * state)
// stack size (200 by default) to be as large as we ever want to support (so
// that growing the stack is unnecessary), or redefine the stack-relocation
// macro ourselves (which is also undocumented).
#define YYLTYPE_IS_TRIVIAL 1
#define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParserState * state)
// For efficiency, we only track offsets; not line,column coordinates // For efficiency, we only track offsets; not line,column coordinates
# define YYLLOC_DEFAULT(Current, Rhs, N) \ # define YYLLOC_DEFAULT(Current, Rhs, N) \
@ -78,24 +73,30 @@ Expr * parseExprFromBuf(
%{ %{
#include "parser-tab.hh" /* The parser is very performance sensitive and loses out on a lot
#include "lexer-tab.hh" of performance even with basic stdlib assertions. Since those don't
affect ABI we can disable those just for this file. */
#if defined(_GLIBCXX_ASSERTIONS) && !defined(_GLIBCXX_DEBUG)
#undef _GLIBCXX_ASSERTIONS
#endif
#include "parser-scanner-decls.hh"
YY_DECL; YY_DECL;
using namespace nix; using namespace nix;
#define CUR_POS state->at(yyloc) #define CUR_POS state->at(yylhs.location)
void parser::BisonParser::error(const location_type &loc_, const std::string &error)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
{ {
auto loc = loc_;
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")) {
loc->beginOffset = loc->endOffset; loc.beginOffset = loc.endOffset;
} }
throw ParseError({ throw ParseError({
.msg = HintFmt(error), .msg = HintFmt(error),
.pos = state->positions[state->at(*loc)] .pos = state->positions[state->at(loc)]
}); });
} }
@ -182,7 +183,7 @@ start: expr {
state->result = $1; state->result = $1;
// This parser does not use yynerrs; suppress the warning. // This parser does not use yynerrs; suppress the warning.
(void) yynerrs; (void) yynerrs_;
}; };
expr: expr_function; expr: expr_function;
@ -563,7 +564,8 @@ Expr * parseExprFromBuf(
Finally _destroy([&] { yylex_destroy(scanner); }); Finally _destroy([&] { yylex_destroy(scanner); });
yy_scan_buffer(text, length, scanner); yy_scan_buffer(text, length, scanner);
yyparse(scanner, &state); Parser parser(scanner, &state);
parser.parse();
return state.result; return state.result;
} }