Node: Compiler directives internally, Previous: BP character constants, Up: Lexical analyzer



Compiler directives internally

Compiler directives are mostly handled in options.c, mostly in common with command-line options, using the definitions in lang-options.h and the tables in gpc-options.h.

A special problem is that the parser sometimes has to read tokens before they're used to decide what to do next. This is generally harmless, but if there is a compiler directive before such a look-ahead token, it would be handled apparently too early. This looks strange from the programmer's point of view – even more so since the programmer cannot easily predict when the parser needs to read ahead and when not, and therefore cannot be sure where exactly to place the directive (especially for local directives that are meant to have a scope as small as possible).

To solve this problem (and in turn give the parser more freedom for further look ahead which is useful, e.g., for a GLR parser), GPC keeps the options that can be changed by directives in a struct options. There are several pointers to such a structure:

lexer_options are the options current to the lexer. These are always the ones read most recently. Compiler directives are applied here when read. Each directive creates a new struct options which is chained in a linked list to the previous ones.

compiler_options points to the options current for the compiler, i.e. seen before the last token handled in a parser rule. To facilitate this, we abuse Bison's location tracking feature (see Locations) and refer to the options seen before a token in the token's location (yylloc). Before each grammar rule is handled, the compiler options are updated to those of the last token involved in the rules handled so far, using Bison's YYLLOC_DEFAULT feature. Actual locations, used for error messages etc., are handled the same way (according to the real purpose of Bison's location tracking), also distinct for the lexer and compiler.

Note: Tokens are not always handled in order. E.g., in 2 + 3 * 4, first 3 * 4 is evaluated, then 2 + 12, i.e., the tokens 2 and + are handled after the following tokens. To avoid jumping back in the options, we store a counter, rather than a pointer, in yyloc, so we can compare it to the current counter. This also allows us to free any struct options that compiler_options has advanced beyond because it can never go back.

Finally, the pointer co points to the current options which is lexer_options when we're in the lexer and compiler_options otherwise. All routines that use or set options refer to co, so there is no problem when they may be called both from the lexer and from other parts of the compiler (e.g., lookup_name).

Note: Some of the options are flags declared in the backend. Since we can't keep them in struct option directly, we have to copy them back and forth in activate_options. This is a little annoyance, but no real problem.