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.