User-defined classes in C++ Bison grammar

Wouter Beek <[email protected]>
Newsgroups gmane.comp.parsers.bison.general
Message-ID <CAEh2WcMe=y5h0f-QwwF7Uys49C7krSMVidH_R4dz37FJqw5gpA@mail.gmail.com>
Dear Bison devs,

I really like the recent developments in Bison 3.1 and 3.2 that improve the
support for modern C++ programming techniques.  As a result, I'm writing my
first C++ Bison parser, but run into some issues that may or may not be
limitations of the current state of C++ support:

  1. When I use a user-defined class in my Bison parser, I *must* define a
default constructor.  Is this an inherent requirement/limitation of Bison?
While it is always possible to define a ‘dummy’ default constructor in
combination with using pointers for all member values, this sometimes makes
the code less logical/pretty.  Specifically, when I use a user-defined
class `A' with no default constructor the compiler emits the following
errors:

     parser.hpp:576:33: error: no matching constructor for initialization
of 'A'
           return *new (yyas_<T> ()) T ();
                                     ^
     parser.cpp:559:21: note: in instantiation of function template
specialization
           'yy::variant<32>::emplace<A>' requested here
             yylhs.value.emplace< A > ();
                         ^
     ./A.hpp:9:3: note: candidate constructor not viable: requires single
argument 'value', but no
           arguments were provided
       A(const std::string& value)
       ^
     ./A.hpp:7:7: note: candidate constructor (the implicit copy
constructor) not viable: requires
           1 argument, but 0 were provided
     class A {
           ^
     ./A.hpp:7:7: note: candidate constructor (the implicit move
constructor) not viable: requires
           1 argument, but 0 were provided

  2. When I use a user-defined sub-class (`B') of a virtual class (`A'), I
get the following errors during compilation.  Could it be that Bison does
not (yet) support sub-classes / virtual classes?  Or is this something one
can work around in some way?  I have an actual use case for this: I would
like to define a pure virtual `Term' with sub-classes `BlankNode', `Iri',
and `Literal'.

     In file included from
/usr/include/c++/8/x86_64-redhat-linux/bits/c++allocator.h:33,
                      from /usr/include/c++/8/bits/allocator.h:46,
                      from /usr/include/c++/8/string:41,
                      from /usr/include/c++/8/bits/locale_classes.h:40,
                      from /usr/include/c++/8/bits/ios_base.h:41,
                      from /usr/include/c++/8/ios:42,
                      from /usr/include/c++/8/ostream:38,
                      from /usr/include/c++/8/iostream:39,
                      from parser.yy:14,
                      from parser.cpp:40:
     /usr/include/c++/8/ext/new_allocator.h: In instantiation of ‘void
__gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = A;
_Args = {const A&}; _Tp = A]’:
     /usr/include/c++/8/bits/alloc_traits.h:475:4:   required from ‘static
void std::allocator_traits<std::allocator<_CharT>
>::construct(std::allocator_traits<std::allocator<_CharT>
>::allocator_type&, _Up*, _Args&& ...) [with _Up = A; _Args = {const A&};
_Tp = A; std::allocator_traits<std::allocator<_CharT> >::allocator_type =
std::allocator<A>]’
     /usr/include/c++/8/bits/stl_vector.h:1079:30:   required from ‘void
std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = A;
_Alloc = std::allocator<A>; std::vector<_Tp, _Alloc>::value_type = A]’
     parser.yy:40:56:   required from here
     /usr/include/c++/8/ext/new_allocator.h:136:4: error: invalid
new-expression of abstract class type ‘A’
       { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     In file included from B.hpp:6,
                      from parser.yy:17,
                      from parser.cpp:40:
     A.hpp:7:7: note:   because the following virtual functions are pure
within ‘A’:
      class A {
            ^
     A.hpp:10:25: note: ‘virtual std::ostream& A::print(std::ostream&)
const’
        virtual std::ostream& print(std::ostream& os) const = 0;
                              ^~~~~
     In file included from /usr/include/c++/8/vector:62,
                      from parser.yy:16,
                      from parser.cpp:40:
     /usr/include/c++/8/bits/stl_construct.h: In instantiation of ‘void
std::_Construct(_T1*, _Args&& ...) [with _T1 = A; _Args = {A}]’:
     /usr/include/c++/8/bits/stl_uninitialized.h:83:18:   required from
‘static _ForwardIterator
std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator,
_InputIterator, _ForwardIterator) [with _InputIterator =
std::move_iterator<A*>; _ForwardIterator = A*; bool _TrivialValueTypes =
false]’
     /usr/include/c++/8/bits/stl_uninitialized.h:134:15:   required from
‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator,
_ForwardIterator) [with _InputIterator = std::move_iterator<A*>;
_ForwardIterator = A*]’
     /usr/include/c++/8/bits/stl_uninitialized.h:289:37:   required from
‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator,
_InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with
_InputIterator = std::move_iterator<A*>; _ForwardIterator = A*; _Tp = A]’
     /usr/include/c++/8/bits/stl_uninitialized.h:311:2:   required from
‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator,
_InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = A*;
_ForwardIterator = A*; _Allocator = std::allocator<A>]’
     /usr/include/c++/8/bits/vector.tcc:447:6:   required from ‘void
std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp,
_Alloc>::iterator, _Args&& ...) [with _Args = {const A&}; _Tp = A; _Alloc =
std::allocator<A>; std::vector<_Tp, _Alloc>::iterator =
__gnu_cxx::__normal_iterator<A*, std::vector<A> >; typename
std::_Vector_base<_Tp, _Alloc>::pointer = A*]’
     /usr/include/c++/8/bits/stl_vector.h:1085:4:   required from ‘void
std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = A;
_Alloc = std::allocator<A>; std::vector<_Tp, _Alloc>::value_type = A]’
     parser.yy:40:56:   required from here
     /usr/include/c++/8/bits/stl_construct.h:75:7: error: invalid
new-expression of abstract class type ‘A’
          { ::new(static_cast<void*>(__p))
_T1(std::forward<_Args>(__args)...); }

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  3. I regularly run into compilation issues due to the definition of the
driver's methods `scan_begin()' and `scan_end()' in the parser source file
`scanner.ll'.  Is it possible to make these methods part of `driver.cpp'?
The way in which the code is currently spread over files seems a bit
unorganized.

PS: I have published the accompanying code over at
https://github.com/wouterbeek/bison-cpp

---
Best regards,
Wouter Beek.

Email: [email protected]
WWW: https://triply.cc
Tel: +31647674624
_______________________________________________
[email protected] https://lists.gnu.org/mailman/listinfo/help-bison
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.