cppannotations/annotations/yo/concrete/definition.yo

131 lines
7.2 KiB
Text
Raw Normal View History

The i(declaration section) contains several sets of declarations, among
which definitions of all the tokens used in the grammar and the priorities and
associativities of the mathematical operators. Moreover, several new and
important specifications can be used here as well. Those relevant to our
current example and only available in tt(bisonc++) are discussed here. The
reader is referred to tt(bisonc++)'s man-page for a full description.
itemization(
it() bi(%baseclass-preinclude) tt(header)nl()
Use tt(header) as the pathname to the file pre-included in the
parser's base-class header. This declaration is useful in
situations where the base class header file refers to types which
might not yet be known. E.g., with ti(%union) a tt(std::string *)
field might be used. Since the class tt(std::string) might not yet
be known to the compiler once it processes the base class header
file we need a way to inform the compiler about these classes and
types. The suggested procedure is to use a pre-include header file
declaring the required types. By default tt(header) is
surrounded by double quotes (using, e.g., tt(#include "header")).
When the argument is surrounded by angle brackets tt(#include
<header>) is included. In the latter case, quotes might be
required to escape interpretation by the shell (e.g., using tt(-H
'<header>')).
it() bi(%filenames) tt(header) nl()
Defines the generic name of all generated files, unless overridden
by specific names. By default the generated files use the
class-name as the generic file name.
it() bi(%scanner) tt(header)nl()
Use tt(header) as the pathname to the file pre-included in the
parser's class header. This file should define a class
2012-02-24 16:21:11 +01:00
tt(Scanner), offering a member tt(int lex()) producing the next
token from the input stream to be analyzed by the parser generated
by tt(bisonc++). When this option is used the parser's member
2012-02-24 16:21:11 +01:00
tt(int lex()) is predefined as (assuming the default parser class
name tt(Parser) is used):
verb(
inline int Parser::lex()
{
return d_scanner.lex();
}
)
and an object tt(Scanner d_scanner) is composed into the
parser. The tt(d_scanner) object is constructed by its
default constructor. If another constructor is required, the
parser class may be provided with an appropriate (overloaded)
parser constructor after having constructed the default parser
class header file using tt(bisonc++). By default tt(header) is
surrounded by double quotes (using, e.g., tt(#include
"header")). When the argument is surrounded by angle brackets
tt(#include <header>) is included.
it() bi(%stype) tt(typename) nl()
The type of the semantic value of tokens. The specification
tt(typename) should be the name of an unstructured type (e.g.,
tt(size_t)). By default it is tt(int). See tt(YYSTYPE) in
tt(bison). It should not be used if a tt(%union) specification is
used. Within the parser class, this type may be used as
tt(STYPE).
it() bi(%union) tt(union-definition) nl()
Acts identically to the tt(bison) declaration. As with tt(bison)
this generates a union for the parser's semantic type. The union
type is named tt(STYPE). If no tt(%union) is declared, a simple
stack-type may be defined using the tt(%stype) declaration. If no
tt(%stype) declaration is used, the default stacktype (tt(int)) is
used.
)
An example of a tt(%union) declaration is:
verb(
%union
{
int i;
double d;
};
)
In pre-C++11 code a i(union) cannot contain objects as its fields, as
constructors cannot be called when a union is created. This means that
hi(string: as union member) a tt(string) cannot be a member of the
union. A tt(string *), however, em(is) a possible union member. It might also
be possible to use em(unrestricted unions) (cf. section ref(UNIONS)), having
class type objects as fields.
As an aside: the scanner does not have to know about such a union. It
2012-02-24 16:21:11 +01:00
can simply pass its scanned text to the parser through its ti(matched) member
function. For example using a statement like
verb(
2012-02-24 16:21:11 +01:00
$$.i = A2x(d_scanner.matched());
)
2012-02-24 16:21:11 +01:00
matched text is converted to a value of an appropriate type.
Tokens and non-terminals can be associated with union fields. This is
strongly advised, as it prevents type mismatches, since the compiler may then
check for type correctness. At the same time, the bison specific
variables tt($$), tt($1), tt($2), etc. may be used, rather than the full field
specification (like tt($$.i)). A non-terminal or a token may be associated
with a union field using the
tt(<fieldname>) specification. E.g.,
verb(
%token <i> INT // token association (deprecated, see below)
<d> DOUBLE
%type <i> intExpr // non-terminal association
)
In the example developed here, both the tokens and the non-terminals can
2012-02-24 16:21:11 +01:00
be associated with a union field. However, as noted before, the scanner does
not have to know about all this. In our opinion, it is cleaner to let the
scanner do just one thing: scan texts. The em(parser), knowing what the input
is all about, may then convert strings like tt("123") to an integer
value. Consequently, the association of a union field and a token is
2012-02-24 16:21:11 +01:00
discouraged. Below, while describing the grammar's rules, this is further
illustrated.
In the tt(%union) discussion the ti(%token) and ti(%type) specifications
should be noted. They are used to specify the tokens (terminal symbols) that
can be returned by the scanner, and to specify the return types of
non-terminals. Apart from tt(%token) the token declarators ti(%left),
ti(%right), and ti(%nonassoc) can be used to specify the associativity of
operators. The tokens mentioned at these indicators are interpreted as tokens
indicating operators, associating in the indicated direction. The precedence
of operators is defined by their order: the first specification has the lowest
priority. To overrule a certain precedence in a certain context ti(%prec) can
be used. As all this is standard tt(bisonc++) practice, it isn't further
elaborated here. The documentation provided with tt(bisonc++)'s distribution
should be consulted for further reference.
Here is the specification of the calculator's declaration section:
verbinclude(//DECLARATION bisonc++/parser/grammar)
In the declaration section tt(%type) specifiers are used, associating the
tt(intExpr) rule's value (see the next section) to the tt(i)-field of the
semantic-value union, and associating tt(doubleExpr)'s value to the
2012-02-24 16:21:11 +01:00
tt(d)-field. This approach, admittedly, is rather complex, as expression rules
must be included for each of the supported union types. Alternatives are
definitely possible, and are illustrated in subsequent sections (e.g., section
ref(BISONSEM)).