mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-21 10:13:03 +00:00
doc: an introductory example for C++
Suggested by Victor Khomenko. http://lists.gnu.org/archive/html/bug-bison/2018-08/msg00037.html * doc/bison.texi (A Simple C++ Example): New. * examples/c++/local.mk, examples/c++/simple.test: New. Extract, check, and install this new example. * examples/local.mk: Adjust. * examples/test: Adjust to the case where the dirname differs from the test name.
This commit is contained in:
@@ -80,6 +80,7 @@ BISON_CXXSTD([14])
|
|||||||
BISON_CXXSTD([17])
|
BISON_CXXSTD([17])
|
||||||
BISON_CXXSTD([2a])
|
BISON_CXXSTD([2a])
|
||||||
AM_CONDITIONAL([ENABLE_CXX11], [test x"$CXX11_CXXFLAGS" != x])
|
AM_CONDITIONAL([ENABLE_CXX11], [test x"$CXX11_CXXFLAGS" != x])
|
||||||
|
AM_CONDITIONAL([ENABLE_CXX14], [test x"$CXX14_CXXFLAGS" != x])
|
||||||
AC_LANG_POP([C++])
|
AC_LANG_POP([C++])
|
||||||
|
|
||||||
AC_ARG_ENABLE([gcc-warnings],
|
AC_ARG_ENABLE([gcc-warnings],
|
||||||
@@ -161,7 +162,7 @@ if test "$enable_gcc_warnings" = yes; then
|
|||||||
do
|
do
|
||||||
gl_WARN_ADD([$i], [WARN_CXXFLAGS_TEST])
|
gl_WARN_ADD([$i], [WARN_CXXFLAGS_TEST])
|
||||||
done
|
done
|
||||||
# Clang++ 3.2+ reject C code generated by Flex.
|
# Clang++ 3.2+ rejects C code generated by Flex.
|
||||||
gl_WARN_ADD([-Wno-null-conversion], [FLEX_SCANNER_CXXFLAGS])
|
gl_WARN_ADD([-Wno-null-conversion], [FLEX_SCANNER_CXXFLAGS])
|
||||||
# So does G++ 4.8...
|
# So does G++ 4.8...
|
||||||
gl_WARN_ADD([-Wno-sign-compare], [FLEX_SCANNER_CXXFLAGS])
|
gl_WARN_ADD([-Wno-sign-compare], [FLEX_SCANNER_CXXFLAGS])
|
||||||
|
|||||||
229
doc/bison.texi
229
doc/bison.texi
@@ -338,6 +338,7 @@ Parsers Written In Other Languages
|
|||||||
|
|
||||||
C++ Parsers
|
C++ Parsers
|
||||||
|
|
||||||
|
* A Simple C++ Example:: A short introduction to C++ parsers
|
||||||
* C++ Bison Interface:: Asking for C++ parser generation
|
* C++ Bison Interface:: Asking for C++ parser generation
|
||||||
* C++ Parser Interface:: Instantiating and running the parser
|
* C++ Parser Interface:: Instantiating and running the parser
|
||||||
* C++ Semantic Values:: %union vs. C++
|
* C++ Semantic Values:: %union vs. C++
|
||||||
@@ -10610,6 +10611,7 @@ The Bison parser in C++ is an object, an instance of the class
|
|||||||
@code{yy::parser}.
|
@code{yy::parser}.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
|
* A Simple C++ Example:: A short introduction to C++ parsers
|
||||||
* C++ Bison Interface:: Asking for C++ parser generation
|
* C++ Bison Interface:: Asking for C++ parser generation
|
||||||
* C++ Parser Interface:: Instantiating and running the parser
|
* C++ Parser Interface:: Instantiating and running the parser
|
||||||
* C++ Semantic Values:: %union vs. C++
|
* C++ Semantic Values:: %union vs. C++
|
||||||
@@ -10618,6 +10620,224 @@ The Bison parser in C++ is an object, an instance of the class
|
|||||||
* A Complete C++ Example:: Demonstrating their use
|
* A Complete C++ Example:: Demonstrating their use
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
@node A Simple C++ Example
|
||||||
|
@subsection A Simple C++ Example
|
||||||
|
|
||||||
|
This tutorial about C++ parsers is based on a simple, self contained
|
||||||
|
example. The following sections are the reference manual for Bison with
|
||||||
|
C++, the last one showing a fully blown example (@pxref{A Complete C++
|
||||||
|
Example}).
|
||||||
|
|
||||||
|
To look nicer, our example will be in C++14. It is not required: Bison
|
||||||
|
supports the original C++98 standard.
|
||||||
|
|
||||||
|
A Bison file has three parts. In the first part, the prologue, we start by
|
||||||
|
making sure we run a version of Bison which is recent enough, and that we
|
||||||
|
generate C++.
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%require "@value{VERSION}"
|
||||||
|
%language "c++"
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Let's dive directly into the middle part: the grammar. Our input is a
|
||||||
|
simple list of strings, that we display once the parsing is done.
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 2
|
||||||
|
@example
|
||||||
|
%%
|
||||||
|
@group
|
||||||
|
result:
|
||||||
|
list @{ std::cout << $1 << '\n'; @}
|
||||||
|
;
|
||||||
|
@end group
|
||||||
|
|
||||||
|
%type <std::vector<std::string>> list;
|
||||||
|
@group
|
||||||
|
list:
|
||||||
|
%empty @{ /* Generates an empty string list */ @}
|
||||||
|
| list item @{ $$ = $1; $$.push_back ($2); @}
|
||||||
|
;
|
||||||
|
@end group
|
||||||
|
@end example
|
||||||
|
|
||||||
|
We used a vector of strings as a semantic value! To use genuine C++ objects
|
||||||
|
as semantic values---not just PODs---we cannot rely on the union that Bison
|
||||||
|
uses by default to store them, we need @emph{variants} (@pxref{C++
|
||||||
|
Variants}):
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%define api.value.type variant
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Our list of strings will be built from two types of items: numbers and
|
||||||
|
strings:
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 2
|
||||||
|
@example
|
||||||
|
%type <std::string> item;
|
||||||
|
%token <std::string> TEXT;
|
||||||
|
%token <int> NUMBER;
|
||||||
|
@group
|
||||||
|
item:
|
||||||
|
TEXT
|
||||||
|
| NUMBER @{ $$ = to_string ($1); @}
|
||||||
|
;
|
||||||
|
@end group
|
||||||
|
@end example
|
||||||
|
|
||||||
|
In the case of @code{TEXT}, the implicit default action applies: @w{@code{$$
|
||||||
|
= $1}.} We recommend that you keep the actions simple, and move details
|
||||||
|
into auxiliary functions, as we did with @code{to_string}, which we
|
||||||
|
implement in the prologue as follows:
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%code
|
||||||
|
@{
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
@group
|
||||||
|
// Convert to string.
|
||||||
|
template <typename T>
|
||||||
|
auto to_string (const T& t) -> std::string
|
||||||
|
@{
|
||||||
|
std::ostringstream o;
|
||||||
|
o << t;
|
||||||
|
return o.str ();
|
||||||
|
@}
|
||||||
|
@end group
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Obviously, the rule for @code{result} needs to print a vector of strings.
|
||||||
|
Again, in the prologue, we add:
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%code
|
||||||
|
@{
|
||||||
|
// Print a list of strings.
|
||||||
|
auto
|
||||||
|
operator<< (std::ostream& o, const std::vector<std::string>& ss)
|
||||||
|
-> std::ostream&
|
||||||
|
@{
|
||||||
|
o << '@{';
|
||||||
|
const char *sep = "";
|
||||||
|
@group
|
||||||
|
for (const auto& s: ss)
|
||||||
|
@{
|
||||||
|
o << sep << s;
|
||||||
|
sep = ", ";
|
||||||
|
@}
|
||||||
|
@end group
|
||||||
|
return o << '@}';
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
You may want to move it into the @code{yy} namespace to avoid leaking it in
|
||||||
|
your default namespace.
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
|
||||||
|
Our scanner deserves some attention. The traditional interface of
|
||||||
|
@code{yylex} is not type safe: since the token type and the token value are
|
||||||
|
not correlated, you may return a @code{NUMBER} with a string as semantic
|
||||||
|
value. To avoid this, we use @emph{token constructors} (@pxref{Complete
|
||||||
|
Symbols}). This directive:
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%define api.token.constructor
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
requests that Bison generates the functions @code{make_TEXT} and
|
||||||
|
@code{make_NUMBER}. As a matter of fact, it is convenient to have also a
|
||||||
|
symbol to mark the end of input, say @code{END_OF_FILE}:
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%token END_OF_FILE 0
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The @code{0} tells Bison this token is special: when it is reached, parsing
|
||||||
|
finishes.
|
||||||
|
|
||||||
|
Everything is in place for our scanner:
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 1
|
||||||
|
@example
|
||||||
|
%code
|
||||||
|
@{
|
||||||
|
namespace yy
|
||||||
|
@{
|
||||||
|
// Return the next token.
|
||||||
|
auto yylex () -> parser::symbol_type
|
||||||
|
@{
|
||||||
|
static int count = 0;
|
||||||
|
switch (int stage = count++)
|
||||||
|
@{
|
||||||
|
@group
|
||||||
|
case 0:
|
||||||
|
return parser::make_TEXT ("I have three numbers for you.");
|
||||||
|
@end group
|
||||||
|
@group
|
||||||
|
case 1: case 2: case 3:
|
||||||
|
return parser::make_NUMBER (stage);
|
||||||
|
@end group
|
||||||
|
@group
|
||||||
|
case 4:
|
||||||
|
return parser::make_TEXT ("And that's all!");
|
||||||
|
@end group
|
||||||
|
@group
|
||||||
|
default:
|
||||||
|
return parser::make_END_OF_FILE ();
|
||||||
|
@end group
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
In the epilogue, the third part of a Bison grammar file, we leave simple
|
||||||
|
details: the error reporting function, and the main function.
|
||||||
|
|
||||||
|
@comment file: c++/simple.yy: 3
|
||||||
|
@example
|
||||||
|
%%
|
||||||
|
namespace yy
|
||||||
|
@{
|
||||||
|
// Report an error to the user.
|
||||||
|
auto parser::error (const std::string& msg) -> void
|
||||||
|
@{
|
||||||
|
std::cerr << msg << '\n';
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
|
||||||
|
int main ()
|
||||||
|
@{
|
||||||
|
yy::parser parse;
|
||||||
|
return parse ();
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Compile, and run!
|
||||||
|
|
||||||
|
@example
|
||||||
|
$ bison simple.yy -o simple.cc
|
||||||
|
$ g++ -std=c++14 simple.cc -o simple
|
||||||
|
@group
|
||||||
|
$ ./simple
|
||||||
|
@{I have three numbers for you., 1, 2, 3, And that's all!@}
|
||||||
|
@end group
|
||||||
|
@end example
|
||||||
|
|
||||||
@node C++ Bison Interface
|
@node C++ Bison Interface
|
||||||
@subsection C++ Bison Interface
|
@subsection C++ Bison Interface
|
||||||
@c - %skeleton "lalr1.cc"
|
@c - %skeleton "lalr1.cc"
|
||||||
@@ -13760,17 +13980,18 @@ London, Department of Computer Science, TR-00-12 (December 2000).
|
|||||||
@c LocalWords: subdirectory Solaris nonassociativity perror schemas Malloy ints
|
@c LocalWords: subdirectory Solaris nonassociativity perror schemas Malloy ints
|
||||||
@c LocalWords: Scannerless ispell american ChangeLog smallexample CSTYPE CLTYPE
|
@c LocalWords: Scannerless ispell american ChangeLog smallexample CSTYPE CLTYPE
|
||||||
@c LocalWords: clval CDEBUG cdebug deftypeopx yyterminate LocationType yyo
|
@c LocalWords: clval CDEBUG cdebug deftypeopx yyterminate LocationType yyo
|
||||||
@c LocalWords: parsers parser's documentencoding documentlanguage Wempty
|
@c LocalWords: parsers parser's documentencoding documentlanguage Wempty ss
|
||||||
@c LocalWords: associativity subclasses precedences unresolvable runnable
|
@c LocalWords: associativity subclasses precedences unresolvable runnable
|
||||||
@c LocalWords: allocators subunit initializations unreferenced untyped dir
|
@c LocalWords: allocators subunit initializations unreferenced untyped dir
|
||||||
@c LocalWords: errorVerbose subtype subtypes Wmidrule midrule's src rvalues
|
@c LocalWords: errorVerbose subtype subtypes Wmidrule midrule's src rvalues
|
||||||
@c LocalWords: automove evolutions Wother Wconflicts PNG lookaheads Acc
|
@c LocalWords: automove evolutions Wother Wconflicts PNG lookaheads Acc sep
|
||||||
@c LocalWords: xsltproc XSL xsl xhtml html num Wprecedence Werror fcaret
|
@c LocalWords: xsltproc XSL xsl xhtml html num Wprecedence Werror fcaret
|
||||||
@c LocalWords: fdiagnostics setlocale nullptr ast srcdir copyable iff drv
|
@c LocalWords: fdiagnostics setlocale nullptr ast srcdir copyable iff drv
|
||||||
@c LocalWords: deftypefunx pragma Wnull dereference Wdocumentation elif
|
@c LocalWords: deftypefunx pragma Wnull dereference Wdocumentation elif ish
|
||||||
@c LocalWords: Wdeprecated Wregister noinput yyloc yypos
|
@c LocalWords: Wdeprecated Wregister noinput yyloc yypos PODs sstream Wsign
|
||||||
|
|
||||||
@c Local Variables:
|
@c Local Variables:
|
||||||
@c ispell-dictionary: "american"
|
@c ispell-dictionary: "american"
|
||||||
@c fill-column: 76
|
@c fill-column: 76
|
||||||
@c End:
|
@c End:
|
||||||
|
@c LocalWords: typename emplace Wconversion Wshorten
|
||||||
|
|||||||
45
examples/c++/local.mk
Normal file
45
examples/c++/local.mk
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
## Copyright (C) 2018 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
## -------------------- ##
|
||||||
|
## Building & testing. ##
|
||||||
|
## -------------------- ##
|
||||||
|
|
||||||
|
BUILT_SOURCES += $(simple_sources)
|
||||||
|
CLEANFILES += %D%/simple.[ch] %D%/simple.output
|
||||||
|
CLEANDIRS += %D%/*.dSYM
|
||||||
|
|
||||||
|
simple_extracted = %D%/simple.yy
|
||||||
|
simple_sources = $(simple_extracted)
|
||||||
|
extracted += $(simple_extracted)
|
||||||
|
|
||||||
|
if ENABLE_CXX14
|
||||||
|
check_PROGRAMS += %D%/simple
|
||||||
|
nodist_%C%_simple_SOURCES = $(simple_sources)
|
||||||
|
|
||||||
|
%C%_simple_CXXFLAGS = $(CXX11_CXXFLAGS)
|
||||||
|
# Don't use gnulib's system headers.
|
||||||
|
%C%_simple_CPPFLAGS = -I$(top_builddir)
|
||||||
|
dist_TESTS += %D%/simple.test
|
||||||
|
%D%/simple.cc: $(BISON_IN) $(dist_pkgdata_DATA)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
## ------------ ##
|
||||||
|
## Installing. ##
|
||||||
|
## ------------ ##
|
||||||
|
|
||||||
|
cxxdir = $(docdir)/examples/c++
|
||||||
|
cxx_DATA = $(simple_extracted)
|
||||||
4
examples/c++/simple.test
Normal file
4
examples/c++/simple.test
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
: >input
|
||||||
|
run 0 "{I have three numbers for you., 1, 2, 3, And that's all!}"
|
||||||
@@ -69,6 +69,7 @@ check_PROGRAMS += %D%/calc++
|
|||||||
nodist_%C%_calc___SOURCES = \
|
nodist_%C%_calc___SOURCES = \
|
||||||
$(calcxx_sources)
|
$(calcxx_sources)
|
||||||
|
|
||||||
|
# Don't use gnulib's system headers.
|
||||||
%C%_calc___CPPFLAGS = -I$(top_builddir)/%D%
|
%C%_calc___CPPFLAGS = -I$(top_builddir)/%D%
|
||||||
%C%_calc___CXXFLAGS = $(AM_CXXFLAGS) $(FLEX_SCANNER_CXXFLAGS)
|
%C%_calc___CXXFLAGS = $(AM_CXXFLAGS) $(FLEX_SCANNER_CXXFLAGS)
|
||||||
dist_TESTS += %D%/calc++.test
|
dist_TESTS += %D%/calc++.test
|
||||||
|
|||||||
@@ -69,5 +69,6 @@ CLEANFILES += %D%/variant.output %D%/variant-11.output
|
|||||||
CLEANDIRS += %D%/*.dSYM
|
CLEANDIRS += %D%/*.dSYM
|
||||||
|
|
||||||
include %D%/calc++/local.mk
|
include %D%/calc++/local.mk
|
||||||
|
include %D%/c++/local.mk
|
||||||
include %D%/mfcalc/local.mk
|
include %D%/mfcalc/local.mk
|
||||||
include %D%/rpcalc/local.mk
|
include %D%/rpcalc/local.mk
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ extracted += $(mfcalc_extracted)
|
|||||||
|
|
||||||
check_PROGRAMS += %D%/mfcalc
|
check_PROGRAMS += %D%/mfcalc
|
||||||
nodist_%C%_mfcalc_SOURCES = $(mfcalc_sources)
|
nodist_%C%_mfcalc_SOURCES = $(mfcalc_sources)
|
||||||
|
# Don't use gnulib's system headers.
|
||||||
%C%_mfcalc_CPPFLAGS = -I$(top_builddir)/%D%
|
%C%_mfcalc_CPPFLAGS = -I$(top_builddir)/%D%
|
||||||
%C%_mfcalc_LDADD = -lm
|
%C%_mfcalc_LDADD = -lm
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ extracted += $(rpcalc_extracted)
|
|||||||
|
|
||||||
check_PROGRAMS += %D%/rpcalc
|
check_PROGRAMS += %D%/rpcalc
|
||||||
nodist_%C%_rpcalc_SOURCES = $(rpcalc_sources)
|
nodist_%C%_rpcalc_SOURCES = $(rpcalc_sources)
|
||||||
|
# Don't use gnulib's system headers.
|
||||||
%C%_rpcalc_CPPFLAGS = -I$(top_builddir)/%D%
|
%C%_rpcalc_CPPFLAGS = -I$(top_builddir)/%D%
|
||||||
%C%_rpcalc_LDADD = -lm
|
%C%_rpcalc_LDADD = -lm
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
me=`basename $1 .test`
|
me=$(basename "$1" .test)
|
||||||
|
medir=$(basename "$(dirname "$1")")
|
||||||
|
|
||||||
# Number of the current test.
|
# Number of the current test.
|
||||||
number=1
|
number=1
|
||||||
@@ -27,7 +28,7 @@ exit=true
|
|||||||
cwd=`pwd`
|
cwd=`pwd`
|
||||||
|
|
||||||
# The exercised program.
|
# The exercised program.
|
||||||
for p in $cwd/examples/$me/$me $cwd/examples/$me
|
for p in "$cwd/examples/$medir/$me" "$cwd/examples/$me"
|
||||||
do
|
do
|
||||||
if test -x "$p"; then
|
if test -x "$p"; then
|
||||||
prog=$p
|
prog=$p
|
||||||
|
|||||||
Reference in New Issue
Block a user