1
0
mirror of https://git.savannah.gnu.org/git/gperf.git synced 2025-12-02 21:19:24 +00:00

Implement % declarations.

This commit is contained in:
Bruno Haible
2003-02-10 14:21:58 +00:00
parent ef37a53d73
commit 6202aaadb1
8 changed files with 721 additions and 90 deletions

View File

@@ -1,5 +1,35 @@
2002-11-16 Bruno Haible <bruno@clisp.org> 2002-11-16 Bruno Haible <bruno@clisp.org>
* src/options.h (Options::get_slot_name): Renamed from
Options::get_key_name.
(Options::set, Options::set_language, Options::set_total_switches,
Options::set_function_name, Options::set_slot_name,
Options::set_class_name, Options::set_hash_name,
Options::set_wordlist_name, Options::set_delimiters): New method
declarations.
(Options::_language): New field.
(Options::_slot_name): Renamed from Options::_key_name.
* src/options.icc (Options::set): New method.
(Options::get_slot_name): Renamed from Options::get_key_name.
* src/options.cc (DEFAULT_FUNCTION_NAME): Renamed from DEFAULT_NAME.
(DEFAULT_SLOT_NAME): Renamed from DEFAULT_NAME.
(Options::Options): Initialize _language. Update.
(Options::~Options): Update.
(Options::set_language, Options::set_total_switches,
Options::set_function_name, Options::set_slot_name,
Options::set_class_name, Options::set_hash_name,
Options::set_wordlist_name, Options::set_delimiters): New methods.
(Options::parse_options): Call set_language. Update.
* src/input.cc (is_declaration, is_declaration_with_arg,
is_define_declaration): New functions.
(Input::read_input): Accept %DECL declarations.
* src/output.cc (Output::output_lookup_function_body): Update.
* doc/gperf.texi (Declarations): Add new subnodes.
(User-supplied Struct, Gperf Declarations, C Code Inclusion): New
nodes.
(Keywords, Output Format, Binary Strings, Options): Mention %
declarations as being equivalent to the command line options.
* src/options.cc (Options::long_usage): Rename options -H, -N, -l, -G. * src/options.cc (Options::long_usage): Rename options -H, -N, -l, -G.
(long_options): Add --hash-function-name, --lookup-function-name, (long_options): Add --hash-function-name, --lookup-function-name,
--compare-lengths. --compare-lengths.

18
NEWS
View File

@@ -9,6 +9,24 @@ New in 2.8:
--compare-strlen --> --compare-lengths --compare-strlen --> --compare-lengths
--global --> --global-table --global --> --global-table
The older variants are still supported for backward compatibility. The older variants are still supported for backward compatibility.
* The following options can now be specified inside the input file:
%delimiters=DELIMITER-LIST
%struct-type
%language=LANGUAGE-NAME
%define slot-name NAME
%define hash-function-name NAME
%define lookup-function-name NAME
%define class-name NAME
%7bit
%compare-lengths
%compare-strncmp
%readonly-tables
%enum
%includes
%global-table
%define word-array-name NAME
%switch=COUNT
%omit-struct-type
* If the input file is given by name, the output file will now contain * If the input file is given by name, the output file will now contain
#line directives referring to the input file. #line directives referring to the input file.
* Bug fixes. * Bug fixes.

View File

@@ -7,7 +7,7 @@
@c some day we should @include version.texi instead of defining @c some day we should @include version.texi instead of defining
@c these values at hand. @c these values at hand.
@set UPDATED 12 November 2002 @set UPDATED 16 November 2002
@set EDITION 2.7.2 @set EDITION 2.7.2
@set VERSION 2.7.2 @set VERSION 2.7.2
@c --------------------- @c ---------------------
@@ -118,10 +118,16 @@ High-Level Description of GNU @code{gperf}
Input Format to @code{gperf} Input Format to @code{gperf}
* Declarations:: @code{struct} Declarations and C Code Inclusion. * Declarations:: Declarations.
* Keywords:: Format for Keyword Entries. * Keywords:: Format for Keyword Entries.
* Functions:: Including Additional C Functions. * Functions:: Including Additional C Functions.
Declarations
* User-supplied Struct:: Specifying keywords with attributes.
* Gperf Declarations:: Embedding command line options in the input.
* C Code Inclusion:: Including C declarations and definitions.
Invoking @code{gperf} Invoking @code{gperf}
* Input Details:: Options that affect Interpretation of the Input File * Input Details:: Options that affect Interpretation of the Input File
@@ -314,27 +320,54 @@ functions
@end group @end group
@end example @end example
@emph{Unlike} @code{flex} or @code{bison}, all sections of @emph{Unlike} @code{flex} or @code{bison}, the declarations section and
@code{gperf}'s input are optional. The following sections describe the the functions section are optional. The following sections describe the
input format for each section. input format for each section.
@menu @menu
* Declarations:: @code{struct} Declarations and C Code Inclusion. * Declarations:: Declarations.
* Keywords:: Format for Keyword Entries. * Keywords:: Format for Keyword Entries.
* Functions:: Including Additional C Functions. * Functions:: Including Additional C Functions.
@end menu @end menu
It is possible to omit the declaration section entirely, if the @samp{-t}
option is not given. In this case the input file begins directly with the
first keyword line, e.g.:
@example
@group
january
february
march
april
...
@end group
@end example
@node Declarations, Keywords, Input Format, Input Format @node Declarations, Keywords, Input Format, Input Format
@subsection @code{struct} Declarations and C Code Inclusion @subsection Declarations
The keyword input file optionally contains a section for including The keyword input file optionally contains a section for including
arbitrary C declarations and definitions, as well as provisions for arbitrary C declarations and definitions, @code{gperf} declarations that
providing a user-supplied @code{struct}. If the @samp{-t} option act like command-line options, as well as for providing a user-supplied
@code{struct}.
@menu
* User-supplied Struct:: Specifying keywords with attributes.
* Gperf Declarations:: Embedding command line options in the input.
* C Code Inclusion:: Including C declarations and definitions.
@end menu
@node User-supplied Struct, Gperf Declarations, Declarations, Declarations
@subsubsection User-supplied @code{struct}
If the @samp{-t} option (or, equivalently, the @samp{%struct-type} declaration)
@emph{is} enabled, you @emph{must} provide a C @code{struct} as the last @emph{is} enabled, you @emph{must} provide a C @code{struct} as the last
component in the declaration section from the input file. The first component in the declaration section from the input file. The first
field in this struct must be a @code{char *} or @code{const char *} field in this struct must be a @code{char *} or @code{const char *}
identifier called @samp{name}, although it is possible to modify this identifier called @samp{name}, although it is possible to modify this
field's name with the @samp{-K} option described below. field's name with the @samp{-K} option (or, equivalently, the
@samp{%define slot-name}) described below.
Here is a simple example, using months of the year and their attributes as Here is a simple example, using months of the year and their attributes as
input: input:
@@ -364,6 +397,174 @@ other fields are a pair of consecutive percent signs, @samp{%%},
appearing left justified in the first column, as in the UNIX utility appearing left justified in the first column, as in the UNIX utility
@code{lex}. @code{lex}.
@node Gperf Declarations, C Code Inclusion, User-supplied Struct, Declarations
@subsubsection Gperf Declarations
The declaration section can contain @code{gperf} declarations. They
influence the way @code{gperf} works, like command line options do.
In fact, every such declaration is equivalent to a command line option.
There are three forms of declarations:
@enumerate
@item
Declarations without argument, like @samp{%compare-lengths}.
@item
Declarations with an argument, like @samp{%switch=@var{count}}.
@item
Declarations of names of entities in the output file, like
@samp{%define lookup-function-name @var{name}}.
@end enumerate
When a declaration is given both in the input file and as a command line
option, the command-line option's value prevails.
The following @code{gperf} declarations are available.
@table @samp
@item %delimiters=@var{delimiter-list}
@cindex @samp{%delimiters}
Allows you to provide a string containing delimiters used to
separate keywords from their attributes. The default is ",". This
option is essential if you want to use keywords that have embedded
commas or newlines.
@item %struct-type
@cindex @samp{%struct-type}
Allows you to include a @code{struct} type declaration for generated
code; see above for an example.
@item %language=@var{language-name}
@cindex @samp{%language}
Instructs @code{gperf} to generate code in the language specified by the
option's argument. Languages handled are currently:
@table @samp
@item KR-C
Old-style K&R C. This language is understood by old-style C compilers and
ANSI C compilers, but ANSI C compilers may flag warnings (or even errors)
because of lacking @samp{const}.
@item C
Common C. This language is understood by ANSI C compilers, and also by
old-style C compilers, provided that you @code{#define const} to empty
for compilers which don't know about this keyword.
@item ANSI-C
ANSI C. This language is understood by ANSI C compilers and C++ compilers.
@item C++
C++. This language is understood by C++ compilers.
@end table
The default is C.
@item %define slot-name @var{name}
@cindex @samp{%define slot-name}
This option is only useful when option @samp{-t} (or, equivalently, the
@samp{%struct-type} declaration) has been given.
By default, the program assumes the structure component identifier for
the keyword is @samp{name}. This option allows an arbitrary choice of
identifier for this component, although it still must occur as the first
field in your supplied @code{struct}.
@item %define hash-function-name @var{name}
@cindex @samp{%define hash-function-name}
Allows you to specify the name for the generated hash function. Default
name is @samp{hash}. This option permits the use of two hash tables in
the same file.
@item %define lookup-function-name @var{name}
@cindex @samp{%define lookup-function-name}
Allows you to specify the name for the generated lookup function.
Default name is @samp{in_word_set}. This option permits multiple
generated hash functions to be used in the same application.
@item %define class-name @var{name}
@cindex @samp{%define class-name}
This option is only useful when option @samp{-L C++} (or, equivalently,
the @samp{%language=C++} declaration) has been given. It
allows you to specify the name of generated C++ class. Default name is
@code{Perfect_Hash}.
@item %7bit
@cindex @samp{%7bit}
This option specifies that all strings that will be passed as arguments
to the generated hash function and the generated lookup function will
solely consist of 7-bit ASCII characters (bytes in the range 0..127).
(Note that the ANSI C functions @code{isalnum} and @code{isgraph} do
@emph{not} guarantee that a byte is in this range. Only an explicit
test like @samp{c >= 'A' && c <= 'Z'} guarantees this.)
@item %compare-lengths
@cindex @samp{%compare-lengths}
Compare keyword lengths before trying a string comparison. This option
is mandatory for binary comparisons (@pxref{Binary Strings}). It also might
cut down on the number of string comparisons made during the lookup, since
keywords with different lengths are never compared via @code{strcmp}.
However, using @samp{%compare-lengths} might greatly increase the size of the
generated C code if the lookup table range is large (which implies that
the switch option @samp{-S} or @samp{%switch} is not enabled), since the length
table contains as many elements as there are entries in the lookup table.
@item %compare-strncmp
@cindex @samp{%compare-strncmp}
Generates C code that uses the @code{strncmp} function to perform
string comparisons. The default action is to use @code{strcmp}.
@item %readonly-tables
@cindex @samp{%readonly-tables}
Makes the contents of all generated lookup tables constant, i.e.,
``readonly''. Many compilers can generate more efficient code for this
by putting the tables in readonly memory.
@item %enum
@cindex @samp{%enum}
Define constant values using an enum local to the lookup function rather
than with #defines. This also means that different lookup functions can
reside in the same file. Thanks to James Clark @code{<jjc@@ai.mit.edu>}.
@item %includes
@cindex @samp{%includes}
Include the necessary system include file, @code{<string.h>}, at the
beginning of the code. By default, this is not done; the user must
include this header file himself to allow compilation of the code.
@item %global-table
@cindex @samp{%global-table}
Generate the static table of keywords as a static global variable,
rather than hiding it inside of the lookup function (which is the
default behavior).
@item %define word-array-name @var{name}
@cindex @samp{%define word-array-name}
Allows you to specify the name for the generated array containing the
hash table. Default name is @samp{wordlist}. This option permits the
use of two hash tables in the same file, even when the option @samp{-G}
(or, equivalently, the @samp{%global-table} declaration) is given.
@item %switch=@var{count}
@cindex @samp{%switch}
Causes the generated C code to use a @code{switch} statement scheme,
rather than an array lookup table. This can lead to a reduction in both
time and space requirements for some input files. The argument to this
option determines how many @code{switch} statements are generated. A
value of 1 generates 1 @code{switch} containing all the elements, a
value of 2 generates 2 tables with 1/2 the elements in each
@code{switch}, etc. This is useful since many C compilers cannot
correctly generate code for large @code{switch} statements. This option
was inspired in part by Keith Bostic's original C program.
@item %omit-struct-type
@cindex @samp{%omit-struct-type}
Prevents the transfer of the type declaration to the output file. Use
this option if the type is already defined elsewhere.
@end table
@node C Code Inclusion, , Gperf Declarations, Declarations
@subsubsection C Code Inclusion
@cindex @samp{%@{} @cindex @samp{%@{}
@cindex @samp{%@}} @cindex @samp{%@}}
Using a syntax similar to GNU utilities @code{flex} and @code{bison}, it Using a syntax similar to GNU utilities @code{flex} and @code{bison}, it
@@ -389,20 +590,6 @@ march, 3, 31, 31
@end group @end group
@end example @end example
It is possible to omit the declaration section entirely, if the @samp{-t}
option is not given. In this case
the input file begins directly with the first keyword line, e.g.:
@example
@group
january
february
march
april
...
@end group
@end example
@node Keywords, Functions, Declarations, Input Format @node Keywords, Functions, Declarations, Input Format
@subsection Format for Keyword Entries @subsection Format for Keyword Entries
@@ -446,7 +633,8 @@ Additional fields may optionally follow the leading keyword. Fields
should be separated by commas, and terminate at the end of line. What should be separated by commas, and terminate at the end of line. What
these fields mean is entirely up to you; they are used to initialize the these fields mean is entirely up to you; they are used to initialize the
elements of the user-defined @code{struct} provided by you in the elements of the user-defined @code{struct} provided by you in the
declaration section. If the @samp{-t} option is @emph{not} enabled declaration section. If the @samp{-t} option (or, equivalently, the
@samp{%struct-type} declaration) is @emph{not} enabled
these fields are simply ignored. All previous examples except the last these fields are simply ignored. All previous examples except the last
one contain keyword attributes. one contain keyword attributes.
@@ -479,18 +667,21 @@ local static array. The associated values table is constructed
internally by @code{gperf} and later output as a static local C array internally by @code{gperf} and later output as a static local C array
called @samp{hash_table}. The relevant selected positions (i.e. indices called @samp{hash_table}. The relevant selected positions (i.e. indices
into @var{str}) are specified via the @samp{-k} option when running into @var{str}) are specified via the @samp{-k} option when running
@code{gperf}, as detailed in the @emph{Options} section below(@pxref{Options}). @code{gperf}, as detailed in the @emph{Options} section below (@pxref{Options}).
@end deftypefun @end deftypefun
@deftypefun {} in_word_set (const char * @var{str}, unsigned int @var{len}) @deftypefun {} in_word_set (const char * @var{str}, unsigned int @var{len})
If @var{str} is in the keyword set, returns a pointer to that If @var{str} is in the keyword set, returns a pointer to that
keyword. More exactly, if the option @samp{-t} was given, it returns keyword. More exactly, if the option @samp{-t} (or, equivalently, the
@samp{%struct-type} declaration) was given, it returns
a pointer to the matching keyword's structure. Otherwise it returns a pointer to the matching keyword's structure. Otherwise it returns
@code{NULL}. @code{NULL}.
@end deftypefun @end deftypefun
If the option @samp{-c} is not used, @var{str} must be a NUL terminated If the option @samp{-c} (or, equivalently, the @samp{%compare-strncmp}
string of exactly length @var{len}. If @samp{-c} is used, @var{str} must declaration) is not used, @var{str} must be a NUL terminated
string of exactly length @var{len}. If @samp{-c} (or, equivalently, the
@samp{%compare-strncmp} declaration) is used, @var{str} must
simply be an array of @var{len} bytes and does not need to be NUL simply be an array of @var{len} bytes and does not need to be NUL
terminated. terminated.
@@ -512,7 +703,9 @@ degree of optimization, this method often results in smaller and faster
code. code.
@end table @end table
If the @samp{-t} and @samp{-S} options are omitted, the default action If the @samp{-t} and @samp{-S} options (or, equivalently, the
@samp{%struct-type} and @samp{%switch} declarations) are omitted, the default
action
is to generate a @code{char *} array containing the keywords, together with is to generate a @code{char *} array containing the keywords, together with
additional empty strings used for padding the array. By experimenting additional empty strings used for padding the array. By experimenting
with the various input and output options, and timing the resulting C with the various input and output options, and timing the resulting C
@@ -529,17 +722,20 @@ that the keywords in the input file must not contain NUL bytes,
and the @var{str} argument passed to @code{hash} or @code{in_word_set} and the @var{str} argument passed to @code{hash} or @code{in_word_set}
must be NUL terminated and have exactly length @var{len}. must be NUL terminated and have exactly length @var{len}.
If option @samp{-c} is used, then the @var{str} argument does not need If option @samp{-c} (or, equivalently, the @samp{%compare-strncmp}
declaration) is used, then the @var{str} argument does not need
to be NUL terminated. The code generated by @code{gperf} will only to be NUL terminated. The code generated by @code{gperf} will only
access the first @var{len}, not @var{len+1}, bytes starting at @var{str}. access the first @var{len}, not @var{len+1}, bytes starting at @var{str}.
However, the keywords in the input file still must not contain NUL However, the keywords in the input file still must not contain NUL
bytes. bytes.
If option @samp{-l} is used, then the hash table performs binary If option @samp{-l} (or, equivalently, the @samp{%compare-lengths}
declaration) is used, then the hash table performs binary
comparison. The keywords in the input file may contain NUL bytes, comparison. The keywords in the input file may contain NUL bytes,
written in string syntax as @code{\000} or @code{\x00}, and the code written in string syntax as @code{\000} or @code{\x00}, and the code
generated by @code{gperf} will treat NUL like any other byte. generated by @code{gperf} will treat NUL like any other byte.
Also, in this case the @samp{-c} option is ignored. Also, in this case the @samp{-c} option (or, equivalently, the
@samp{%compare-strncmp} declaration) is ignored.
@node Options, Bugs, Description, Top @node Options, Bugs, Description, Top
@chapter Invoking @code{gperf} @chapter Invoking @code{gperf}
@@ -572,11 +768,14 @@ or if it is @samp{-}.
@node Input Details, Output Language, Output File, Options @node Input Details, Output Language, Output File, Options
@section Options that affect Interpretation of the Input File @section Options that affect Interpretation of the Input File
These options are also available as declarations in the input file
(@pxref{Gperf Declarations}).
@table @samp @table @samp
@item -e @var{keyword-delimiter-list} @item -e @var{keyword-delimiter-list}
@itemx --delimiters=@var{keyword-delimiter-list} @itemx --delimiters=@var{keyword-delimiter-list}
@cindex Delimiters @cindex Delimiters
Allows the user to provide a string containing delimiters used to Allows you to provide a string containing delimiters used to
separate keywords from their attributes. The default is ",". This separate keywords from their attributes. The default is ",". This
option is essential if you want to use keywords that have embedded option is essential if you want to use keywords that have embedded
commas or newlines. One useful trick is to use -e'TAB', where TAB is commas or newlines. One useful trick is to use -e'TAB', where TAB is
@@ -595,6 +794,9 @@ Modula 3 and JavaScript reserved words are distributed with this release.
@node Output Language, Output Details, Input Details, Options @node Output Language, Output Details, Input Details, Options
@section Options to specify the Language for the Output Code @section Options to specify the Language for the Output Code
These options are also available as declarations in the input file
(@pxref{Gperf Declarations}).
@table @samp @table @samp
@item -L @var{generated-language-name} @item -L @var{generated-language-name}
@itemx --language=@var{generated-language-name} @itemx --language=@var{generated-language-name}
@@ -633,20 +835,25 @@ This option is supported for compatibility with previous releases of
@node Output Details, Algorithmic Details, Output Language, Options @node Output Details, Algorithmic Details, Output Language, Options
@section Options for fine tuning Details in the Output Code @section Options for fine tuning Details in the Output Code
Most of these options are also available as declarations in the input file
(@pxref{Gperf Declarations}).
@table @samp @table @samp
@item -K @var{slot-name} @item -K @var{slot-name}
@itemx --slot-name=@var{slot-name} @itemx --slot-name=@var{slot-name}
@cindex Slot name @cindex Slot name
This option is only useful when option @samp{-t} has been given. This option is only useful when option @samp{-t} (or, equivalently, the
@samp{%struct-type} declaration) has been given.
By default, the program assumes the structure component identifier for By default, the program assumes the structure component identifier for
the keyword is @samp{slot-name}. This option allows an arbitrary choice of the keyword is @samp{name}. This option allows an arbitrary choice of
identifier for this component, although it still must occur as the first identifier for this component, although it still must occur as the first
field in your supplied @code{struct}. field in your supplied @code{struct}.
@item -F @var{initializers} @item -F @var{initializers}
@itemx --initializer-suffix=@var{initializers} @itemx --initializer-suffix=@var{initializers}
@cindex Initializers @cindex Initializers
This option is only useful when option @samp{-t} has been given. This option is only useful when option @samp{-t} (or, equivalently, the
@samp{%struct-type} declaration) has been given.
It permits to specify initializers for the structure members following It permits to specify initializers for the structure members following
@var{slot-name} in empty hash table entries. The list of initializers @var{slot-name} in empty hash table entries. The list of initializers
should start with a comma. By default, the emitted code will should start with a comma. By default, the emitted code will
@@ -661,14 +868,14 @@ the same file.
@item -N @var{lookup-function-name} @item -N @var{lookup-function-name}
@itemx --lookup-function-name=@var{lookup-function-name} @itemx --lookup-function-name=@var{lookup-function-name}
Allows you to specify the name for the generated lookup function. Allows you to specify the name for the generated lookup function.
Default name is @samp{in_word_set}. This option permits completely Default name is @samp{in_word_set}. This option permits multiple
automatic generation of perfect hash functions, especially when multiple generated hash functions to be used in the same application.
generated hash functions are used in the same application.
@item -Z @var{class-name} @item -Z @var{class-name}
@itemx --class-name=@var{class-name} @itemx --class-name=@var{class-name}
@cindex Class name @cindex Class name
This option is only useful when option @samp{-L C++} has been given. It This option is only useful when option @samp{-L C++} (or, equivalently,
the @samp{%language=C++} declaration) has been given. It
allows you to specify the name of generated C++ class. Default name is allows you to specify the name of generated C++ class. Default name is
@code{Perfect_Hash}. @code{Perfect_Hash}.
@@ -691,8 +898,8 @@ cut down on the number of string comparisons made during the lookup, since
keywords with different lengths are never compared via @code{strcmp}. keywords with different lengths are never compared via @code{strcmp}.
However, using @samp{-l} might greatly increase the size of the However, using @samp{-l} might greatly increase the size of the
generated C code if the lookup table range is large (which implies that generated C code if the lookup table range is large (which implies that
the switch option @samp{-S} is not enabled), since the length table the switch option @samp{-S} or @samp{%switch} is not enabled), since the length
contains as many elements as there are entries in the lookup table. table contains as many elements as there are entries in the lookup table.
@item -c @item -c
@itemx --compare-strncmp @itemx --compare-strncmp
@@ -729,7 +936,7 @@ default behavior).
Allows you to specify the name for the generated array containing the Allows you to specify the name for the generated array containing the
hash table. Default name is @samp{wordlist}. This option permits the hash table. Default name is @samp{wordlist}. This option permits the
use of two hash tables in the same file, even when the option @samp{-G} use of two hash tables in the same file, even when the option @samp{-G}
is given. (or, equivalently, the @samp{%global-table} declaration) is given.
@item -S @var{total-switch-statements} @item -S @var{total-switch-statements}
@itemx --switch=@var{total-switch-statements} @itemx --switch=@var{total-switch-statements}
@@ -836,7 +1043,8 @@ choose the best results. This increases the running time by a factor of
Provides an initial @var{value} for the associate values array. Default Provides an initial @var{value} for the associate values array. Default
is 0. Increasing the initial value helps inflate the final table size, is 0. Increasing the initial value helps inflate the final table size,
possibly leading to more time efficient keyword lookups. Note that this possibly leading to more time efficient keyword lookups. Note that this
option is not particularly useful when @samp{-S} is used. Also, option is not particularly useful when @samp{-S} (or, equivalently,
@samp{%switch}) is used. Also,
@samp{-i} is overridden when the @samp{-r} option is used. @samp{-i} is overridden when the @samp{-r} option is used.
@item -j @var{jump-value} @item -j @var{jump-value}
@@ -896,7 +1104,8 @@ values are useful for limiting the overall size of the generated hash
table, though this usually increases the number of duplicate hash table, though this usually increases the number of duplicate hash
values. values.
If `generate switch' option @samp{-S} is @emph{not} enabled, the maximum If `generate switch' option @samp{-S} (or, equivalently, @samp{%switch}) is
@emph{not} enabled, the maximum
associated value influences the static array table size, and a larger associated value influences the static array table size, and a larger
table should decrease the time required for an unsuccessful search, at table should decrease the time required for an unsuccessful search, at
the expense of extra table space. the expense of extra table space.

View File

@@ -46,6 +46,187 @@ pretty_input_file_name ()
return "(standard input)"; return "(standard input)";
} }
/* Returns true if the given line contains a "%DECL" declaration. */
static bool
is_declaration (const char *line, const char *line_end, unsigned int lineno,
const char *decl)
{
/* Skip '%'. */
line++;
/* Skip DECL. */
for (const char *d = decl; *d; d++)
{
if (!(line < line_end))
return false;
if (!(*line == *d || (*d == '-' && *line == '_')))
return false;
line++;
}
if (line < line_end
&& ((*line >= 'A' && *line <= 'Z')
|| (*line >= 'a' && *line <= 'z')
|| *line == '-' || *line == '_'))
return false;
/* OK, found DECL. */
/* Skip whitespace. */
while (line < line_end && (*line == ' ' || *line == '\t'))
line++;
/* Expect end of line. */
if (line < line_end && *line != '\n')
{
fprintf (stderr, "%s:%u: junk after declaration\n",
pretty_input_file_name (), lineno);
exit (1);
}
return true;
}
/* Tests if the given line contains a "%DECL=ARG" declaration.
If yes, it sets *ARGP to the argument, and returns true.
Otherwise, it returns false. */
static bool
is_declaration_with_arg (const char *line, const char *line_end,
unsigned int lineno,
const char *decl, char **argp)
{
/* Skip '%'. */
line++;
/* Skip DECL. */
for (const char *d = decl; *d; d++)
{
if (!(line < line_end))
return false;
if (!(*line == *d || (*d == '-' && *line == '_')))
return false;
line++;
}
if (line < line_end
&& ((*line >= 'A' && *line <= 'Z')
|| (*line >= 'a' && *line <= 'z')
|| *line == '-' || *line == '_'))
return false;
/* OK, found DECL. */
/* Skip '='. */
if (!(line < line_end && *line == '='))
{
fprintf (stderr, "%s:%u: missing argument in %%%s=ARG declaration.\n",
pretty_input_file_name (), lineno, decl);
exit (1);
}
line++;
/* The next word is the argument. */
char *arg = new char[line_end - line + 1];
char *p = arg;
while (line < line_end && !(*line == ' ' || *line == '\t' || *line == '\n'))
*p++ = *line++;
*p = '\0';
/* Skip whitespace. */
while (line < line_end && (*line == ' ' || *line == '\t'))
line++;
/* Expect end of line. */
if (line < line_end && *line != '\n')
{
fprintf (stderr, "%s:%u: junk after declaration\n",
pretty_input_file_name (), lineno);
exit (1);
}
*argp = arg;
return true;
}
/* Tests if the given line contains a "%define DECL ARG" declaration.
If yes, it sets *ARGP to the argument, and returns true.
Otherwise, it returns false. */
static bool
is_define_declaration (const char *line, const char *line_end,
unsigned int lineno,
const char *decl, char **argp)
{
/* Skip '%'. */
line++;
/* Skip "define". */
{
for (const char *d = "define"; *d; d++)
{
if (!(line < line_end))
return false;
if (!(*line == *d))
return false;
line++;
}
if (!(line < line_end && (*line == ' ' || *line == '\t')))
return false;
}
/* Skip whitespace. */
while (line < line_end && (*line == ' ' || *line == '\t'))
line++;
/* Skip DECL. */
for (const char *d = decl; *d; d++)
{
if (!(line < line_end))
return false;
if (!(*line == *d || (*d == '-' && *line == '_')))
return false;
line++;
}
if (line < line_end
&& ((*line >= 'A' && *line <= 'Z')
|| (*line >= 'a' && *line <= 'z')
|| *line == '-' || *line == '_'))
return false;
/* OK, found DECL. */
/* Skip whitespace. */
if (!(line < line_end && (*line == ' ' || *line == '\t')))
{
fprintf (stderr, "%s:%u:"
" missing argument in %%define %s ARG declaration.\n",
pretty_input_file_name (), lineno, decl);
exit (1);
}
do
line++;
while (line < line_end && (*line == ' ' || *line == '\t'));
/* The next word is the argument. */
char *arg = new char[line_end - line + 1];
char *p = arg;
while (line < line_end && !(*line == ' ' || *line == '\t' || *line == '\n'))
*p++ = *line++;
*p = '\0';
/* Skip whitespace. */
while (line < line_end && (*line == ' ' || *line == '\t'))
line++;
/* Expect end of line. */
if (line < line_end && *line != '\n')
{
fprintf (stderr, "%s:%u: junk after declaration\n",
pretty_input_file_name (), lineno);
exit (1);
}
*argp = arg;
return true;
}
/* Reads the entire input file. */ /* Reads the entire input file. */
void void
Input::read_input () Input::read_input ()
@@ -208,18 +389,18 @@ Input::read_input ()
char *struct_decl = NULL; char *struct_decl = NULL;
unsigned int *struct_decl_linenos = NULL; unsigned int *struct_decl_linenos = NULL;
unsigned int struct_decl_linecount = 0; unsigned int struct_decl_linecount = 0;
for (const char *p = declarations; p < declarations_end; ) for (const char *line = declarations; line < declarations_end; )
{ {
const char *line_end; const char *line_end;
line_end = (const char *) memchr (p, '\n', declarations_end - p); line_end = (const char *) memchr (line, '\n', declarations_end - line);
if (line_end != NULL) if (line_end != NULL)
line_end++; line_end++;
else else
line_end = declarations_end; line_end = declarations_end;
if (*p == '%') if (*line == '%')
{ {
if (p[1] == '{') if (line[1] == '{')
{ {
/* Handle %{. */ /* Handle %{. */
if (_verbatim_declarations != NULL) if (_verbatim_declarations != NULL)
@@ -231,10 +412,10 @@ Input::read_input ()
pretty_input_file_name (), lineno); pretty_input_file_name (), lineno);
exit (1); exit (1);
} }
_verbatim_declarations = p + 2; _verbatim_declarations = line + 2;
_verbatim_declarations_lineno = lineno; _verbatim_declarations_lineno = lineno;
} }
else if (p[1] == '}') else if (line[1] == '}')
{ {
/* Handle %}. */ /* Handle %}. */
if (_verbatim_declarations == NULL) if (_verbatim_declarations == NULL)
@@ -251,11 +432,11 @@ Input::read_input ()
pretty_input_file_name (), lineno); pretty_input_file_name (), lineno);
exit (1); exit (1);
} }
_verbatim_declarations_end = p; _verbatim_declarations_end = line;
/* Give a warning if the rest of the line is nonempty. */ /* Give a warning if the rest of the line is nonempty. */
bool nonempty_line = false; bool nonempty_line = false;
const char *q; const char *q;
for (q = p + 2; q < line_end; q++) for (q = line + 2; q < line_end; q++)
{ {
if (*q == '\n') if (*q == '\n')
{ {
@@ -280,9 +461,98 @@ Input::read_input ()
} }
else else
{ {
fprintf (stderr, "%s:%u: unrecognized %% directive\n", char *arg;
pretty_input_file_name (), lineno);
exit (1); if (is_declaration_with_arg (line, line_end, lineno,
"delimiters", &arg))
option.set_delimiters (arg);
else
if (is_declaration (line, line_end, lineno, "struct-type"))
option.set (TYPE);
else
if (is_declaration_with_arg (line, line_end, lineno,
"language", &arg))
option.set_language (arg);
else
if (is_define_declaration (line, line_end, lineno,
"slot-name", &arg))
option.set_slot_name (arg);
else
if (is_define_declaration (line, line_end, lineno,
"hash-function-name", &arg))
option.set_hash_name (arg);
else
if (is_define_declaration (line, line_end, lineno,
"lookup-function-name", &arg))
option.set_function_name (arg);
else
if (is_define_declaration (line, line_end, lineno,
"class-name", &arg))
option.set_class_name (arg);
else
if (is_declaration (line, line_end, lineno, "7bit"))
option.set (SEVENBIT);
else
if (is_declaration (line, line_end, lineno, "compare-lengths"))
option.set (LENTABLE);
else
if (is_declaration (line, line_end, lineno, "compare-strncmp"))
option.set (COMP);
else
if (is_declaration (line, line_end, lineno, "readonly-tables"))
option.set (CONST);
else
if (is_declaration (line, line_end, lineno, "enum"))
option.set (ENUM);
else
if (is_declaration (line, line_end, lineno, "includes"))
option.set (INCLUDE);
else
if (is_declaration (line, line_end, lineno, "global-table"))
option.set (GLOBAL);
else
if (is_define_declaration (line, line_end, lineno,
"word-array-name", &arg))
option.set_wordlist_name (arg);
else
if (is_declaration_with_arg (line, line_end, lineno,
"switch", &arg))
{
option.set_total_switches (atoi (arg));
if (option.get_total_switches () <= 0)
{
fprintf (stderr, "%s:%u: number of switches %s"
" must be a positive number\n",
pretty_input_file_name (), lineno, arg);
exit (1);
}
}
else
if (is_declaration (line, line_end, lineno, "omit-struct-type"))
option.set (NOTYPE);
else
{
fprintf (stderr, "%s:%u: unrecognized %% directive\n",
pretty_input_file_name (), lineno);
exit (1);
}
} }
} }
else if (!(_verbatim_declarations != NULL else if (!(_verbatim_declarations != NULL
@@ -290,12 +560,12 @@ Input::read_input ()
{ {
/* Append the line to struct_decl. */ /* Append the line to struct_decl. */
size_t old_len = (struct_decl ? strlen (struct_decl) : 0); size_t old_len = (struct_decl ? strlen (struct_decl) : 0);
size_t line_len = line_end - p; size_t line_len = line_end - line;
size_t new_len = old_len + line_len + 1; size_t new_len = old_len + line_len + 1;
char *new_struct_decl = new char[new_len]; char *new_struct_decl = new char[new_len];
if (old_len > 0) if (old_len > 0)
memcpy (new_struct_decl, struct_decl, old_len); memcpy (new_struct_decl, struct_decl, old_len);
memcpy (new_struct_decl + old_len, p, line_len); memcpy (new_struct_decl + old_len, line, line_len);
new_struct_decl[old_len + line_len] = '\0'; new_struct_decl[old_len + line_len] = '\0';
if (struct_decl) if (struct_decl)
delete[] struct_decl; delete[] struct_decl;
@@ -314,7 +584,7 @@ Input::read_input ()
struct_decl_linecount++; struct_decl_linecount++;
} }
lineno++; lineno++;
p = line_end; line = line_end;
} }
if (_verbatim_declarations != NULL && _verbatim_declarations_end == NULL) if (_verbatim_declarations != NULL && _verbatim_declarations_end == NULL)
{ {

View File

@@ -41,10 +41,10 @@ const char *program_name;
static const int DEFAULT_JUMP_VALUE = 5; static const int DEFAULT_JUMP_VALUE = 5;
/* Default name for generated lookup function. */ /* Default name for generated lookup function. */
static const char *const DEFAULT_NAME = "in_word_set"; static const char *const DEFAULT_FUNCTION_NAME = "in_word_set";
/* Default name for the key component. */ /* Default name for the key component. */
static const char *const DEFAULT_KEY = "name"; static const char *const DEFAULT_SLOT_NAME = "name";
/* Default struct initializer suffix. */ /* Default struct initializer suffix. */
static const char *const DEFAULT_INITIALIZER_SUFFIX = ""; static const char *const DEFAULT_INITIALIZER_SUFFIX = "";
@@ -428,14 +428,15 @@ Options::Options ()
: _option_word (C), : _option_word (C),
_input_file_name (NULL), _input_file_name (NULL),
_output_file_name (NULL), _output_file_name (NULL),
_language (NULL),
_iterations (0), _iterations (0),
_jump (DEFAULT_JUMP_VALUE), _jump (DEFAULT_JUMP_VALUE),
_initial_asso_value (0), _initial_asso_value (0),
_asso_iterations (0), _asso_iterations (0),
_total_switches (1), _total_switches (1),
_size_multiple (1), _size_multiple (1),
_function_name (DEFAULT_NAME), _function_name (DEFAULT_FUNCTION_NAME),
_key_name (DEFAULT_KEY), _slot_name (DEFAULT_SLOT_NAME),
_initializer_suffix (DEFAULT_INITIALIZER_SUFFIX), _initializer_suffix (DEFAULT_INITIALIZER_SUFFIX),
_class_name (DEFAULT_CLASS_NAME), _class_name (DEFAULT_CLASS_NAME),
_hash_name (DEFAULT_HASH_NAME), _hash_name (DEFAULT_HASH_NAME),
@@ -476,7 +477,7 @@ Options::~Options ()
"\nlookup function name = %s" "\nlookup function name = %s"
"\nhash function name = %s" "\nhash function name = %s"
"\nword list name = %s" "\nword list name = %s"
"\nkey name = %s" "\nslot name = %s"
"\ninitializer suffix = %s" "\ninitializer suffix = %s"
"\nasso_values iterations = %d" "\nasso_values iterations = %d"
"\njump value = %d" "\njump value = %d"
@@ -505,7 +506,7 @@ Options::~Options ()
_option_word & INCLUDE ? "enabled" : "disabled", _option_word & INCLUDE ? "enabled" : "disabled",
_option_word & SEVENBIT ? "enabled" : "disabled", _option_word & SEVENBIT ? "enabled" : "disabled",
_iterations, _iterations,
_function_name, _hash_name, _wordlist_name, _key_name, _function_name, _hash_name, _wordlist_name, _slot_name,
_initializer_suffix, _asso_iterations, _jump, _size_multiple, _initializer_suffix, _asso_iterations, _jump, _size_multiple,
_initial_asso_value, _delimiters, _total_switches); _initial_asso_value, _delimiters, _total_switches);
if (_option_word & ALLCHARS) if (_option_word & ALLCHARS)
@@ -528,6 +529,91 @@ Options::~Options ()
} }
/* Sets the output language, if not already set. */
void
Options::set_language (const char *language)
{
if (_language == NULL)
{
_language = language;
_option_word &= ~(KRC | C | ANSIC | CPLUSPLUS);
if (!strcmp (language, "KR-C"))
_option_word |= KRC;
else if (!strcmp (language, "C"))
_option_word |= C;
else if (!strcmp (language, "ANSI-C"))
_option_word |= ANSIC;
else if (!strcmp (language, "C++"))
_option_word |= CPLUSPLUS;
else
{
fprintf (stderr, "unsupported language option %s, defaulting to C\n",
language);
_option_word |= C;
}
}
}
/* Sets the total number of switch statements, if not already set. */
void
Options::set_total_switches (int total_switches)
{
if (!(_option_word & SWITCH))
{
_option_word |= SWITCH;
_total_switches = total_switches;
}
}
/* Sets the generated function name, if not already set. */
void
Options::set_function_name (const char *name)
{
if (_function_name == DEFAULT_FUNCTION_NAME)
_function_name = name;
}
/* Set the keyword key name, if not already set. */
void
Options::set_slot_name (const char *name)
{
if (_slot_name == DEFAULT_SLOT_NAME)
_slot_name = name;
}
/* Sets the generated class name, if not already set. */
void
Options::set_class_name (const char *name)
{
if (_class_name == DEFAULT_CLASS_NAME)
_class_name = name;
}
/* Sets the hash function name, if not already set. */
void
Options::set_hash_name (const char *name)
{
if (_hash_name == DEFAULT_HASH_NAME)
_hash_name = name;
}
/* Sets the hash table array name, if not already set. */
void
Options::set_wordlist_name (const char *name)
{
if (_wordlist_name == DEFAULT_WORDLIST_NAME)
_wordlist_name = name;
}
/* Sets the delimiters string, if not already set. */
void
Options::set_delimiters (const char *delimiters)
{
if (_delimiters == DEFAULT_DELIMITERS)
_delimiters = delimiters;
}
/* Parses the command line Options and sets appropriate flags in option_word. */ /* Parses the command line Options and sets appropriate flags in option_word. */
static const struct option long_options[] = static const struct option long_options[] =
@@ -737,7 +823,7 @@ Options::parse_options (int argc, char *argv[])
} }
case 'K': /* Make this the keyname for the keyword component field. */ case 'K': /* Make this the keyname for the keyword component field. */
{ {
_key_name = /*getopt*/optarg; _slot_name = /*getopt*/optarg;
break; break;
} }
case 'l': /* Create length table to avoid extra string compares. */ case 'l': /* Create length table to avoid extra string compares. */
@@ -747,20 +833,8 @@ Options::parse_options (int argc, char *argv[])
} }
case 'L': /* Deal with different generated languages. */ case 'L': /* Deal with different generated languages. */
{ {
_option_word &= ~(KRC | C | ANSIC | CPLUSPLUS); _language = NULL;
if (!strcmp (/*getopt*/optarg, "KR-C")) set_language (/*getopt*/optarg);
_option_word |= KRC;
else if (!strcmp (/*getopt*/optarg, "C"))
_option_word |= C;
else if (!strcmp (/*getopt*/optarg, "ANSI-C"))
_option_word |= ANSIC;
else if (!strcmp (/*getopt*/optarg, "C++"))
_option_word |= CPLUSPLUS;
else
{
fprintf (stderr, "unsupported language option %s, defaulting to C\n", /*getopt*/optarg);
_option_word |= C;
}
break; break;
} }
case 'm': /* Multiple iterations for finding good asso_values. */ case 'm': /* Multiple iterations for finding good asso_values. */
@@ -805,7 +879,8 @@ Options::parse_options (int argc, char *argv[])
case 'S': /* Generate switch statement output, rather than lookup table. */ case 'S': /* Generate switch statement output, rather than lookup table. */
{ {
_option_word |= SWITCH; _option_word |= SWITCH;
if ((_total_switches = atoi (/*getopt*/optarg)) <= 0) _total_switches = atoi (/*getopt*/optarg);
if (_total_switches <= 0)
{ {
fprintf (stderr, "number of switches %s must be a positive number\n", /*getopt*/optarg); fprintf (stderr, "number of switches %s must be a positive number\n", /*getopt*/optarg);
short_usage (stderr); short_usage (stderr);

View File

@@ -180,6 +180,8 @@ public:
/* Tests a given boolean option. Returns true if set, false otherwise. */ /* Tests a given boolean option. Returns true if set, false otherwise. */
bool operator[] (Option_Type option) const; bool operator[] (Option_Type option) const;
/* Sets a given boolean option. */
void set (Option_Type option);
/* Returns the input file name. */ /* Returns the input file name. */
const char * get_input_file_name () const; const char * get_input_file_name () const;
@@ -187,6 +189,9 @@ public:
/* Returns the output file name. */ /* Returns the output file name. */
const char * get_output_file_name () const; const char * get_output_file_name () const;
/* Sets the output language, if not already set. */
void set_language (const char *language);
/* Returns the iterations value. */ /* Returns the iterations value. */
int get_iterations () const; int get_iterations () const;
@@ -201,30 +206,44 @@ public:
/* Returns the total number of switch statements to generate. */ /* Returns the total number of switch statements to generate. */
int get_total_switches () const; int get_total_switches () const;
/* Sets the total number of switch statements, if not already set. */
void set_total_switches (int total_switches);
/* Returns the factor by which to multiply the generated table's size. */ /* Returns the factor by which to multiply the generated table's size. */
int get_size_multiple () const; int get_size_multiple () const;
/* Returns the generated function name. */ /* Returns the generated function name. */
const char * get_function_name () const; const char * get_function_name () const;
/* Sets the generated function name, if not already set. */
void set_function_name (const char *name);
/* Returns the keyword key name. */ /* Returns the keyword key name. */
const char * get_key_name () const; const char * get_slot_name () const;
/* Set the keyword key name, if not already set. */
void set_slot_name (const char *name);
/* Returns the struct initializer suffix. */ /* Returns the struct initializer suffix. */
const char * get_initializer_suffix () const; const char * get_initializer_suffix () const;
/* Returns the generated class name. */ /* Returns the generated class name. */
const char * get_class_name () const; const char * get_class_name () const;
/* Sets the generated class name, if not already set. */
void set_class_name (const char *name);
/* Returns the hash function name. */ /* Returns the hash function name. */
const char * get_hash_name () const; const char * get_hash_name () const;
/* Sets the hash function name, if not already set. */
void set_hash_name (const char *name);
/* Returns the hash table array name. */ /* Returns the hash table array name. */
const char * get_wordlist_name () const; const char * get_wordlist_name () const;
/* Sets the hash table array name, if not already set. */
void set_wordlist_name (const char *name);
/* Returns the string used to delimit keywords from other attributes. */ /* Returns the string used to delimit keywords from other attributes. */
const char * get_delimiters () const; const char * get_delimiters () const;
/* Sets the delimiters string, if not already set. */
void set_delimiters (const char *delimiters);
/* Returns key positions. /* Returns key positions.
Only to be called if !options[ALLCHARS]. */ Only to be called if !options[ALLCHARS]. */
@@ -256,6 +275,9 @@ private:
/* Name of output file. */ /* Name of output file. */
char * _output_file_name; char * _output_file_name;
/* The output language. */
const char * _language;
/* Amount to iterate when a collision occurs. */ /* Amount to iterate when a collision occurs. */
int _iterations; int _iterations;
@@ -278,7 +300,7 @@ private:
const char * _function_name; const char * _function_name;
/* Name used for keyword key. */ /* Name used for keyword key. */
const char * _key_name; const char * _slot_name;
/* Suffix for empty struct initializers. */ /* Suffix for empty struct initializers. */
const char * _initializer_suffix; const char * _initializer_suffix;

View File

@@ -125,6 +125,13 @@ Options::operator[] (Option_Type option) const
return _option_word & option; return _option_word & option;
} }
/* Sets a given boolean option. */
INLINE void
Options::set (Option_Type option)
{
_option_word |= option;
}
/* Returns the input file name. */ /* Returns the input file name. */
INLINE const char * INLINE const char *
Options::get_input_file_name () const Options::get_input_file_name () const
@@ -190,9 +197,9 @@ Options::get_function_name () const
/* Returns the keyword key name. */ /* Returns the keyword key name. */
INLINE const char * INLINE const char *
Options::get_key_name () const Options::get_slot_name () const
{ {
return _key_name; return _slot_name;
} }
/* Returns the struct initializer suffix. */ /* Returns the struct initializer suffix. */

View File

@@ -1210,7 +1210,7 @@ Output::output_lookup_function_body (const Output_Compare& comparison) const
printf ("%*s register %schar *s = ", printf ("%*s register %schar *s = ",
indent, "", const_always); indent, "", const_always);
if (option[TYPE]) if (option[TYPE])
printf ("wordptr->%s", option.get_key_name ()); printf ("wordptr->%s", option.get_slot_name ());
else else
printf ("*wordptr"); printf ("*wordptr");
printf (";\n\n" printf (";\n\n"
@@ -1241,7 +1241,7 @@ Output::output_lookup_function_body (const Output_Compare& comparison) const
printf (" {\n" printf (" {\n"
" register %schar *s = resword->%s;\n\n" " register %schar *s = resword->%s;\n\n"
" if (", " if (",
const_always, option.get_key_name ()); const_always, option.get_slot_name ());
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s")); comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
printf (")\n" printf (")\n"
" return resword;\n" " return resword;\n"
@@ -1279,7 +1279,7 @@ Output::output_lookup_function_body (const Output_Compare& comparison) const
indent, "", indent, "",
indent, "", const_always, option.get_wordlist_name ()); indent, "", const_always, option.get_wordlist_name ());
if (option[TYPE]) if (option[TYPE])
printf (".%s", option.get_key_name ()); printf (".%s", option.get_slot_name ());
printf (";\n\n" printf (";\n\n"
"%*s if (", "%*s if (",
indent, ""); indent, "");
@@ -1330,7 +1330,7 @@ Output::output_lookup_function_body (const Output_Compare& comparison) const
printf ("%*s register %schar *s = ", printf ("%*s register %schar *s = ",
indent, "", const_always); indent, "", const_always);
if (option[TYPE]) if (option[TYPE])
printf ("wordptr->%s", option.get_key_name ()); printf ("wordptr->%s", option.get_slot_name ());
else else
printf ("*wordptr"); printf ("*wordptr");
printf (";\n\n" printf (";\n\n"
@@ -1374,7 +1374,7 @@ Output::output_lookup_function_body (const Output_Compare& comparison) const
indent, "", const_always, option.get_wordlist_name ()); indent, "", const_always, option.get_wordlist_name ());
if (option[TYPE]) if (option[TYPE])
printf (".%s", option.get_key_name ()); printf (".%s", option.get_slot_name ());
printf (";\n\n" printf (";\n\n"
"%*s if (", "%*s if (",