I have a YACC (Bison) grammar, a Lex (Flex) tokenizer, and a C program among which I need to share a struct
(or really any variable). Currently, I declare the actual object in the grammar file and extern
it wherever I need it (which is to say, my C source file), usually using a pointer to manipulate it. I have a shared header (and implementation) file between the C file and the grammar file with functions useful for manipulating my data structure. This works, but it feels a little uncomfortable. Is there a better way to share memory between the grammar and program?
views:
71answers:
2A header file to share the extern declaration between the source files that need it is the best way to go, usually. The major alternative is to provide 'functional access' to it - that is, some sort of 'get value' and 'set value' function (or a set of functions). This is usually overkill. Ensure you include the header in the grammar (where you define the variable_ as well as in the lexer and the other code so that inconsistencies are spotted as soon as possible.
If you want to stick to standard (POSIX) lex/yacc, then your only option is to go for global variables/functions. If you're fine with using Bison and Flex extensions, there are a couple of ways to pass variables around, which mostly involve adding extra parameters to yyparse() and yylex().
In Bison, this is accomplished through %lex-param and %parse-param.
%parse-param { struct somestruct *mystruct }
%lex-param { struct somestruct *mystruct }
In Flex, there are two different mechanisms, depending on whether you want a reentrant lexer or not. Assuming you go with the default (non-reentrant) option, you'll want to redefine YY_DECL:
%{
#define YY_DECL int yylex(struct somestruct *mystruct)
%}
In a reentrant Flex lexer, extra arguments can be added through the scanner struct that Flex carries around to keep its state. You'll want to define YY_EXTRA_TYPE; the extra data can be accessed through yyget/set_extra().