2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00
Files
spirit/doc/lex/tokens_values.qbk
Joel de Guzman 2431a80d8a spirit2 ! :)
[SVN r44360]
2008-04-13 03:02:30 +00:00

208 lines
10 KiB
Plaintext

[/==============================================================================
Copyright (C) 2001-2008 Joel de Guzman
Copyright (C) 2001-2008 Hartmut Kaiser
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
===============================================================================/]
[section About Tokens and Token Values]
As already discussed, lexical scanning is the process of analyzing the stream
of input characters and separating it into strings called tokens, most of the
time separated by whitespace. The different token types recognized by a lexical
analyzer often get assigned unique integer token identifiers (token ids). These
token ids arenormally used by the parser to identifiy the current token without
having to look at the matched string again. The __lex__ library is not
different with respect to this, as it uses the token ids as the main means of
identification of the different token types defined for a particular lexical
analyzer. However, it is different from commonly used lexical analyzers in the
sense that it returns (references to) instances of a (user defined) token class
to the user. The only real limitation posed on this token class is consequently,
that it has to carry at least the token id of the token it represents. For more
information about the interface a user defined token type has to expose please
look at the __sec_ref_lex_token__ reference. The library provides a default
token type based on the __lexertl__ library which should be sufficient in most
use cases: the __class_lexertl_token__ type. This section focusses on the
description of general features a token class may implement and how this
integrates with the other parts of the __lex__ library.
[heading The Anatomy of a Token]
It is very important to understand the difference between a token definition
(represented by the __class_token_def__ template) and a token itself (for
instance represented by the __class_lexertl_token__ template).
The token definition is used to describe the main features of a particular
token type, especially:
* to simplify the definition of a token type using a regular expression pattern
applied while matching this token type,
* to associate a token type with a particular lexer state,
* to optionally assign a token id to a token type,
* to optionally associate some code to execute whenever an instance of this
token type has been matched,
* and to optionally specify the attribute type of the token value.
The token itself is a data structure returned by the lexer iterators.
Dereferencing a lexer iterator returns a reference to the last matched token
instance. It encapsulates the part of the underlying input sequence matched by
the regular expression used during the definiton of this token type.
Incrementing the lexer iterator invokes the lexical analyzer to
match the next token by advancing the underlying input stream. The token data
structure contains at least the token id of the matched token type,
allowing to identify the matched character sequence. Optionally, the token
instance may contain a token value and/or the lexer state this token instance
was matched in. The following [link spirit.lex.tokenstructure figure] shows the
schematic structure of a token.
[fig ./images/TokenStructure.png..The structure of a token..spirit.lex.tokenstructure]
The token value and the token state may be omitted for optimization reasons,
avoiding the token to carry more data than actually required. This
configuration can be achieved by supplying appropriate template parameters
for the __class_lexertl_token__ template while defining the token type.
The lexer iterator returns the same token type for each of the different
matched token definitions. To accomodate for the possibly different token
/value/ types exposed by the various token types (token definitions), the
general type of the token value is a __boost_variant__. As a minimum (for the
default configuration) this token value variant will be configured to always
hold a __boost_iterator_range__ containing the pair of iterators pointing to
the matched input sequence for this token instance.
[note If the lexical analyzer is used in conjunction with a __qi__ parser, the
stored __boost_iterator_range__ token value will be converted to the
requested token type (parser attribute) exactly once. This happens at the
time of the first access to the token value requiring the
corresponding type conversion. The converted token value will be stored
in the __boost_variant__ replacing the initially stored iterator range.
This avoids to convert the input sequence to the token value more than
once, thus optimizing the integration of the lexer with __qi__, even
during parser backtracking.
]
Here is the template prototype of the __class_lexertl_token__ template:
template <
typename Iterator = char const*,
typename AttributeTypes = mpl::vector0<>,
typename HasState = mpl::true_
>
struct lexertl_token;
[variablelist where:
[[Iterator] [This is the type of the iterator used to access the
underlying input stream. It defaults to a plain
`char const*`.]]
[[AttributeTypes] [This is either a mpl sequence containing all
attribute types used for the token definitions or the
type `omitted`. If the mpl sequence is empty (which is
the default), all token instances will store a
`boost::iterator_range<Iterator>` pointing to the start
and the end of the matched section in the input stream.
If the type is `omitted`, the generated tokens will
contain no token value (attribute) at all.]]
[[HasState] [This is either `mpl::true_` or `mpl::false_`, allowing
to control whether the generated token instances will
contain the lexer state they were generated in. The
default is mpl::true_, so all token instances will
contain the lexer state.]]
]
Normally, during its construction, a token instance always holds the
__boost_iterator_range__ as its token value (except, if it has been defined
using the `omitted` token value type). This iterator range then is
converted in place to the requested token value type (attribute) when it is
requested for the first time.
[heading The Physiognomy of a Token Definition]
The token definitions (represented by the __class_token_def__ template) are
normally used as part of the definition of the lexical analyzer. At the same
time a token definition instance may be used as a parser component in __qi__.
The template prototype of this class is shown here:
template<
typename Attribute = unused_type,
typename Char = char
>
class token_def;
[variablelist where:
[[Attribute] [This is the type of the token value (attribute)
supported by token instances representing this token
type. This attribute type is exposed to the __qi__
library, whenever this token definition is used as a
parser component. The default attribute type is
`unused_type`, which means the token instance holds a
__boost_iterator_range__ pointing to the start
and the end of the matched section in the input stream.
If the attribute is `omitted` the token instance will
expose no token type at all. Any other type will be
used directly as the token value type.]]
[[Char] [This is the value type of the iterator for the
underlying input sequence. It defaults to `char`.]]
]
The semantics of the template parameters for the token type and the token
definition type are very similar and interdependent. As a rule of thumb you can
think of the token definition type as the means of specifying everything
related to a single specific token type (such as `identifier` or `integer`).
On the other hand the token type is used to define the general proerties of all
token instances generated by the __lex__ library.
[important If you don't list any token value types in the token type definition
declaration (resulting in the usage of the default __boost_iterator_range__
token type) everything will compile and work just fine, just a bit
less efficient. This is because the token value will be converted
from the matched input sequence every time it is requested.
But as soon as you specify at least one token value type while
defining the token type you'll have to list all value types used for
__class_token_def__ declarations in the token definition class,
otherwise compilation errors will occur.
]
[heading Examples of using __class_lexertl_token__]
Let's start with some examples. We refer to one of the __lex__ examples (for
the full source code of this example please see
[@../../example/lex/example4.cpp example4.cpp]).
[import ../example/lex/example4.cpp]
The first code snippet shows an excerpt of the token definition class, the
definition of a couple of token types. Some of the token types do not expose a
special token value (`if_`, `else_`, and `while_`). Their token value will
always hold the iterator range of the matched input sequence only. The token
definitions for the `identifier` and the integer `constant` are specialized
to expose an explicit token type each: `std::string` and `unsigned int`.
[example4_token_def]
As the parsers generated by __qi__ are fully attributed, any __qi__ parser
component needs to expose a certain type as its parser attribute. Naturally,
the __class_token_def__ exposes the token value type as its parser attribute,
enabling a smooth integration with __qi__.
The next code snippet demonstrates how the required token value types are
specified while defining the token type to use. All of the token value types
used for at least one of the token definitions have to be re-iterated for the
token definition as well.
[example4_token]
To avoid the token to have a token value at all, the special tag `omitted` can
be used: `token_def<omitted>` and `lexertl_token<base_iterator_type, omitted>`.
[endsect]