Previous: BP character constants, Up: Lexical analyzer



12.2.3 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. LALR(1) parsers would read at most one such token, but with GLR, the parser can split and consume tokens while not doing any actions. The number of such tokens is unbounded, though the relevant context can be determined by analyzing the grammar.

Reading look-ahead tokens 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. This is particularly important for local directives that are meant to have a scope as small as possible.

To solve this problem, GPC keeps those options that can be changed by directives in a linked list of struct options. There are several pointers into the list:

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 causes a new struct options to be chained to the list.

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 (bison)) 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 pointed 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.

Please 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 ones. 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. (Previously, lookup_name was such a routine, but now the lexer doesn't call it anymore.)

Please 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.