Extract calc++ from the documentation.

* doc/bison.texinfo (Calc++): Add the extraction marks.
* examples/extexi: New, from the aborted GNU Programming 2E.
Separate the different paragraph of a file with empty lines.
* examples/Makefile: Use it to extract the whole calc++ example.
This commit is contained in:
Akim Demaille
2005-07-05 07:21:30 +00:00
parent 8a0adb0183
commit 1c59e0a121
9 changed files with 235 additions and 66 deletions

View File

@@ -1,3 +1,11 @@
2005-07-05 Akim Demaille <akim@epita.fr>
Extract calc++ from the documentation.
* doc/bison.texinfo (Calc++): Add the extraction marks.
* examples/extexi: New, from the aborted GNU Programming 2E.
Separate the different paragraph of a file with empty lines.
* examples/Makefile: Use it to extract the whole calc++ example.
2005-06-24 Akim Demaille <akim@epita.fr> 2005-06-24 Akim Demaille <akim@epita.fr>
* doc/bison.texinfo (C++ Parser Interface): Use defcv to define * doc/bison.texinfo (C++ Parser Interface): Use defcv to define

View File

@@ -7031,6 +7031,7 @@ The declaration of this driver class, @file{calc++-driver.hh}, is as
follows. The first part includes the CPP guard and imports the follows. The first part includes the CPP guard and imports the
required standard library components. required standard library components.
@comment file: calc++-driver.hh
@example @example
#ifndef CALCXX_DRIVER_HH #ifndef CALCXX_DRIVER_HH
# define CALCXX_DRIVER_HH # define CALCXX_DRIVER_HH
@@ -7045,10 +7046,15 @@ 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 by the rest of the project, it is saner to forward declare the
parser's information here. parser's information here.
@comment file: calc++-driver.hh
@example @example
// Forward declarations. // Forward declarations.
union YYSTYPE; union YYSTYPE;
namespace yy @{ class calcxx_parser; @} namespace yy
@{
class location;
class calcxx_parser;
@}
class calcxx_driver; class calcxx_driver;
@end example @end example
@@ -7057,9 +7063,11 @@ Then comes the declaration of the scanning function. Flex expects
the signature of @code{yylex} to be defined in the macro 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 @code{YY_DECL}, and the C++ parser expects it to be declared. We can
factor both as follows. factor both as follows.
@comment file: calc++-driver.hh
@example @example
// Announce to Flex the prototype we want for lexing function, ... // Announce to Flex the prototype we want for lexing function, ...
# define YY_DECL \ # define YY_DECL \
int yylex (YYSTYPE* yylval, yy::location* yylloc, calcxx_driver& driver) int yylex (YYSTYPE* yylval, yy::location* yylloc, calcxx_driver& driver)
// ... and declare it for the parser's sake. // ... and declare it for the parser's sake.
YY_DECL; YY_DECL;
@@ -7069,6 +7077,7 @@ YY_DECL;
The @code{calcxx_driver} class is then declared with its most obvious The @code{calcxx_driver} class is then declared with its most obvious
members. members.
@comment file: calc++-driver.hh
@example @example
// Conducting the whole scanning and parsing of Calc++. // Conducting the whole scanning and parsing of Calc++.
class calcxx_driver class calcxx_driver
@@ -7087,6 +7096,7 @@ To encapsulate the coordination with the Flex scanner, it is useful to
have two members function to open and close the scanning phase. have two members function to open and close the scanning phase.
members. members.
@comment file: calc++-driver.hh
@example @example
// Handling the scanner. // Handling the scanner.
void scan_begin (); void scan_begin ();
@@ -7097,6 +7107,7 @@ members.
@noindent @noindent
Similarly for the parser itself. Similarly for the parser itself.
@comment file: calc++-driver.hh
@example @example
// Handling the parser. // Handling the parser.
void parse (const std::string& f); void parse (const std::string& f);
@@ -7110,6 +7121,7 @@ dumping them on the standard error output, we will pass them to the
compiler driver using the following two member functions. Finally, we compiler driver using the following two member functions. Finally, we
close the class declaration and CPP guard. close the class declaration and CPP guard.
@comment file: calc++-driver.hh
@example @example
// Error handling. // Error handling.
void error (const yy::location& l, const std::string& m); void error (const yy::location& l, const std::string& m);
@@ -7123,6 +7135,7 @@ member function deserves some attention. The @code{error} functions
are simple stubs, they should actually register the located error are simple stubs, they should actually register the located error
messages and set error state. messages and set error state.
@comment file: calc++-driver.cc
@example @example
#include "calc++-driver.hh" #include "calc++-driver.hh"
#include "calc++-parser.hh" #include "calc++-parser.hh"
@@ -7169,6 +7182,8 @@ The parser definition file @file{calc++-parser.yy} starts by asking
for the C++ skeleton, the creation of the parser header file, and for the C++ skeleton, the creation of the parser header file, and
specifies the name of the parser class. It then includes the required specifies the name of the parser class. It then includes the required
headers. headers.
@comment file: calc++-parser.yy
@example @example
%skeleton "lalr1.cc" /* -*- C++ -*- */ %skeleton "lalr1.cc" /* -*- C++ -*- */
%define "parser_class_name" "calcxx_parser" %define "parser_class_name" "calcxx_parser"
@@ -7184,6 +7199,7 @@ The driver is passed by reference to the parser and to the scanner.
This provides a simple but effective pure interface, not relying on This provides a simple but effective pure interface, not relying on
global variables. global variables.
@comment file: calc++-parser.yy
@example @example
// The parsing context. // The parsing context.
%parse-param @{ calcxx_driver& driver @} %parse-param @{ calcxx_driver& driver @}
@@ -7196,6 +7212,7 @@ first location's file name. Afterwards new locations are computed
relatively to the previous locations: the file name will be relatively to the previous locations: the file name will be
automatically propagated. automatically propagated.
@comment file: calc++-parser.yy
@example @example
%locations %locations
%initial-action %initial-action
@@ -7209,6 +7226,7 @@ automatically propagated.
Use the two following directives to enable parser tracing and verbose Use the two following directives to enable parser tracing and verbose
error messages. error messages.
@comment file: calc++-parser.yy
@example @example
%debug %debug
%error-verbose %error-verbose
@@ -7218,6 +7236,7 @@ error messages.
Semantic values cannot use ``real'' objects, but only pointers to Semantic values cannot use ``real'' objects, but only pointers to
them. them.
@comment file: calc++-parser.yy
@example @example
// Symbols. // Symbols.
%union %union
@@ -7234,6 +7253,7 @@ of ``$end''. Similarly user friendly named are provided for each
symbol. Note that the tokens names are prefixed by @code{TOKEN_} to symbol. Note that the tokens names are prefixed by @code{TOKEN_} to
avoid name clashes. avoid name clashes.
@comment file: calc++-parser.yy
@example @example
%token YYEOF 0 "end of file" %token YYEOF 0 "end of file"
%token TOKEN_ASSIGN ":=" %token TOKEN_ASSIGN ":="
@@ -7246,6 +7266,7 @@ avoid name clashes.
To enable memory deallocation during error recovery, use To enable memory deallocation during error recovery, use
@code{%destructor}. @code{%destructor}.
@comment file: calc++-parser.yy
@example @example
%printer @{ debug_stream () << *$$; @} "identifier" %printer @{ debug_stream () << *$$; @} "identifier"
%destructor @{ delete $$; @} "identifier" %destructor @{ delete $$; @} "identifier"
@@ -7256,6 +7277,7 @@ To enable memory deallocation during error recovery, use
@noindent @noindent
The grammar itself is straightforward. The grammar itself is straightforward.
@comment file: calc++-parser.yy
@example @example
%% %%
%start unit; %start unit;
@@ -7281,9 +7303,11 @@ exp: exp '+' exp @{ $$ = $1 + $3; @}
Finally the @code{error} member function registers the errors to the Finally the @code{error} member function registers the errors to the
driver. driver.
@comment file: calc++-parser.yy
@example @example
void void
yy::calcxx_parser::error (const location_type& l, const std::string& m) yy::calcxx_parser::error (const yy::calcxx_parser::location_type& l,
const std::string& m)
@{ @{
driver.error (l, m); driver.error (l, m);
@} @}
@@ -7295,6 +7319,7 @@ yy::calcxx_parser::error (const location_type& l, const std::string& m)
The Flex scanner first includes the driver declaration, then the The Flex scanner first includes the driver declaration, then the
parser's to get the set of defined tokens. parser's to get the set of defined tokens.
@comment file: calc++-scanner.ll
@example @example
%@{ /* -*- C++ -*- */ %@{ /* -*- C++ -*- */
# include <string> # include <string>
@@ -7309,6 +7334,7 @@ Because there is no @code{#include}-like feature we don't need
actual file, this is not an interactive session with the user. actual file, this is not an interactive session with the user.
Finally we enable the scanner tracing features. Finally we enable the scanner tracing features.
@comment file: calc++-scanner.ll
@example @example
%option noyywrap nounput batch debug %option noyywrap nounput batch debug
@end example @end example
@@ -7316,6 +7342,7 @@ Finally we enable the scanner tracing features.
@noindent @noindent
Abbreviations allow for more readable rules. Abbreviations allow for more readable rules.
@comment file: calc++-scanner.ll
@example @example
id [a-zA-Z][a-zA-Z_0-9]* id [a-zA-Z][a-zA-Z_0-9]*
int [0-9]+ int [0-9]+
@@ -7331,6 +7358,7 @@ cursor is adjusted, and each time blanks are matched, the begin cursor
is moved onto the end cursor to effectively ignore the blanks is moved onto the end cursor to effectively ignore the blanks
preceding tokens. Comments would be treated equally. preceding tokens. Comments would be treated equally.
@comment file: calc++-scanner.ll
@example @example
%% %%
%@{ %@{
@@ -7345,6 +7373,7 @@ preceding tokens. Comments would be treated equally.
The rules are simple, just note the use of the driver to report The rules are simple, just note the use of the driver to report
errors. errors.
@comment file: calc++-scanner.ll
@example @example
[-+*/] return yytext[0]; [-+*/] return yytext[0];
":=" return TOKEN_ASSIGN; ":=" return TOKEN_ASSIGN;
@@ -7358,6 +7387,7 @@ errors.
Finally, because the scanner related driver's member function depend Finally, because the scanner related driver's member function depend
on the scanner's data, it is simpler to implement them in this file. on the scanner's data, it is simpler to implement them in this file.
@comment file: calc++-scanner.ll
@example @example
void void
calcxx_driver::scan_begin () calcxx_driver::scan_begin ()
@@ -7379,6 +7409,7 @@ calcxx_driver::scan_end ()
The top level file, @file{calc++.cc}, poses no problem. The top level file, @file{calc++.cc}, poses no problem.
@comment file: calc++.cc
@example @example
#include <iostream> #include <iostream>
#include "calc++-driver.hh" #include "calc++-driver.hh"

View File

@@ -13,3 +13,23 @@ clean:
calc++-parser.cc calc++-parser.hh \ calc++-parser.cc calc++-parser.hh \
calc++-scanner.cc \ calc++-scanner.cc \
calc++ calc++
## ------------ ##
## Extracting. ##
## ------------ ##
EXTRACTED = \
calc++-driver.hh calc++-driver.cc \
calc++-parser.yy \
calc++-scanner.ll \
calc++.cc
doc = ../../doc/bison.texinfo
extexi = gawk -f ../extexi
RECURSIVE_TARGETS += extract
$(EXTRACTED): $(doc) ../extexi
$(extexi) $(doc) -- $(EXTRACTED)
extract extract-am: $(EXTRACTED)

View File

@@ -1,9 +1,9 @@
#line 7140 "../../doc/bison.texinfo"
#include "calc++-driver.hh" #include "calc++-driver.hh"
#include "calc++-parser.hh" #include "calc++-parser.hh"
calcxx_driver::calcxx_driver () calcxx_driver::calcxx_driver ()
: trace_scanning (false), : trace_scanning (false), trace_parsing (false)
trace_parsing (false)
{ {
variables["one"] = 1; variables["one"] = 1;
variables["two"] = 2; variables["two"] = 2;

View File

@@ -1,64 +1,47 @@
#line 7036 "../../doc/bison.texinfo"
#ifndef CALCXX_DRIVER_HH #ifndef CALCXX_DRIVER_HH
# define CALCXX_DRIVER_HH # define CALCXX_DRIVER_HH
# include <string> # include <string>
# include <map> # include <map>
#line 7051 "../../doc/bison.texinfo"
/// Forward declarations. // Forward declarations.
union YYSTYPE; union YYSTYPE;
namespace yy namespace yy
{ {
class calcxx_parser;
class location; class location;
class calcxx_parser;
} }
class calcxx_driver; class calcxx_driver;
#line 7069 "../../doc/bison.texinfo"
// Announce to Flex the prototype we want for lexing function, ... // Announce to Flex the prototype we want for lexing function, ...
# define YY_DECL \ # define YY_DECL \
int yylex (YYSTYPE* yylval, yy::location* yylloc, calcxx_driver& driver) int yylex (YYSTYPE* yylval, yy::location* yylloc, calcxx_driver& driver)
// ... and declare it for the parser's sake. // ... and declare it for the parser's sake.
YY_DECL; YY_DECL;
#line 7082 "../../doc/bison.texinfo"
/// Conducting the whole scanning and parsing of Calc++. // Conducting the whole scanning and parsing of Calc++.
class calcxx_driver class calcxx_driver
{ {
public: public:
calcxx_driver (); calcxx_driver ();
virtual ~calcxx_driver (); virtual ~calcxx_driver ();
/// The variables.
std::map<std::string, int> variables; std::map<std::string, int> variables;
/// \name Handling the scanner.
/// \{
/// Open \a file for scanning.
void scan_begin ();
/// End scanning, clean up memory.
void scan_end ();
/// Whether to enable scanner traces.
bool trace_scanning;
/// \}
/// \name Handling the parser.
/// \{
/// Parse the file \a f.
void parse (const std::string& f);
/// The file being parsed.
std::string file;
/// Whether to enable parsing traces.
bool trace_parsing;
/// \}
/// The result.
int result; int result;
#line 7101 "../../doc/bison.texinfo"
/// \name Error handling. // Handling the scanner.
/// \{ void scan_begin ();
/// Register a located error. void scan_end ();
bool trace_scanning;
#line 7112 "../../doc/bison.texinfo"
// Handling the parser.
void parse (const std::string& f);
std::string file;
bool trace_parsing;
#line 7126 "../../doc/bison.texinfo"
// Error handling.
void error (const yy::location& l, const std::string& m); void error (const yy::location& l, const std::string& m);
/// Register an error.
void error (const std::string& m); void error (const std::string& m);
/// \}
}; };
#endif #endif // ! CALCXX_DRIVER_HH

View File

@@ -1,48 +1,44 @@
%skeleton "lalr1.cc" /* -*- C++ -*- */ #line 7188 "../../doc/bison.texinfo"
%skeleton "lalr1.cc" /* -*- C++ -*- */
%define "parser_class_name" "calcxx_parser" %define "parser_class_name" "calcxx_parser"
%defines %defines
%{ %{
# include <string> # include <string>
# include "calc++-driver.hh" # include "calc++-driver.hh"
%} %}
#line 7204 "../../doc/bison.texinfo"
%error-verbose
// The parsing context. // The parsing context.
%parse-param { calcxx_driver& driver } %parse-param { calcxx_driver& driver }
%lex-param { calcxx_driver& driver } %lex-param { calcxx_driver& driver }
#line 7217 "../../doc/bison.texinfo"
%locations %locations
%initial-action %initial-action
{ {
// Initialize the initial location. // Initialize the initial location.
@$.begin.filename = @$.end.filename = &driver.file; @$.begin.filename = @$.end.filename = &driver.file;
}; };
#line 7231 "../../doc/bison.texinfo"
// Define yydebug.
%debug %debug
%error-verbose
#line 7241 "../../doc/bison.texinfo"
// Symbols. // Symbols.
%union %union
{ {
/// Value of a numeric literal.
int ival; int ival;
/// Name of a variable.
std::string *sval; std::string *sval;
}; };
#line 7258 "../../doc/bison.texinfo"
%token YYEOF 0 "end of file" %token YYEOF 0 "end of file"
%token TOKEN_ASSIGN ":=" %token TOKEN_ASSIGN ":="
%token <sval> TOKEN_IDENTIFIER "identifier" %token <sval> TOKEN_IDENTIFIER "identifier"
%token <ival> TOKEN_NUMBER "number" %token <ival> TOKEN_NUMBER "number"
%type <ival> exp "expression" %type <ival> exp "expression"
#line 7271 "../../doc/bison.texinfo"
%printer { debug_stream () << *$$; } "identifier" %printer { debug_stream () << *$$; } "identifier"
%destructor { delete $$; } "identifier" %destructor { delete $$; } "identifier"
%printer { debug_stream () << $$; } "number" "expression" %printer { debug_stream () << $$; } "number" "expression"
#line 7282 "../../doc/bison.texinfo"
%% %%
%start unit; %start unit;
unit: assignments exp { driver.result = $2; }; unit: assignments exp { driver.result = $2; };
@@ -61,8 +57,10 @@ exp: exp '+' exp { $$ = $1 + $3; }
| TOKEN_IDENTIFIER { $$ = driver.variables[*$1]; } | TOKEN_IDENTIFIER { $$ = driver.variables[*$1]; }
| TOKEN_NUMBER { $$ = $1; }; | TOKEN_NUMBER { $$ = $1; };
%% %%
#line 7308 "../../doc/bison.texinfo"
void void
yy::calcxx_parser::error (const location& l, const std::string& m) yy::calcxx_parser::error (const yy::calcxx_parser::location_type& l,
const std::string& m)
{ {
driver.error (l, m); driver.error (l, m);
} }

View File

@@ -1,33 +1,28 @@
%{ /* -*- C++ -*- */ %{ /* -*- C++ -*- */
# include <string> # include <string>
# include <cerrno>
# include "calc++-driver.hh" # include "calc++-driver.hh"
# include "calc++-parser.hh" # include "calc++-parser.hh"
%} %}
%option noyywrap nounput debug batch %option noyywrap nounput batch debug
id [a-zA-Z][a-zA-Z_0-9]* id [a-zA-Z][a-zA-Z_0-9]*
int [0-9]+ int [0-9]+
blank [ \t] blank [ \t]
%% %%
%{ %{
# define YY_USER_ACTION yylloc->columns (yyleng);
yylloc->step (); yylloc->step ();
# define YY_USER_ACTION yylloc->columns (yyleng);
%} %}
{blank}+ yylloc->step (); {blank}+ yylloc->step ();
[\n]+ yylloc->lines (yyleng); yylloc->step (); [\n]+ yylloc->lines (yyleng); yylloc->step ();
[-+*/] return yytext[0]; [-+*/] return yytext[0];
":=" return TOKEN_ASSIGN; ":=" return TOKEN_ASSIGN;
{int} yylval->ival = atoi (yytext); return TOKEN_NUMBER; {int} yylval->ival = atoi (yytext); return TOKEN_NUMBER;
{id} yylval->sval = new std::string (yytext); return TOKEN_IDENTIFIER; {id} yylval->sval = new std::string (yytext); return TOKEN_IDENTIFIER;
. driver.error (*yylloc, "invalid character"); . driver.error (*yylloc, "invalid character");
%% %%
void void

View File

@@ -1,3 +1,4 @@
#line 7414 "../../doc/bison.texinfo"
#include <iostream> #include <iostream>
#include "calc++-driver.hh" #include "calc++-driver.hh"

133
examples/extexi Normal file
View File

@@ -0,0 +1,133 @@
# Extract all examples from the manual source. -*- AWK -*-
# This file is part of GNU M4
# Copyright 1992, 2000, 2001 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307 USA
# This script is for use with any New AWK.
# Well, now it uses ARGV/ARGC, and I don't know if it's New AWK portable.
#
# Usage: extexi input-file.texi ... -- [FILES to extract]
BEGIN {
if (!output_dir)
output_dir = ".";
for (argc = 1; argc < ARGC; ++argc)
if (ARGV[argc] == "--")
break;
for (i = argc + 1; i < ARGC; ++i)
file_wanted[ARGV[i]] = 1;
ARGC = argc;
}
/^@node / {
if (seq > 0)
print "AT_CLEANUP";
split ($0, tmp, ",");
node = substr(tmp[1], 7);
seq = 0;
}
/^@comment file: / {
if (!file_wanted[$3])
message("ignoring " $3);
else
{
message("extracting " $3);
file = $3;
}
}
/^@example$/, /^@end example$/ {
if (!file)
next;
if ($0 ~ /^@example$/)
{
input = files_output[file] ? "\n" : "";
# FNR is starting at 0 instead of 1, and
# #line report the line number of the *next* line.
# => + 2.
# Note that recent Bison support it, but not Flex.
if (file ~ /\.[chy]*$/)
input = "#line " (FNR + 1) " \"" FILENAME "\"\n";
next;
}
if ($0 ~ /^@end example$/)
{
if (input == "")
fatal("no contents: " file);
input = normalize(input);
# No spurious end of line: use printf.
if (files_output[file])
printf ("%s", input) >> output_dir "/" file;
else
printf ("%s", input) > output_dir "/" file;
close (output_dir "/" file);
files_output[file] = 1;
file = input = "";
next;
}
input = input $0 "\n";
}
# We have to handle CONTENTS line per line, since anchors in AWK are
# referring to the whole string, not the lines.
function normalize(contents, i, lines, n, line, res) {
# Remove the Texinfo tags.
n = split (contents, lines, "\n");
# We don't want the last field which empty: it's behind the last \n.
for (i = 1; i < n; ++i)
{
line = lines[i];
# Whole line commands.
if (line ~ /^@(c |comment|dots|end (ignore|group)|ignore|group)/)
# Gperf accepts empty lines as valid input!!!
if (file ~ /\.gperf$/)
continue;
else
line = "";
gsub (/^@result\{\}/, "", line);
gsub (/^@error\{\}/, "", line);
gsub ("@[{]", "{", line);
gsub ("@}", "}", line);
gsub ("@@", "@", line);
gsub ("@comment.*", "", line);
res = res line "\n";
}
return res;
}
function message(msg) {
# FNR starts at 0 instead of 1 for line numbers.
print "extexi: " FILENAME ":" (FNR + 1) ": " msg > "/dev/stderr";
}
function fatal(msg) {
message(msg);
exit 1
}