[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
doc: an introductory example for C++
From: |
Akim Demaille |
Subject: |
doc: an introductory example for C++ |
Date: |
Wed, 24 Oct 2018 06:22:36 +0200 |
commit a4dce889a44a7d363ce670585704876857543a5e
Author: Akim Demaille <address@hidden>
Date: Tue Oct 23 20:14:47 2018 +0200
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.
diff --git a/configure.ac b/configure.ac
index f2240100..c81446ca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -80,6 +80,7 @@ BISON_CXXSTD([14])
BISON_CXXSTD([17])
BISON_CXXSTD([2a])
AM_CONDITIONAL([ENABLE_CXX11], [test x"$CXX11_CXXFLAGS" != x])
+AM_CONDITIONAL([ENABLE_CXX14], [test x"$CXX14_CXXFLAGS" != x])
AC_LANG_POP([C++])
AC_ARG_ENABLE([gcc-warnings],
@@ -161,7 +162,7 @@ if test "$enable_gcc_warnings" = yes; then
do
gl_WARN_ADD([$i], [WARN_CXXFLAGS_TEST])
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])
# So does G++ 4.8...
gl_WARN_ADD([-Wno-sign-compare], [FLEX_SCANNER_CXXFLAGS])
diff --git a/doc/bison.texi b/doc/bison.texi
index ac2165ed..f6d240da 100644
--- a/doc/bison.texi
+++ b/doc/bison.texi
@@ -338,6 +338,7 @@ Parsers Written In Other Languages
C++ Parsers
+* A Simple C++ Example:: A short introduction to C++ parsers
* C++ Bison Interface:: Asking for C++ parser generation
* C++ Parser Interface:: Instantiating and running the parser
* 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}.
@menu
+* A Simple C++ Example:: A short introduction to C++ parsers
* C++ Bison Interface:: Asking for C++ parser generation
* C++ Parser Interface:: Instantiating and running the parser
* 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
@end menu
address@hidden A Simple C++ Example
address@hidden 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++.
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%require "@value{VERSION}"
+%language "c++"
address@hidden 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.
+
address@hidden file: c++/simple.yy: 2
address@hidden
+%%
address@hidden
+result:
+ list @{ std::cout << $1 << '\n'; @}
+;
address@hidden group
+
+%type <std::vector<std::string>> list;
address@hidden
+list:
+ %empty @{ /* Generates an empty string list */ @}
+| list item @{ $$ = $1; $$.push_back ($2); @}
+;
address@hidden group
address@hidden 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}):
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%define api.value.type variant
address@hidden example
+
+Our list of strings will be built from two types of items: numbers and
+strings:
+
address@hidden file: c++/simple.yy: 2
address@hidden
+%type <std::string> item;
+%token <std::string> TEXT;
+%token <int> NUMBER;
address@hidden
+item:
+ TEXT
+| NUMBER @{ $$ = to_string ($1); @}
+;
address@hidden group
address@hidden example
+
+In the case of @code{TEXT}, the implicit default action applies:
@address@hidden
+= $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:
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%code
address@hidden
+ #include <sstream>
+
address@hidden
+ // Convert to string.
+ template <typename T>
+ auto to_string (const T& t) -> std::string
+ @{
+ std::ostringstream o;
+ o << t;
+ return o.str ();
+ @}
address@hidden group
address@hidden
address@hidden example
+
+Obviously, the rule for @code{result} needs to print a vector of strings.
+Again, in the prologue, we add:
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%code
address@hidden
+ // Print a list of strings.
+ auto
+ operator<< (std::ostream& o, const std::vector<std::string>& ss)
+ -> std::ostream&
+ @{
+ o << '@{';
+ const char *sep = "";
address@hidden
+ for (const auto& s: ss)
+ @{
+ o << sep << s;
+ sep = ", ";
+ @}
address@hidden group
+ return o << '@}';
+ @}
address@hidden
address@hidden example
+
address@hidden
+You may want to move it into the @code{yy} namespace to avoid leaking it in
+your default namespace.
+
address@hidden 1
+
+Our scanner deserves some attention. The traditional interface of
address@hidden 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:
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%define api.token.constructor
address@hidden example
+
address@hidden
+requests that Bison generates the functions @code{make_TEXT} and
address@hidden As a matter of fact, it is convenient to have also a
+symbol to mark the end of input, say @code{END_OF_FILE}:
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%token END_OF_FILE 0
address@hidden example
+
address@hidden
+The @code{0} tells Bison this token is special: when it is reached, parsing
+finishes.
+
+Everything is in place for our scanner:
+
address@hidden file: c++/simple.yy: 1
address@hidden
+%code
address@hidden
+ namespace yy
+ @{
+ // Return the next token.
+ auto yylex () -> parser::symbol_type
+ @{
+ static int count = 0;
+ switch (int stage = count++)
+ @{
address@hidden
+ case 0:
+ return parser::make_TEXT ("I have three numbers for you.");
address@hidden group
address@hidden
+ case 1: case 2: case 3:
+ return parser::make_NUMBER (stage);
address@hidden group
address@hidden
+ case 4:
+ return parser::make_TEXT ("And that's all!");
address@hidden group
address@hidden
+ default:
+ return parser::make_END_OF_FILE ();
address@hidden group
+ @}
+ @}
+ @}
address@hidden
address@hidden example
+
+In the epilogue, the third part of a Bison grammar file, we leave simple
+details: the error reporting function, and the main function.
+
address@hidden file: c++/simple.yy: 3
address@hidden
+%%
+namespace yy
address@hidden
+ // Report an error to the user.
+ auto parser::error (const std::string& msg) -> void
+ @{
+ std::cerr << msg << '\n';
+ @}
address@hidden
+
+int main ()
address@hidden
+ yy::parser parse;
+ return parse ();
address@hidden
address@hidden example
+
+Compile, and run!
+
address@hidden
+$ bison simple.yy -o simple.cc
+$ g++ -std=c++14 simple.cc -o simple
address@hidden
+$ ./simple
address@hidden have three numbers for you., 1, 2, 3, And that's address@hidden
address@hidden group
address@hidden example
+
@node C++ Bison Interface
@subsection C++ Bison Interface
@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: Scannerless ispell american ChangeLog smallexample CSTYPE CLTYPE
@c LocalWords: clval CDEBUG cdebug deftypeopx yyterminate LocationType yyo
address@hidden LocalWords: parsers parser's documentencoding documentlanguage
Wempty
address@hidden LocalWords: parsers parser's documentencoding documentlanguage
Wempty ss
@c LocalWords: associativity subclasses precedences unresolvable runnable
@c LocalWords: allocators subunit initializations unreferenced untyped dir
@c LocalWords: errorVerbose subtype subtypes Wmidrule midrule's src rvalues
address@hidden LocalWords: automove evolutions Wother Wconflicts PNG lookaheads
Acc
address@hidden LocalWords: automove evolutions Wother Wconflicts PNG lookaheads
Acc sep
@c LocalWords: xsltproc XSL xsl xhtml html num Wprecedence Werror fcaret
@c LocalWords: fdiagnostics setlocale nullptr ast srcdir copyable iff drv
address@hidden LocalWords: deftypefunx pragma Wnull dereference Wdocumentation
elif
address@hidden LocalWords: Wdeprecated Wregister noinput yyloc yypos
address@hidden LocalWords: deftypefunx pragma Wnull dereference Wdocumentation
elif ish
address@hidden LocalWords: Wdeprecated Wregister noinput yyloc yypos PODs
sstream Wsign
@c Local Variables:
@c ispell-dictionary: "american"
@c fill-column: 76
@c End:
address@hidden LocalWords: typename emplace Wconversion Wshorten
diff --git a/examples/c++/local.mk b/examples/c++/local.mk
new file mode 100644
index 00000000..438decba
--- /dev/null
+++ b/examples/c++/local.mk
@@ -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)
diff --git a/examples/c++/simple.test b/examples/c++/simple.test
new file mode 100644
index 00000000..8300c9dc
--- /dev/null
+++ b/examples/c++/simple.test
@@ -0,0 +1,4 @@
+#! /bin/sh
+
+: >input
+run 0 "{I have three numbers for you., 1, 2, 3, And that's all!}"
diff --git a/examples/calc++/local.mk b/examples/calc++/local.mk
index e0458dba..6809188e 100644
--- a/examples/calc++/local.mk
+++ b/examples/calc++/local.mk
@@ -69,6 +69,7 @@ check_PROGRAMS += %D%/calc++
nodist_%C%_calc___SOURCES = \
$(calcxx_sources)
+# Don't use gnulib's system headers.
%C%_calc___CPPFLAGS = -I$(top_builddir)/%D%
%C%_calc___CXXFLAGS = $(AM_CXXFLAGS) $(FLEX_SCANNER_CXXFLAGS)
dist_TESTS += %D%/calc++.test
diff --git a/examples/local.mk b/examples/local.mk
index 2e653363..81ecbfab 100644
--- a/examples/local.mk
+++ b/examples/local.mk
@@ -69,5 +69,6 @@ CLEANFILES += %D%/variant.output %D%/variant-11.output
CLEANDIRS += %D%/*.dSYM
include %D%/calc++/local.mk
+include %D%/c++/local.mk
include %D%/mfcalc/local.mk
include %D%/rpcalc/local.mk
diff --git a/examples/mfcalc/local.mk b/examples/mfcalc/local.mk
index 192214bf..9df3b05e 100644
--- a/examples/mfcalc/local.mk
+++ b/examples/mfcalc/local.mk
@@ -28,6 +28,7 @@ extracted += $(mfcalc_extracted)
check_PROGRAMS += %D%/mfcalc
nodist_%C%_mfcalc_SOURCES = $(mfcalc_sources)
+# Don't use gnulib's system headers.
%C%_mfcalc_CPPFLAGS = -I$(top_builddir)/%D%
%C%_mfcalc_LDADD = -lm
diff --git a/examples/rpcalc/local.mk b/examples/rpcalc/local.mk
index c6631508..48f6ac67 100644
--- a/examples/rpcalc/local.mk
+++ b/examples/rpcalc/local.mk
@@ -28,6 +28,7 @@ extracted += $(rpcalc_extracted)
check_PROGRAMS += %D%/rpcalc
nodist_%C%_rpcalc_SOURCES = $(rpcalc_sources)
+# Don't use gnulib's system headers.
%C%_rpcalc_CPPFLAGS = -I$(top_builddir)/%D%
%C%_rpcalc_LDADD = -lm
diff --git a/examples/test b/examples/test
index 733c870b..e9db3d61 100755
--- a/examples/test
+++ b/examples/test
@@ -15,7 +15,8 @@
# You should have received a copy of the GNU General Public License
# 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=1
@@ -27,7 +28,7 @@ exit=true
cwd=`pwd`
# The exercised program.
-for p in $cwd/examples/$me/$me $cwd/examples/$me
+for p in "$cwd/examples/$medir/$me" "$cwd/examples/$me"
do
if test -x "$p"; then
prog=$p
- doc: an introductory example for C++,
Akim Demaille <=