I know I can declare %parse-param {struct my_st *arg}
in a .y
file. And so yyparse()
is changed to be yyparse(struct my_st *arg)
. But how do I reference the argument in the flex rules? For example:
[0-9]+ { do_work(arg); return NUMBER; }
I want to make a reentrant parser, so I need to do this. Please help me, thanks!
You need to pass the argument through to
yylex
. That requires a modification of both the bison parser description, so that the parser callsyylex
with the desired arguments, and the flex scanner description, so that the scanner generatesyylex
with the desired parameters.Bison and flex do not communicate with each other and do not see each other's source files. However, it is normal for the scanner to
#include
the header file generated by bison, and bison allows the possibility of inserting code directly into this header file. That makes it possible to put the entire configuration into the bison file.In bison, you specify additional parameters for
yylex
using the%lex-param
directive. But you need to be aware of the additional argument(s) which would also be added to the call if you%define api.pure
.If you use bison 3.0 or more recent, you can use
as an abbreviation for
It makes sense to use a single directive (if your bison is sufficiently recent) because there is no way to insert a local variable declaration into the
yyparse
function. So the only variables which could be passed through toyylex
are global variables and parameters toyyparse
. [Note 1]Remember that it is your responsibility to declare
yylex
andyyerror
in the bison file. Even if you use%lex-param
, bison will not automatically generate a declaration foryylex
. [Note 2]Flex does normally generate a declaration for
yylex
, so you cannot simply put your declaration into the bison-generated header file and then#include
it into the scanner. However, if theYY_DECL
macro is defined, then the flex-generated scanner will not forward-declareyylex
, and it will use theYY_DECL
macro in the definition ofyylex
. You can use this feature to put the declaration ofyylex
into the bison description in a way that it will be passed through to the flex scanner.In bison, you can add declarations to the generated header using either
%code requires
sections or%code provides
sections. The difference is thatrequires
segments are earlier in the header file, beforeYYSTYPE
andYYLTYPE
have been declared. If you use a pure parser, theyylex
prototype will usually refer toYYSTYPE
(andYYLTYPE
, if you use locations), so it needs to go in a%code provides
section. In order to interface gracefully with flex, you can use theYY_DECL
macro to generate theyylex
declaration.So you might end up with something like the following: [Note 3]
file: mylanguage.y
Then you would just insert the generated header into your flex file in the normal way:
file: mylanguage.l
The
%define api.pure full
declaration in bison avoids the need for the global variablesyylval
andyylloc
. However, there are a number of other internal globals used by a flex-generated scanner; in order to make the scanner truly re-entrant, you need to add%option reentrant
to your flex file. With that option,yylex
is expected to include the parameteryyscan_t yyscanner
(as are all the other lexer-related functions defined by flex). You need to manage theyyscanner
value, so it will need to be passed throughyyparse
toyylex
as above. You also need to initialize and destroy it, as described in the flex manual. (Its name inyylex
must be preciselyyyscanner
, if you are generating theyylex
prototype usingYY_DECL
.)If you also want to pass your own context object, you could add two parameters to
yyparse
andyylex
, or you could include your context object inside theyyscan_t
object as described in the Flex manual section on Extra Data.Finally, if you use the bison pure parser API, then you need to change the way you write flex actions. Instead of assigning to the global
yylval
(eg.yylval.integer = atol(yytext);
), you need to assign through the pointer passed as an argument:yylvalp->integer = atol(yytext);
. (The name of the argument is up to you; the one I use here is the one I specified above.)Notes
Older implementations allowed you to specify the arguments to
yylex
by defining the macroYYLEX_PARAM
. This is no longer supported as of bison 3.0, so you shouldn't use it.If you use
%parse-param
, the additional parameter will also be added toyyerror
. And if you%define api-pure full
,yyerror
will also receive a location object. Your declaration ofyyerror
needs to be consistent.The
%locations
directive forces bison to generate code which stores location information for each token. I use it here because it makes the prototypes predictable. Without it, the prototypes would includeYYLTYPE
arguments only if you actually refer to a location somewhere in a semantic action. If you don't intend to use token locations, you might prefer to remove the%locations
directive and all theYYLTYPE
arguments. But usually the location information is useful.