mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-23 19:23:02 +00:00
* doc/bison.texinfo (C++ Language Interface): First stab.
(C++ Parsers): Remove.
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
2005-06-22 Akim Demaille <akim@epita.fr>
|
||||||
|
|
||||||
|
* doc/bison.texinfo (C++ Language Interface): First stab.
|
||||||
|
(C++ Parsers): Remove.
|
||||||
|
|
||||||
2005-06-22 Akim Demaille <akim@epita.fr>
|
2005-06-22 Akim Demaille <akim@epita.fr>
|
||||||
|
|
||||||
* data/lalr1.cc (yylex_): Honor %lex-param.
|
* data/lalr1.cc (yylex_): Honor %lex-param.
|
||||||
|
|||||||
@@ -117,9 +117,10 @@ Reference sections:
|
|||||||
messy for Bison to handle straightforwardly.
|
messy for Bison to handle straightforwardly.
|
||||||
* Debugging:: Understanding or debugging Bison parsers.
|
* Debugging:: Understanding or debugging Bison parsers.
|
||||||
* Invocation:: How to run Bison (to produce the parser source file).
|
* Invocation:: How to run Bison (to produce the parser source file).
|
||||||
|
* C++ Language Interface:: Creating C++ parser objects.
|
||||||
|
* FAQ:: Frequently Asked Questions
|
||||||
* Table of Symbols:: All the keywords of the Bison language are explained.
|
* Table of Symbols:: All the keywords of the Bison language are explained.
|
||||||
* Glossary:: Basic concepts are explained.
|
* Glossary:: Basic concepts are explained.
|
||||||
* FAQ:: Frequently Asked Questions
|
|
||||||
* Copying This Manual:: License for copying this manual.
|
* Copying This Manual:: License for copying this manual.
|
||||||
* Index:: Cross-references to the text.
|
* Index:: Cross-references to the text.
|
||||||
|
|
||||||
@@ -292,12 +293,32 @@ Invoking Bison
|
|||||||
* Option Cross Key:: Alphabetical list of long options.
|
* Option Cross Key:: Alphabetical list of long options.
|
||||||
* Yacc Library:: Yacc-compatible @code{yylex} and @code{main}.
|
* Yacc Library:: Yacc-compatible @code{yylex} and @code{main}.
|
||||||
|
|
||||||
|
C++ Language Interface
|
||||||
|
|
||||||
|
* C++ Parsers:: The interface to generate C++ parser classes
|
||||||
|
* A Complete C++ Example:: Demonstrating their use
|
||||||
|
|
||||||
|
C++ Parsers
|
||||||
|
|
||||||
|
* C++ Bison Interface:: Asking for C++ parser generation
|
||||||
|
* C++ Semantic Values:: %union vs. C++
|
||||||
|
* C++ Location Values:: The position and location classes
|
||||||
|
* C++ Parser Interface:: Instantiating and running the parser
|
||||||
|
* C++ Scanner Interface:: Exchanges between yylex and parse
|
||||||
|
|
||||||
|
A Complete C++ Example
|
||||||
|
|
||||||
|
* Calc++ --- C++ Calculator:: The specifications
|
||||||
|
* Calc++ Parsing Driver:: An active parsing context
|
||||||
|
* Calc++ Parser:: A parser class
|
||||||
|
* Calc++ Scanner:: A pure C++ Flex scanner
|
||||||
|
* Calc++ Top Level:: Conducting the band
|
||||||
|
|
||||||
Frequently Asked Questions
|
Frequently Asked Questions
|
||||||
|
|
||||||
* Parser Stack Overflow:: Breaking the Stack Limits
|
* Parser Stack Overflow:: Breaking the Stack Limits
|
||||||
* How Can I Reset the Parser:: @code{yyparse} Keeps some State
|
* How Can I Reset the Parser:: @code{yyparse} Keeps some State
|
||||||
* Strings are Destroyed:: @code{yylval} Loses Track of Strings
|
* Strings are Destroyed:: @code{yylval} Loses Track of Strings
|
||||||
* C++ Parsers:: Compiling Parsers with C++ Compilers
|
|
||||||
* Implementing Gotos/Loops:: Control Flow in the Calculator
|
* Implementing Gotos/Loops:: Control Flow in the Calculator
|
||||||
|
|
||||||
Copying This Manual
|
Copying This Manual
|
||||||
@@ -6737,7 +6758,650 @@ If you use the Yacc library's @code{main} function, your
|
|||||||
int yyparse (void);
|
int yyparse (void);
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@c ================================================= Invoking Bison
|
@c ================================================= C++ Bison
|
||||||
|
|
||||||
|
@node C++ Language Interface
|
||||||
|
@chapter C++ Language Interface
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* C++ Parsers:: The interface to generate C++ parser classes
|
||||||
|
* A Complete C++ Example:: Demonstrating their use
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node C++ Parsers
|
||||||
|
@section C++ Parsers
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* C++ Bison Interface:: Asking for C++ parser generation
|
||||||
|
* C++ Semantic Values:: %union vs. C++
|
||||||
|
* C++ Location Values:: The position and location classes
|
||||||
|
* C++ Parser Interface:: Instantiating and running the parser
|
||||||
|
* C++ Scanner Interface:: Exchanges between yylex and parse
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node C++ Bison Interface
|
||||||
|
@subsection C++ Bison Interface
|
||||||
|
@c - %skeleton "lalr1.cc"
|
||||||
|
@c - Always pure
|
||||||
|
@c - initial action
|
||||||
|
|
||||||
|
The C++ parser LALR(1) skeleton is named @file{lalr1.cc}. To select
|
||||||
|
it, you may either pass the option @option{--skeleton=lalr1.cc} to
|
||||||
|
Bison, or include the directive @samp{%skeleton "lalr1.cc"} in the
|
||||||
|
grammar preamble. When run, @command{bison} will create several
|
||||||
|
files:
|
||||||
|
@table @file
|
||||||
|
@item position.hh
|
||||||
|
@itemx location.hh
|
||||||
|
The definition of the classes @code{position} and @code{location},
|
||||||
|
used for location tracking. @xref{C++ Location Values}.
|
||||||
|
|
||||||
|
@item stack.hh
|
||||||
|
An auxiliary class @code{stack} used by the parser.
|
||||||
|
|
||||||
|
@item @var{filename}.hh
|
||||||
|
@itemx @var{filename}.cc
|
||||||
|
The declaration and implementation of the C++ parser class.
|
||||||
|
@var{filename} is the name of the output file. It follows the same
|
||||||
|
rules as with regular C parsers.
|
||||||
|
|
||||||
|
Note that @file{@var{filename}.hh} is @emph{mandatory}, the C++ cannot
|
||||||
|
work without the parser class declaration. Therefore, you must either
|
||||||
|
pass @option{-d}/@option{--defines} to @command{bison}, or use the
|
||||||
|
@samp{%defines} directive.
|
||||||
|
@end table
|
||||||
|
|
||||||
|
All these files are documented using Doxygen; run @command{doxygen}
|
||||||
|
for a complete and accurate documentation.
|
||||||
|
|
||||||
|
@node C++ Semantic Values
|
||||||
|
@subsection C++ Semantic Values
|
||||||
|
@c - No objects in unions
|
||||||
|
@c - YSTYPE
|
||||||
|
@c - Printer and destructor
|
||||||
|
|
||||||
|
The @code{%union} directive works as for C, see @ref{Union Decl, ,The
|
||||||
|
Collection of Value Types}. In particular it produces a genuine
|
||||||
|
@code{union}@footnote{In the future techniques to allow complex types
|
||||||
|
within pseudo-unions (variants) might be implemented to alleviate
|
||||||
|
these issues.}, which have a few specific features in C++.
|
||||||
|
@itemize @minus
|
||||||
|
@item
|
||||||
|
The name @code{YYSTYPE} also denotes @samp{union YYSTYPE}. You may
|
||||||
|
forward declare it just with @samp{union YYSTYPE;}.
|
||||||
|
@item
|
||||||
|
Non POD (Plain Old Data) types cannot be used. C++ forbids any
|
||||||
|
instance of classes with constructors in unions: only @emph{pointers}
|
||||||
|
to such objects are allowed.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
Because objects have to be stored via pointers, memory is not
|
||||||
|
reclaimed automatically: using the @code{%destructor} directive is the
|
||||||
|
only means to avoid leaks. @xref{Destructor Decl, , Freeing Discarded
|
||||||
|
Symbols}.
|
||||||
|
|
||||||
|
|
||||||
|
@node C++ Location Values
|
||||||
|
@subsection C++ Location Values
|
||||||
|
@c - %locations
|
||||||
|
@c - class Position
|
||||||
|
@c - class Location
|
||||||
|
@c - %define "filename_type" "const symbol::Symbol"
|
||||||
|
|
||||||
|
When the directive @code{%locations} is used, the C++ parser supports
|
||||||
|
location tracking, see @ref{Locations, , Locations Overview}. Two
|
||||||
|
auxiliary classes define a @code{position}, a single point in a file,
|
||||||
|
and a @code{location}, a range composed of a pair of
|
||||||
|
@code{position}s (possibly spanning several files).
|
||||||
|
|
||||||
|
@deftypemethod {position} {std::string*} filename
|
||||||
|
The name of the file. It will always be handled as a pointer, the
|
||||||
|
parser will never duplicate nor deallocate it. As an experimental
|
||||||
|
feature you may change it to @samp{@var{type}*} using @samp{%define
|
||||||
|
"filename_type" "@var{type}"}.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {position} {unsigned int} line
|
||||||
|
The line, starting at 1.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {position} {unsigned int} lines (int @var{height} = 1)
|
||||||
|
Advance by @var{height} lines, resetting the column number.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {position} {unsigned int} column
|
||||||
|
The column, starting at 0.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {position} {unsigned int} columns (int @var{width} = 1)
|
||||||
|
Advance by @var{width} columns, without changing the line number.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {position} {position&} operator+= (position& @var{pos}, int @var{width})
|
||||||
|
@deftypemethodx {position} {position} operator+ (const position& @var{pos}, int @var{width})
|
||||||
|
@deftypemethodx {position} {position&} operator-= (const position& @var{pos}, int @var{width})
|
||||||
|
@deftypemethodx {position} {position} operator- (position& @var{pos}, int @var{width})
|
||||||
|
Various forms of syntactic sugar for @code{columns}.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {position} {position} operator<< (std::ostream @var{o}, const position& @var{p})
|
||||||
|
Report @var{p} on @var{o} like this:
|
||||||
|
@samp{@var{filename}:@var{line}.@var{column}}, or
|
||||||
|
@samp{@var{line}.@var{column}} if @var{filename} is null.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {location} {position} begin
|
||||||
|
@deftypemethodx {location} {position} end
|
||||||
|
The first, inclusive, position of the range, and the first beyond.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {location} {unsigned int} columns (int @var{width} = 1)
|
||||||
|
@deftypemethodx {location} {unsigned int} lines (int @var{height} = 1)
|
||||||
|
Advance the @code{end} position.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {location} {location} operator+ (const location& @var{begin}, const location& @var{end})
|
||||||
|
@deftypemethodx {location} {location} operator+ (const location& @var{begin}, int @var{width})
|
||||||
|
@deftypemethodx {location} {location} operator+= (const location& @var{loc}, int @var{width})
|
||||||
|
Various forms of syntactic sugar.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {location} {void} step ()
|
||||||
|
Move @code{begin} onto @code{end}.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
|
||||||
|
@node C++ Parser Interface
|
||||||
|
@subsection C++ Parser Interface
|
||||||
|
@c - define parser_class_name
|
||||||
|
@c - Ctor
|
||||||
|
@c - parse, error, set_debug_level, debug_level, set_debug_stream,
|
||||||
|
@c debug_stream.
|
||||||
|
@c - Reporting errors
|
||||||
|
|
||||||
|
The output files @file{@var{output}.hh} and @file{@var{output}.cc}
|
||||||
|
declare and define the parser class in the namespace @code{yy}. The
|
||||||
|
class name defaults to @code{parser}, but may be changed using
|
||||||
|
@samp{%define "parser_class_name" "@var{name}"}. The interface of
|
||||||
|
this class is detailled below. It can be extended using the
|
||||||
|
@code{%parse-param} feature: its semantics is slightly changed since
|
||||||
|
it describes an additional member of the parser class, and an
|
||||||
|
additional argument for its constructor.
|
||||||
|
|
||||||
|
@deftypemethod {parser} {semantic_value_type}
|
||||||
|
@deftypemethodx {parser} {location_value_type}
|
||||||
|
The types for semantics value and locations.
|
||||||
|
@c FIXME: deftypemethod pour des types ???
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {parser} {} parser (@var{type1} @var{arg1}, ...)
|
||||||
|
Build a new parser object. There are no arguments by default, unless
|
||||||
|
@samp{%parse-param @{@var{type1} @var{arg1}@}} was used.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {parser} {int} parse ()
|
||||||
|
Run the syntactic analysis, and return 0 on success, 1 otherwise.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {parser} {std::ostream&} debug_stream ()
|
||||||
|
@deftypemethodx {parser} {void} set_debug_stream (std::ostream& @var{o})
|
||||||
|
Get or set the stream used for tracing the parsing. It defaults to
|
||||||
|
@code{std::cerr}.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {parser} {debug_level_type} debug_level ()
|
||||||
|
@deftypemethodx {parser} {void} set_debug_level (debug_level @var{l})
|
||||||
|
Get or set the tracing level. Currently its value is either 0, no trace,
|
||||||
|
or non-zero, full tracing.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
@deftypemethod {parser} {void} error (const location_type& @var{l}, const std::string& @var{m})
|
||||||
|
The definition for this member function must be supplied by the user:
|
||||||
|
the parser uses it to report a parser error occurring at @var{l},
|
||||||
|
described by @var{m}.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
|
||||||
|
@node C++ Scanner Interface
|
||||||
|
@subsection C++ Scanner Interface
|
||||||
|
@c - prefix for yylex.
|
||||||
|
@c - Pure interface to yylex
|
||||||
|
@c - %lex-param
|
||||||
|
|
||||||
|
The parser invokes the scanner by calling @code{yylex}. Contrary to C
|
||||||
|
parsers, C++ parsers are always pure: there is no point in using the
|
||||||
|
@code{%pure-parser} directive. Therefore the interface is as follows.
|
||||||
|
|
||||||
|
@deftypemethod {parser} {int} yylex (semantic_value_type& @var{yylval}, location_type& @var{yylloc}, @var{type1} @var{arg1}, ...)
|
||||||
|
Return the next token. Its type is the return value, its semantic
|
||||||
|
value and location being @var{yylval} and @var{yylloc}. Invocations of
|
||||||
|
@samp{%lex-param @{@var{type1} @var{arg1}@}} yield additional arguments.
|
||||||
|
@end deftypemethod
|
||||||
|
|
||||||
|
|
||||||
|
@node A Complete C++ Example
|
||||||
|
@section A Complete C++ Example
|
||||||
|
|
||||||
|
This section demonstrates the use of a C++ parser with a simple but
|
||||||
|
complete example. This example should be available on your system,
|
||||||
|
ready to compile, in the directory @dfn{../bison/examples/calc++}. It
|
||||||
|
focuses on the use of Bison, therefore the design of the various C++
|
||||||
|
classes is very naive: no accessors, no encapsulation of members etc.
|
||||||
|
We will use a Lex scanner, and more precisely, a Flex scanner, to
|
||||||
|
demonstrate the various interaction. A hand written scanner is
|
||||||
|
actually easier to interface with.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* Calc++ --- C++ Calculator:: The specifications
|
||||||
|
* Calc++ Parsing Driver:: An active parsing context
|
||||||
|
* Calc++ Parser:: A parser class
|
||||||
|
* Calc++ Scanner:: A pure C++ Flex scanner
|
||||||
|
* Calc++ Top Level:: Conducting the band
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node Calc++ --- C++ Calculator
|
||||||
|
@subsection Calc++ --- C++ Calculator
|
||||||
|
|
||||||
|
Of course the grammar is dedicated to arithmetics, a single
|
||||||
|
expression, possibily preceded by variable assignments. An
|
||||||
|
environment containing possibly predefined variables such as
|
||||||
|
@code{one} and @code{two}, is exchanged with the parser. An example
|
||||||
|
of valid input follows.
|
||||||
|
|
||||||
|
@example
|
||||||
|
three := 3
|
||||||
|
seven := one + two * three
|
||||||
|
seven * seven
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@node Calc++ Parsing Driver
|
||||||
|
@subsection Calc++ Parsing Driver
|
||||||
|
@c - An env
|
||||||
|
@c - A place to store error messages
|
||||||
|
@c - A place for the result
|
||||||
|
|
||||||
|
To support a pure interface with the parser (and the scanner) the
|
||||||
|
technique of the ``parsing context'' is convenient: a structure
|
||||||
|
containing all the data to exchange. Since, in addition to simply
|
||||||
|
launch the parsing, there are several auxiliary tasks to execute (open
|
||||||
|
the file for parsing, instantiate the parser etc.), we recommend
|
||||||
|
transforming the simple parsing context structure into a fully blown
|
||||||
|
@dfn{parsing driver} class.
|
||||||
|
|
||||||
|
The declaration of this driver class, @file{calc++-driver.hh}, is as
|
||||||
|
follows. The first part includes the CPP guard and imports the
|
||||||
|
required standard library components.
|
||||||
|
|
||||||
|
@example
|
||||||
|
#ifndef CALCXX_DRIVER_HH
|
||||||
|
# define CALCXX_DRIVER_HH
|
||||||
|
# include <string>
|
||||||
|
# include <map>
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Then come forward declarations. Because the parser uses the parsing
|
||||||
|
driver and reciprocally, simple inclusions of header files will not
|
||||||
|
do. Because the driver's declaration is the one that will be imported
|
||||||
|
by the rest of the project, it is saner to forward declare the
|
||||||
|
parser's information here.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// Forward declarations.
|
||||||
|
union YYSTYPE;
|
||||||
|
namespace yy @{ class calcxx_parser; @}
|
||||||
|
class calcxx_driver;
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Then comes the declaration of the scanning function. Flex expects
|
||||||
|
the signature of @code{yylex} to be defined in the macro
|
||||||
|
@code{YY_DECL}, and the C++ parser expects it to be declared. We can
|
||||||
|
factor both as follows.
|
||||||
|
@example
|
||||||
|
// Announce to Flex the prototype we want for lexing function, ...
|
||||||
|
# define YY_DECL \
|
||||||
|
int yylex (YYSTYPE* yylval, yy::location* yylloc, calcxx_driver& driver)
|
||||||
|
// ... and declare it for the parser's sake.
|
||||||
|
YY_DECL;
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The @code{calcxx_driver} class is then declared with its most obvious
|
||||||
|
members.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// Conducting the whole scanning and parsing of Calc++.
|
||||||
|
class calcxx_driver
|
||||||
|
@{
|
||||||
|
public:
|
||||||
|
calcxx_driver ();
|
||||||
|
virtual ~calcxx_driver ();
|
||||||
|
|
||||||
|
std::map<std::string, int> variables;
|
||||||
|
|
||||||
|
int result;
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
To encapsulate the coordination with the Flex scanner, it is useful to
|
||||||
|
have two members function to open and close the scanning phase.
|
||||||
|
members.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// Handling the scanner.
|
||||||
|
void scan_begin ();
|
||||||
|
void scan_end ();
|
||||||
|
bool trace_scanning;
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Similarly for the parser itself.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// Handling the parser.
|
||||||
|
void parse (const std::string& f);
|
||||||
|
std::string file;
|
||||||
|
bool trace_parsing;
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
To demonstrate pure handling of parse errors, instead of simply
|
||||||
|
dumping them on the standard error output, we will pass them to the
|
||||||
|
compiler driver using the following two member functions. Finally, we
|
||||||
|
close the class declaration and CPP guard.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// Error handling.
|
||||||
|
void error (const yy::location& l, const std::string& m);
|
||||||
|
void error (const std::string& m);
|
||||||
|
@};
|
||||||
|
#endif // ! CALCXX_DRIVER_HH
|
||||||
|
@end example
|
||||||
|
|
||||||
|
The implementation of the driver is straightforward. The @code{parse}
|
||||||
|
member function deserves some attention. The @code{error} functions
|
||||||
|
are simple stubs, they should actually register the located error
|
||||||
|
messages and set error state.
|
||||||
|
|
||||||
|
@example
|
||||||
|
#include "calc++-driver.hh"
|
||||||
|
#include "calc++-parser.hh"
|
||||||
|
|
||||||
|
calcxx_driver::calcxx_driver ()
|
||||||
|
: trace_scanning (false), trace_parsing (false)
|
||||||
|
@{
|
||||||
|
variables["one"] = 1;
|
||||||
|
variables["two"] = 2;
|
||||||
|
@}
|
||||||
|
|
||||||
|
calcxx_driver::~calcxx_driver ()
|
||||||
|
@{
|
||||||
|
@}
|
||||||
|
|
||||||
|
void
|
||||||
|
calcxx_driver::parse (const std::string &f)
|
||||||
|
@{
|
||||||
|
file = f;
|
||||||
|
scan_begin ();
|
||||||
|
yy::calcxx_parser parser (*this);
|
||||||
|
parser.set_debug_level (trace_parsing);
|
||||||
|
parser.parse ();
|
||||||
|
scan_end ();
|
||||||
|
@}
|
||||||
|
|
||||||
|
void
|
||||||
|
calcxx_driver::error (const yy::location& l, const std::string& m)
|
||||||
|
@{
|
||||||
|
std::cerr << l << ": " << m << std::endl;
|
||||||
|
@}
|
||||||
|
|
||||||
|
void
|
||||||
|
calcxx_driver::error (const std::string& m)
|
||||||
|
@{
|
||||||
|
std::cerr << m << std::endl;
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@node Calc++ Parser
|
||||||
|
@subsection Calc++ Parser
|
||||||
|
|
||||||
|
The parser definition file @file{calc++-parser.yy} starts by asking
|
||||||
|
for the C++ skeleton, the creation of the parser header file, and
|
||||||
|
specifies the name of the parser class. It then includes the required
|
||||||
|
headers.
|
||||||
|
@example
|
||||||
|
%skeleton "lalr1.cc" /* -*- C++ -*- */
|
||||||
|
%define "parser_class_name" "calcxx_parser"
|
||||||
|
%defines
|
||||||
|
%@{
|
||||||
|
# include <string>
|
||||||
|
# include "calc++-driver.hh"
|
||||||
|
%@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The driver is passed by reference to the parser and to the scanner.
|
||||||
|
This provides a simple but effective pure interface, not relying on
|
||||||
|
global variables.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// The parsing context.
|
||||||
|
%parse-param @{ calcxx_driver& driver @}
|
||||||
|
%lex-param @{ calcxx_driver& driver @}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Then we request the location tracking feature, and initialize the
|
||||||
|
first location's file name. Afterwards new locations are computed
|
||||||
|
relatively to the previous locations: the file name will be
|
||||||
|
automatically propagated.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%locations
|
||||||
|
%initial-action
|
||||||
|
@{
|
||||||
|
// Initialize the initial location.
|
||||||
|
@@$.begin.filename = @@$.end.filename = &driver.file;
|
||||||
|
@};
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Use the two following directives to enable parser tracing and verbose
|
||||||
|
error messages.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%debug
|
||||||
|
%error-verbose
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Semantic values cannot use ``real'' objects, but only pointers to
|
||||||
|
them.
|
||||||
|
|
||||||
|
@example
|
||||||
|
// Symbols.
|
||||||
|
%union
|
||||||
|
@{
|
||||||
|
int ival;
|
||||||
|
std::string *sval;
|
||||||
|
@};
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The token numbered as 0 corresponds to end of file; the following line
|
||||||
|
allows for nicer error messages referring to ``end of file'' instead
|
||||||
|
of ``$end''. Similarly user friendly named are provided for each
|
||||||
|
symbol. Note that the tokens names are prefixed by @code{TOKEN_} to
|
||||||
|
avoid name clashes.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%token YYEOF 0 "end of file"
|
||||||
|
%token TOKEN_ASSIGN ":="
|
||||||
|
%token <sval> TOKEN_IDENTIFIER "identifier"
|
||||||
|
%token <ival> TOKEN_NUMBER "number"
|
||||||
|
%type <ival> exp "expression"
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
To enable memory deallocation during error recovery, use
|
||||||
|
@code{%destructor}.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%printer @{ debug_stream () << *$$; @} "identifier"
|
||||||
|
%destructor @{ delete $$; @} "identifier"
|
||||||
|
|
||||||
|
%printer @{ debug_stream () << $$; @} "number" "expression"
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The grammar itself is straightforward.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%%
|
||||||
|
%start unit;
|
||||||
|
unit: assignments exp @{ driver.result = $2; @};
|
||||||
|
|
||||||
|
assignments: assignments assignment @{@}
|
||||||
|
| /* Nothing. */ @{@};
|
||||||
|
|
||||||
|
assignment: TOKEN_IDENTIFIER ":=" exp @{ driver.variables[*$1] = $3; @};
|
||||||
|
|
||||||
|
%left '+' '-';
|
||||||
|
%left '*' '/';
|
||||||
|
exp: exp '+' exp @{ $$ = $1 + $3; @}
|
||||||
|
| exp '-' exp @{ $$ = $1 - $3; @}
|
||||||
|
| exp '*' exp @{ $$ = $1 * $3; @}
|
||||||
|
| exp '/' exp @{ $$ = $1 / $3; @}
|
||||||
|
| TOKEN_IDENTIFIER @{ $$ = driver.variables[*$1]; @}
|
||||||
|
| TOKEN_NUMBER @{ $$ = $1; @};
|
||||||
|
%%
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Finally the @code{error} member function registers the errors to the
|
||||||
|
driver.
|
||||||
|
|
||||||
|
@example
|
||||||
|
void
|
||||||
|
yy::calcxx_parser::error (const location_type& l, const std::string& m)
|
||||||
|
@{
|
||||||
|
driver.error (l, m);
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@node Calc++ Scanner
|
||||||
|
@subsection Calc++ Scanner
|
||||||
|
|
||||||
|
The Flex scanner first includes the driver declaration, then the
|
||||||
|
parser's to get the set of defined tokens.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%@{ /* -*- C++ -*- */
|
||||||
|
# include <string>
|
||||||
|
# include "calc++-driver.hh"
|
||||||
|
# include "calc++-parser.hh"
|
||||||
|
%@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Because there is no @code{#include}-like feature we don't need
|
||||||
|
@code{yywrap}, we don't need @code{unput} either, and we parse an
|
||||||
|
actual file, this is not an interactive session with the user.
|
||||||
|
Finally we enable the scanner tracing features.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%option noyywrap nounput batch debug
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Abbreviations allow for more readable rules.
|
||||||
|
|
||||||
|
@example
|
||||||
|
id [a-zA-Z][a-zA-Z_0-9]*
|
||||||
|
int [0-9]+
|
||||||
|
blank [ \t]
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The following paragraph suffices to track locations acurately. Each
|
||||||
|
time @code{yylex} is invoked, the begin position is moved onto the end
|
||||||
|
position. Then when a pattern is matched, the end position is
|
||||||
|
advanced of its width. In case it matched ends of lines, the end
|
||||||
|
cursor is adjusted, and each time blanks are matched, the begin cursor
|
||||||
|
is moved onto the end cursor to effectively ignore the blanks
|
||||||
|
preceding tokens. Comments would be treated equally.
|
||||||
|
|
||||||
|
@example
|
||||||
|
%%
|
||||||
|
%@{
|
||||||
|
yylloc->step ();
|
||||||
|
# define YY_USER_ACTION yylloc->columns (yyleng);
|
||||||
|
%@}
|
||||||
|
@{blank@}+ yylloc->step ();
|
||||||
|
[\n]+ yylloc->lines (yyleng); yylloc->step ();
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The rules are simple, just note the use of the driver to report
|
||||||
|
errors.
|
||||||
|
|
||||||
|
@example
|
||||||
|
[-+*/] return yytext[0];
|
||||||
|
":=" return TOKEN_ASSIGN;
|
||||||
|
@{int@} yylval->ival = atoi (yytext); return TOKEN_NUMBER;
|
||||||
|
@{id@} yylval->sval = new std::string (yytext); return TOKEN_IDENTIFIER;
|
||||||
|
. driver.error (*yylloc, "invalid character");
|
||||||
|
%%
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Finally, because the scanner related driver's member function depend
|
||||||
|
on the scanner's data, it is simpler to implement them in this file.
|
||||||
|
|
||||||
|
@example
|
||||||
|
void
|
||||||
|
calcxx_driver::scan_begin ()
|
||||||
|
@{
|
||||||
|
yy_flex_debug = trace_scanning;
|
||||||
|
if (!(yyin = fopen (file.c_str (), "r")))
|
||||||
|
error (std::string ("cannot open ") + file);
|
||||||
|
@}
|
||||||
|
|
||||||
|
void
|
||||||
|
calcxx_driver::scan_end ()
|
||||||
|
@{
|
||||||
|
fclose (yyin);
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@node Calc++ Top Level
|
||||||
|
@subsection Calc++ Top Level
|
||||||
|
|
||||||
|
The top level file, @file{calc++.cc}, poses no problem.
|
||||||
|
|
||||||
|
@example
|
||||||
|
#include <iostream>
|
||||||
|
#include "calc++-driver.hh"
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, const char* argv[])
|
||||||
|
@{
|
||||||
|
calcxx_driver driver;
|
||||||
|
for (++argv; argv[0]; ++argv)
|
||||||
|
if (*argv == std::string ("-p"))
|
||||||
|
driver.trace_parsing = true;
|
||||||
|
else if (*argv == std::string ("-s"))
|
||||||
|
driver.trace_scanning = true;
|
||||||
|
else
|
||||||
|
@{
|
||||||
|
driver.parse (*argv);
|
||||||
|
std::cout << driver.result << std::endl;
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@c ================================================= FAQ
|
||||||
|
|
||||||
@node FAQ
|
@node FAQ
|
||||||
@chapter Frequently Asked Questions
|
@chapter Frequently Asked Questions
|
||||||
@@ -6751,7 +7415,6 @@ are addressed.
|
|||||||
* Parser Stack Overflow:: Breaking the Stack Limits
|
* Parser Stack Overflow:: Breaking the Stack Limits
|
||||||
* How Can I Reset the Parser:: @code{yyparse} Keeps some State
|
* How Can I Reset the Parser:: @code{yyparse} Keeps some State
|
||||||
* Strings are Destroyed:: @code{yylval} Loses Track of Strings
|
* Strings are Destroyed:: @code{yylval} Loses Track of Strings
|
||||||
* C++ Parsers:: Compiling Parsers with C++ Compilers
|
|
||||||
* Implementing Gotos/Loops:: Control Flow in the Calculator
|
* Implementing Gotos/Loops:: Control Flow in the Calculator
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@@ -6916,27 +7579,6 @@ $ @kbd{printf 'one\ntwo\n' | ./split-lines}
|
|||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
|
||||||
@node C++ Parsers
|
|
||||||
@section C++ Parsers
|
|
||||||
|
|
||||||
@display
|
|
||||||
How can I generate parsers in C++?
|
|
||||||
@end display
|
|
||||||
|
|
||||||
We are working on a C++ output for Bison, but unfortunately, for lack of
|
|
||||||
time, the skeleton is not finished. It is functional, but in numerous
|
|
||||||
respects, it will require additional work which @emph{might} break
|
|
||||||
backward compatibility. Since the skeleton for C++ is not documented,
|
|
||||||
we do not consider ourselves bound to this interface, nevertheless, as
|
|
||||||
much as possible we will try to keep compatibility.
|
|
||||||
|
|
||||||
Another possibility is to use the regular C parsers, and to compile them
|
|
||||||
with a C++ compiler. This works properly, provided that you bear some
|
|
||||||
simple C++ rules in mind, such as not including ``real classes'' (i.e.,
|
|
||||||
structure with constructors) in unions. Therefore, in the
|
|
||||||
@code{%union}, use pointers to classes.
|
|
||||||
|
|
||||||
|
|
||||||
@node Implementing Gotos/Loops
|
@node Implementing Gotos/Loops
|
||||||
@section Implementing Gotos/Loops
|
@section Implementing Gotos/Loops
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user