mirror of https://github.com/CGAL/cgal
280 lines
15 KiB
TeX
280 lines
15 KiB
TeX
% -------------------------------------------------------------------------
|
|
\subsection{Visitor Design Pattern for the Parser\label{visitor}}
|
|
|
|
To ease the use of the parser, we provide a visitor interface,
|
|
following the visitor design pattern~\cite{cgal:ghjv-dpero-95}. An
|
|
application would derive its own visitor from the
|
|
\texttt{Benchmark\_visitor} base class. This design choice makes the
|
|
parser independent of application types, such as long integers.
|
|
Instead, the parser keeps almost all recognized literals in strings of
|
|
type \texttt{std::string}.
|
|
|
|
The visitor has various functions that will be called from the parser
|
|
with suitable parameters when the parser recognizes certain grammar
|
|
rules. The visitor can then rely on the fact that the file is well
|
|
formed and just process the parameters. Typically the visitor will
|
|
store the objects found in the benchmark file in an internal data
|
|
structure for later use in the application. The visitor has to convert
|
|
the string parameters to the application specific types, for example,
|
|
for long integers for curve coefficients.
|
|
|
|
\subsubsection{A First Visitor\label{checker}}
|
|
|
|
We start with an example for a basic visitor that just checks a
|
|
benchmark file for correct syntax, i.e., it does not actually process
|
|
the objects in the file. Programs using the visitor will look similar,
|
|
but they will typically implement more member functions of the base
|
|
class interface, see Section~\ref{functions}. This example program
|
|
exists in source code in the distribution at
|
|
\verb|BMTools/parser/src/check_syntax.C|.
|
|
|
|
\begin{verbatim}
|
|
#include <iostream>
|
|
#include <benchmark_visitor.hpp>
|
|
|
|
struct Checker : public Benchmark_visitor {
|
|
Checker() {}
|
|
\end{verbatim}
|
|
|
|
\noindent
|
|
The visitor class \texttt{Checker} is derived from the
|
|
\texttt{Benchmark\_visitor} base class and has a default constructor.
|
|
The base class defines the interface for all visitors in terms of
|
|
virtual member functions that we can override in the derived class.
|
|
All member functions have sensible defaults that simplify the
|
|
implementation of a visitor that accepts only a subset of the objects.
|
|
For example, all object accepting member functions default to a call
|
|
to the error handler with a ``token not handled'' error message. We
|
|
explain in Section~\ref{functions} the available member functions and
|
|
in Section~\ref{extend} how the \texttt{Benchmark\_visitor} can be
|
|
extended. Here, we just override the particular error handler for
|
|
the ``token not handled'' error message and disable the error message,
|
|
since we are just interested in accepting all tokens but do not handle
|
|
them individually.
|
|
|
|
\begin{verbatim}
|
|
virtual void token_not_handled( std::string s) {}
|
|
\end{verbatim}
|
|
|
|
\noindent
|
|
In other typical visitor implementations this function would not be
|
|
overwritten because it is not called when the visitor recognizes and
|
|
processes a parameter properly. An example of such a processing is the
|
|
following handling of the classification entries, which is handled
|
|
with the \texttt{classification} member function in the visitor that
|
|
accepts six string parameters for the different classification
|
|
entries. We implement a validness test that checks the parameters for
|
|
proper values.
|
|
|
|
\begin{verbatim}
|
|
virtual void accept_classification( std::string problem,
|
|
std::string geom,
|
|
std::string clas,
|
|
std::string family,
|
|
std::string instance,
|
|
std::string release) {
|
|
if ((problem != "Arrangement") && (problem != "CSG")
|
|
&& (problem != " "))
|
|
error_handler( "classification error");
|
|
|
|
if ((geom != "Lines") && (geom != "Circles") && (geom != "Conics")
|
|
&& (geom != "Cubics") && (geom != "Quartics")
|
|
&& (geom != "ArbitraryDegreeCurves") && ( geom != "Quadrics")
|
|
&& (geom != "Tori") && (geom != "Planes") && (geom != " "))
|
|
error_handler( "classification error" );
|
|
|
|
if ((clas != "FullCurves") && (clas != "Ellipses")
|
|
&& (clas != "BoundedArcs") && (clas != "UnboundedArcs")
|
|
&& (clas != "FullSurfaces") && (clas != "BoundedPatches")
|
|
&& (clas != "UnboundedPatches") && (clas != " "))
|
|
error_handler( "classification error" );
|
|
}
|
|
\end{verbatim}
|
|
|
|
\noindent
|
|
In addition, we use the benchmark name for a suitable diagnostic
|
|
output. For this, we override the implementation of the
|
|
\texttt{accept\_benchmark\_name} member function that accepts one
|
|
string parameters for the name. However, the base class implementation
|
|
of this function stores the name in the base class for later access
|
|
with the \texttt{benchmark\_name} member function, for example for
|
|
error messages, that we want to preserve. The easiest solution is to
|
|
call the implementation of the member function in the base class as
|
|
well. That finishes our visitor implementation.
|
|
|
|
\begin{verbatim}
|
|
virtual void accept_benchmark_name( std::string s) {
|
|
Benchmark_visitor::accept_benchmark_name(s);
|
|
std::cerr << "name '" << s << "', ";
|
|
}
|
|
};
|
|
\end{verbatim}
|
|
|
|
\noindent
|
|
It follows a simple main program that checks all files given by their
|
|
filenames on the command line for syntax errors and prints a suitable
|
|
diagnostic message.
|
|
|
|
\begin{verbatim}
|
|
int main( int argc, char* argv[] ) {
|
|
int exit_status = 0;
|
|
for ( int i = 1; i < argc; ++i) {
|
|
Checker check;
|
|
std::cerr << "File '" << argv[i] << "', ";
|
|
if ( benchmark_parse_file( argv[i], & check)) {
|
|
std::cerr << "is o.k." << std::endl;
|
|
} else {
|
|
std::cerr << "is malformed." << std::endl;
|
|
exit_status = 1;
|
|
}
|
|
}
|
|
return exit_status;
|
|
}
|
|
\end{verbatim}
|
|
|
|
\noindent
|
|
A similar global parser function allows parsing from an already opened
|
|
\CC\ stream. However, a stream is lacking useful diagnostic
|
|
information---filename and line number---that we provide explicitly
|
|
for this global parser function, where the line number has the
|
|
sensible default to start at one. Both functions return \texttt{true}
|
|
if the file parses without error, and \texttt{false} otherwise. The
|
|
full function signatures are:
|
|
|
|
\begin{verbatim}
|
|
bool benchmark_parse_file( std::string name, Benchmark_visitor* v);
|
|
bool benchmark_parse_stream( std::istream& in, std::string name,
|
|
Benchmark_visitor* v, int n = 1);
|
|
\end{verbatim}
|
|
|
|
|
|
\subsubsection{Benchmark Visitor Functions\label{functions}}
|
|
|
|
In the following table the functions that exist in the
|
|
\ccc{Benchmark_visitor} class are listed. The \$-bison syntax
|
|
denotes the value of the arguments in the grammar rule, typically the
|
|
integer and strings, counting terminal symbols, non-terminal symbols,
|
|
and also the blocks in curly braces. For their semantic specification
|
|
see Section~\ref{grammar} and Section~\ref{extend}. The statements in
|
|
curly braces
|
|
% \Open\ts{...}\Close\
|
|
\texttt{\{...\}}
|
|
are function calls to the visitor.
|
|
They come in two flavors, either a single function call, such as in
|
|
\texttt{accept\_integer(\$1)} for integers, if the entry has a small
|
|
constant number of parameters and only a few variants, or a pair of
|
|
functions that enclose the parsing of the parameters and form a proper
|
|
bracketing, such as for polynomials with
|
|
\ccc{begin_polynomial_1()} and \ccc{end_polynomial_1()},
|
|
where its integer coefficients are detected between these two function
|
|
calls.
|
|
|
|
A typical visitor would maintain a list of integers, initialize
|
|
it in the \ccc{begin_polynomial_1()} call, stores all integers
|
|
accepted in the \ccc{accept_integer(\$1)} function in this list,
|
|
and builds a new polynomial from the list in the
|
|
\ccc{end_polynomial_1()} function. In general, a visitor
|
|
implementation would work with stacks to reflect the recursive nature
|
|
of the grammar.
|
|
|
|
\begin{ccTexOnly}
|
|
\begin{tabular}{*{2}{| l} |} \hline
|
|
Object & grammar rule with visitor function \\ \hline \hline
|
|
Integer & {\bf Integer} \{accept\_integer(\$1);\} \\ \hline
|
|
Rational & {\bf Rational ( Integer , Integer )} \{accept\_rational(\$3, \$5);\} \\ \hline
|
|
Infty & {\bf MINUS\_INFTY} \{accept\_infty(\$1);\} \\
|
|
& {\bf PLUS\_INFTY} \{accept\_infty(\$1);\} \\ \hline
|
|
Orientation & {\bf COUNTERCLOCKWISE} \\
|
|
& {\bf CLOCKWISE} \\
|
|
& {\bf VOID} \\ \hline
|
|
Integer\_sequence & {\bf Integer} \{accept\_integer(\$1);\} \\
|
|
& Integer\_sequence {\bf \Large ,} {\bf Integer} \{accept\_integer(\$3);\} \\ \hline
|
|
Point\_2 & {\bf Point\_2 ( Integer , Integer )} \{accept\_point\_2(\$3, \$5);\} \\
|
|
& {\bf Point\_2 ( Integer , Integer , Integer )} \{accept\_point\_2(\$3, \$5, \$7);\} \\
|
|
& {\bf Point\_2 ( Rational ( Integer , Integer ) , Rational ( Integer , Integer ) )}\\
|
|
& \ \ \ \ \{accept\_point\_2(\$5, \$7, \$12, \$14);\} \\ \hline
|
|
Polynomial & {\bf Polynomial\_1} \{begin\_polynomial\_1();\} {\bf \Large (} Integer\_sequence {\bf \Large )}\\
|
|
& \ \ \ \ \{end\_polynomial\_1();\} \\ \hline
|
|
AlgebraicReal & {\bf AlgebraicReal} \{begin\_algebraic\_real();\} {\bf \Large (}\\
|
|
& \ \ {\bf Polynomial\_1} \{begin\_polynomial\_1();\}\\
|
|
& \ \ \ \ \ {\bf \Large (} Integer\_sequence {\bf \Large )}\\
|
|
& \ \ \ \ \ \ \ \ \ \{end\_polynomial\_1();\} {\bf \Large ,} \\
|
|
& \ \ Rational {\bf \Large ,} Rational {\bf \Large ,} \\
|
|
& \ \ {\bf Integer} \{accept\_integer(\$15);\} {\bf \Large )} \\
|
|
& \ \ \ \ \{end\_algebraic\_real();\} \\ \hline
|
|
LineSegment & {\bf LineSegment\_2} \{begin\_line\_segment\_2();\} {\bf \Large (} Point\_2 {\bf \Large ,} \\
|
|
& \ \ \ \ \ \ \ \ \ \ Point\_2 {\bf \Large )} \{end\_line\_segment\_2();\}\\ \hline
|
|
Circle & {\bf Circle\_2} \{begin\_circle\_2();\} {\bf \Large (} Point\_2 {\bf \Large ,} \\
|
|
& {\bf Integer} \{accept\_integer(\$6);\} {\bf \Large )} \{end\_circle\_2();\} \\
|
|
Circle & {\bf Circle\_2} \{begin\_circle\_2();\} {\bf \Large (} Point\_2 {\bf \Large ,} Rational {\bf \Large )} \{end\_circle\_2();\} \\ \hline
|
|
Conic & {\bf Conic\_2 ( Integer, Integer, Integer, Integer, Integer, Integer )} \\
|
|
& \{accept\_conic\_2 (\$3, \$5, \$7, \$9, \$11, \$13);\}\\ \hline
|
|
ConicPoint & {\bf ConicPoint\_2} \{begin\_conic\_point\_2();\} {\bf (} Conic , \\
|
|
& \ \ AlgebraicReal {\bf \Large ,} Integer {\bf \Large )} \\
|
|
& \ \ \ \ \ \ \ \ \ \{end\_conic\_point\_2();\}\\
|
|
& {\bf ConicPoint\_2} \{begin\_conic\_point\_2();\} {\bf (} Conic , \\
|
|
& \ \ Infty {\bf \Large ,} Integer {\bf \Large )} \\
|
|
& \ \ \ \ \ \ \ \ \ \{end\_conic\_point\_2();\}\\
|
|
& {\bf ConicPoint\_2} \{begin\_conic\_point\_2();\} {\bf (} Conic , \\
|
|
& \ \ AlgebraicReal {\bf \Large ,} Infty {\bf \Large )} \\
|
|
& \ \ \ \ \ \ \ \ \ \{end\_conic\_point\_2();\}\\ \hline
|
|
ConicArc & {\bf ConicArc\_2} \{begin\_conic\_arc\_2();\} {\bf \Large (} Conic {\bf \Large ,} \\
|
|
& \ \ ConicPoint {\bf \Large ,} \\
|
|
& \ \ ConicPoint {\bf \Large ,} \\
|
|
& \ \ orientation \{accept\_orientation(\$10);\} {\bf \Large )} \\
|
|
& \{end\_conic\_arc\_2();\}\\
|
|
& {\bf ConicArc\_2} \{begin\_conic\_arc\_2();\} {\bf \Large (} ConicPoint {\bf \Large )} \{end\_conic\_arc\_2();\}\\ \hline
|
|
Cubic & {\bf Cubic\_2 (} \\
|
|
& \ \ {\bf Integer , Integer , Integer , Integer , Integer ,} \\
|
|
& \ \ {\bf Integer , Integer , Integer , Integer , Integer )} \\
|
|
& \{accept\_cubic\_2 (\$3, \$5, \$7, \$9, \$11, \$13, \$15, \$17, \$19, \$21);\}\\ \hline
|
|
Quadric & {\bf Quadric\_3 (} \\
|
|
& \ \ {\bf Integer , Integer , Integer , Integer , Integer ,} \\
|
|
& \ \ {\bf Integer , Integer , Integer , Integer , Integer )} \\
|
|
& \{accept\_quadric\_3 (\$3, \$5, \$7, \$9, \$11, \$13, \$15, \$17, \$19, \$21);\}\\ \hline
|
|
\end{tabular}
|
|
\end{ccTexOnly}
|
|
|
|
\begin{ccHtmlOnly}
|
|
<div align="center">
|
|
<table cellpadding=3 border="1">
|
|
<tr><th>Object</th><th>Grammar rule with visitor function</th></tr>
|
|
<tr><td>Integer</td><td><b>Integer</b>{accept_integer($1);}</td></tr>
|
|
<tr><td>Rational</td><td><b>Rational(Integer, Integer)</b> {accept_rational($3, $5);}</td></tr>
|
|
<tr><td>Infty</td><td><b>MINUS_INFTY</b> {accept_infty($1);}<br>
|
|
<b>PLUS_INFTY</b> {accept_infty($1);}</td></tr>
|
|
<tr><td>Orientation</td><td><b>COUNTERCLOCKWISE</b><br>
|
|
<b>CLOCKWISE</b><br>
|
|
<b>VOID</b></td></tr>
|
|
<tr><td>Integer_sequence</td>
|
|
<td><b>Integer} {accept_integer($1);}<br>
|
|
Integer_sequence, <b>Integer</b> {accept_integer($3);}</td></tr>
|
|
<tr><td>Point_2</td>
|
|
<td><b>Point_2(Integer,Integer)</b> {accept_point_2($3, $5);}<br>
|
|
<b>Point_2(Integer,Integer,Integer)</b> {accept_point_2($3, $5, $7);}<br>
|
|
<b>Point_2(Rational(Integer,Integer),Rational(Integer,Integer))</b> {accept_point_2($5, $7, $12, $14);}</td></tr>
|
|
<tr><td>Polynomial</td><td><b>Polynomial_1</b> {begin_polynomial_1();}<b>(</b>Integer_sequence<b>)</b>{end_polynomial_1();}</td></tr>
|
|
<tr><td>AlgebraicReal</td>
|
|
<td><b>AlgebraicReal</b> {begin_algebraic_real();}<b>(</b><br>
|
|
<b>Polynomial_1} {begin_polynomial_1();}<b>(</b>Integer_sequence<b>)</b>{end_polynomial_1();}<b>,</b>Rational<b>,</b>Rational<b>,</b><b>Integer} {accept_integer($15);}<br>
|
|
<b>)</b> {end_algebraic_real();}</td></tr>
|
|
<tr><td>LineSegment</td><td><b>LineSegment_2</b> {begin_line_segment_2();}<b>(</b>Point_2<b>,</b>Point_2<b>)</b>{end_line_segment_2();}</td></tr>
|
|
<tr><td>Circle<br>
|
|
Circle</td><td><b>Circle_2</b> {begin_circle_2();}<b>(</b>Point_2<b>,</b><b>Integer</b> {accept_integer($6);}<b>)</b> {end_circle_2();}<br>
|
|
<b>Circle_2</b> {begin_circle_2();}<b>(</b>Point_2<b>,</b>Rational<b>)</b> {end_circle_2();}</td></tr>
|
|
<tr><td>Conic</td>
|
|
<td><b>Conic_2(Integer,Integer,Integer,Integer,Integer,Integer)</b> {accept_conic_2 ($3, $5, $7, $9, $11, $13);}</td></tr>
|
|
<tr><td>ConicPoint</td>
|
|
<td><b>ConicPoint_2</b> {begin_conic_point_2();}<b>(}Conic,AlgebraicReal<b>,</b>Integer<b>)</b> {end_conic_point_2();}<br>
|
|
<b>ConicPoint_2</b> {begin_conic_point_2();}<b>(}Conic,Infty<b>,</b>Integer<b>)</b> {end_conic_point_2();}<br>
|
|
<b>ConicPoint_2</b> {begin_conic_point_2();}<b>(}Conic,AlgebraicReal<b>,</b>Infty<b>)</b> {end_conic_point_2();}</td></tr>
|
|
<tr><td>ConicArc</td>
|
|
<td><b>ConicArc_2</b> {begin_conic_arc_2();} <b>(</b>Conic<b>,</b>ConicPoint<b>,</b>ConicPoint<b>,</b>orientation {accept_orientation($10);}<b>)</b> {end_conic_arc_2();}<br>
|
|
<b>ConicArc_2</b> {begin_conic_arc_2();} <b>(</b>ConicPoint<b>)</b> {end_conic_arc_2();}</td></tr>
|
|
<tr><td>Cubic</td>
|
|
<td><b>Cubic_2(Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer)</b> {accept_cubic_2($3, $5, $7, $9, $11, $13, $15, $17, $19, $21);}</td></tr>
|
|
<tr><td>Quadric</td><td><b>Quadric_3(Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer,Integer)</b> {accept_quadric_3($3, $5, $7, $9, $11, $13, $15, $17, $19, $21);}
|
|
</table>
|
|
</div>
|
|
\end{ccHtmlOnly}
|