bison-patches
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]