From 0a82316e54fead8c3f1cf41d2213c868ab88f508 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Mon, 14 Dec 2020 20:26:41 +0100 Subject: [PATCH] glr2.cc: example: use objects (not pointers) to represent the AST Currently we are using pointers. The whole point of glr2.cc (vs. glr.cc) is precisely to allow genuine C++ objects to be semantic values. Let's make that work. * data/skeletons/glr2.cc (glr_state::glr_state): Be sure to initialize yysval. (glr_state): Add copy-ctor, assignment and dtor. (glr_state::copyFrom): Be sure to initialize the destination if it was not. (glr_state::~glr_state): Destroy the semantic value. * examples/c++/glr/ast.hh: Rewrite so that we use genuine objects, rather than a traditional OOP hierarchy that requires to deal with pointers. With help from Bruno Belanyi . * examples/c++/glr/c++-types.yy: Remove memory management. Use true objects. (main): Don't reach yydebug directly. * examples/c++/glr/local.mk: We need C++11. --- THANKS | 1 + TODO | 17 ++++ data/skeletons/glr2.cc | 51 ++++++++--- examples/c++/glr/ast.hh | 164 ++++++++++++++++++++++------------ examples/c++/glr/c++-types.yy | 26 +++--- examples/c++/glr/local.mk | 5 +- examples/c++/local.mk | 2 +- 7 files changed, 177 insertions(+), 89 deletions(-) diff --git a/THANKS b/THANKS index 195153a6..a5a99f12 100644 --- a/THANKS +++ b/THANKS @@ -34,6 +34,7 @@ Bob Rossi bob@brasko.net Brandon Lucia blucia@gmail.com Brooks Moses bmoses@google.com Bruce Lilly blilly@erols.com +Bruno Belanyi bruno.belanyi@epita.fr Bruno Haible bruno@clisp.org Charles-Henri de Boysson de-boy_c@epita.fr Christian Burger cburger@sunysb.edu diff --git a/TODO b/TODO index b857199a..7992d2ba 100644 --- a/TODO +++ b/TODO @@ -9,6 +9,23 @@ Clarify that rule numbers in the skeletons are 1-based. There are many macros that should obey api.prefix: YY_CPLUSPLUS, YY_MOVE, etc. +** YYDEBUG etc. in C++ +Discourage the use of YYDEBUG in C++ (see thread with Jot). Stop supporting +#define YYSTYPE by the user. + +Add value_type as a synonym for semantic_type. + +** Asymmetries +In glr_state, we have yysval and yylloc. It should be yyval/yyloc (and +yylval/yylloc when referring to the lookahead). glr.c should +s/yysval/yyval/. + +Also + + yystack.yyglrShift (create_state_set_index(0), 0, 0, yylval, &yylloc); + +Why are yylval and yylloc treated differently? + ** yyerrok in Java And add tests in calc.at, to prepare work for D. diff --git a/data/skeletons/glr2.cc b/data/skeletons/glr2.cc index abd90256..ac3acd80 100644 --- a/data/skeletons/glr2.cc +++ b/data/skeletons/glr2.cc @@ -779,16 +779,15 @@ public: {} /// Build with a semantic value. - glr_state(state_num lrState, size_t posn, YYSTYPE sval]b4_locations_if([[, YYLTYPE loc]])[) - : yyresolved(true) - , yylrState(lrState) - , yyposn(posn) - , yypred(0)]b4_locations_if([[ - , yyloc(loc)]])[]b4_parse_assert_if([[ + glr_state (state_num lrState, size_t posn, YYSTYPE sval]b4_locations_if([[, YYLTYPE loc]])[) + : yyresolved (true) + , yylrState (lrState) + , yyposn (posn) + , yypred (0) + , yysval (sval)]b4_locations_if([[ + , yyloc (loc)]])[]b4_parse_assert_if([[ , magic_ (MAGIC)]])[ - { - semanticVal() = sval; - } + {} /// Build with a semantic option. glr_state(state_num lrState, size_t posn) @@ -800,17 +799,43 @@ public: , magic_ (MAGIC)]])[ {} + glr_state (const glr_state& other)]b4_parse_assert_if([[ + : magic_ (MAGIC)]])[ + { + // FIXME: Do it right. + copyFrom (other); + } + + ~glr_state () + {]b4_parse_assert_if([[ + check_ (); + magic_ = 0;]])[ + if (yyresolved) + yysval.YYSTYPE::~semantic_type (); + } + + glr_state& operator= (const glr_state& other) + { + copyFrom (other); + return *this; + } + void copyFrom (const glr_state& other) {]b4_parse_assert_if([[ check_ (); other.check_ ();]])[ - *this = other; + if (!yyresolved && other.yyresolved) + new (&yysval) YYSTYPE; + yyresolved = other.yyresolved; + yylrState = other.yylrState; + yyposn = other.yyposn; setPred(other.pred()); if (other.yyresolved) { semanticVal() = other.semanticVal(); } else { setFirstVal(other.firstVal()); - } + }]b4_locations_if([[ + yyloc = other.yyloc;]])[ } /** Type tag for the semantic value. If true, yysval applies, otherwise @@ -1691,7 +1716,7 @@ public: #endif yys.yyresolved = s->yyresolved; if (s->yyresolved) - yys.semanticVal() = s->semanticVal(); + new (&yys.semanticVal()) YYSTYPE(s->semanticVal()); else /* The effect of using semanticVal or yyloc (in an immediate rule) is * undefined. */ @@ -2633,7 +2658,7 @@ public: { yys.yyresolved = true; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - yys.semanticVal() = yysval; + new (&yys.semanticVal()) YYSTYPE(yysval); YY_IGNORE_MAYBE_UNINITIALIZED_END } else diff --git a/examples/c++/glr/ast.hh b/examples/c++/glr/ast.hh index efa92b42..3fb0845e 100644 --- a/examples/c++/glr/ast.hh +++ b/examples/c++/glr/ast.hh @@ -15,113 +15,159 @@ along with this program. If not, see . */ +#include // nullptr_t #include +#include -#if __cplusplus < 201103L -# define nullptr 0 -#endif +// Type erasure 101 . +class NodeInterface; class Node { public: - Node () - : parents_ (0) - {} + Node (const Node& node) = default; + Node (Node&& node) = default; + Node () = default; + ~Node () = default; - virtual ~Node (); + template >::value, int>* = nullptr> + Node (T&& t); - void free () + Node& operator= (const Node& node) = default; + Node& operator= (Node&& node) = default; + + explicit operator bool () const { - parents_ -= 1; - /* Free only if 0 (last parent) or -1 (no parents). */ - if (parents_ <= 0) - delete this; + return impl_ != nullptr; } - virtual std::ostream& print (std::ostream& o) const = 0; + std::ostream& print (std::ostream& o) const; -protected: - friend class Nterm; - friend class Term; - int parents_; + std::shared_ptr impl_; }; -Node::~Node () -{} - - static std::ostream& operator<< (std::ostream& o, const Node &node) { return node.print (o); } -class Nterm : public Node +class NodeInterface { public: - Nterm (char const *form, - Node *child0 = nullptr, Node *child1 = nullptr, Node *child2 = nullptr) - : form_ (form) + virtual ~NodeInterface () = default; + virtual std::ostream& print (std::ostream& o) const = 0; +}; + + +std::ostream& Node::print (std::ostream& o) const +{ + if (impl_) + impl_->print (o); + return o; +} + + +template >::value, int>* = nullptr> +struct NodeImpl : public NodeInterface +{ + template + explicit NodeImpl (U&& u) + : t{std::forward (u)} + {} + virtual ~NodeImpl () = default; + virtual std::ostream& print (std::ostream& o) const { - children_[0] = child0; - if (child0) - child0->parents_ += 1; - children_[1] = child1; - if (child1) - child1->parents_ += 1; - children_[2] = child2; - if (child2) - child2->parents_ += 1; + return o << t; } - ~Nterm (); + T t; +}; - std::ostream& print (std::ostream& o) const + +template >::value, int>*> +Node::Node (T&& t) + : impl_ (new NodeImpl>{std::forward (t)}) +{} + +class Nterm +{ +public: + Nterm (std::string form, + Node child0 = Node (), Node child1 = Node (), Node child2 = Node ()) + : form_ (std::move (form)) { - o << form_; - if (children_[0]) + children_[0] = child0; + children_[1] = child1; + children_[2] = child2; + } + + friend std::ostream& operator<< (std::ostream& o, const Nterm& t) + { + o << t.form_; + if (t.children_[0]) { - o << '(' << *children_[0]; - if (children_[1]) - o << ", " << *children_[1]; - if (children_[2]) - o << ", " << *children_[2]; + o << '(' << t.children_[0]; + if (t.children_[1]) + o << ", " << t.children_[1]; + if (t.children_[2]) + o << ", " << t.children_[2]; o << ')'; } return o; } private: - char const *form_; - Node *children_[3]; + std::string form_; + Node children_[3]; }; -Nterm::~Nterm () -{ - for (int i = 0; i < 3; ++i) - if (children_[i]) - children_[i]->free (); -} -class Term : public Node +class Term { public: - Term (const std::string &text) - : text_ (text) + Term (std::string text) + : text_ (std::move (text)) {} - ~Term(); - std::ostream& print (std::ostream& o) const + friend std::ostream& operator<< (std::ostream& o, const Term& t) { - o << text_; - return o; + return o << t.text_; } private: std::string text_; }; -Term::~Term () +#ifdef TEST +int main () { + Node n0; + std::cout << n0 << '\n'; + + Node n; + n = n0; + std::cout << n0 << '\n'; + + Term t1 = Term ("T"); + std::cout << t1 << '\n'; + + n = t1; + std::cout << n << '\n'; + std::cout << Nterm ("+", t1, t1) << '\n'; + + auto n1 + = Nterm ("", + Nterm ("", Term ("T"), Term ("x")), + Nterm ("", Term ("x"), Term ("T"))); + std::cout << n1 << '\n'; + + n = n1; + std::cout << n1 << '\n'; } +#endif diff --git a/examples/c++/glr/c++-types.yy b/examples/c++/glr/c++-types.yy index 80bd16f0..1fc26166 100644 --- a/examples/c++/glr/c++-types.yy +++ b/examples/c++/glr/c++-types.yy @@ -20,6 +20,7 @@ %glr-parser %skeleton "glr2.cc" +%define parse.assert %header %locations %debug @@ -32,7 +33,7 @@ #include "ast.hh" } -%define api.value.type {Node *} +%define api.value.type {Node} %code { @@ -61,32 +62,29 @@ %right '=' %left '+' -%destructor { $$->free (); } stmt expr decl declarator TYPENAME ID - %% prog : %empty - | prog stmt { std::cout << @2 << ": " << *$2 << '\n'; $2->free (); } + | prog stmt { std::cout << @2 << ": " << $2 << '\n'; } ; stmt : expr ';' %merge { $$ = $1; } | decl %merge - | error ';' { $$ = new Nterm (""); } + | error ';' { $$ = Nterm (""); } | '@' { $$ = $1; YYACCEPT; } ; expr : ID | TYPENAME '(' expr ')' - { $$ = new Nterm ("", $3, $1); } - | expr '+' expr { $$ = new Nterm ("+", $1, $3); } - | expr '=' expr { $$ = new Nterm ("=", $1, $3); } + { $$ = Nterm ("", $3, $1); } + | expr '+' expr { $$ = Nterm ("+", $1, $3); } + | expr '=' expr { $$ = Nterm ("=", $1, $3); } ; decl : TYPENAME declarator ';' - { $$ = new Nterm ("", $1, $2); } + { $$ = Nterm ("", $1, $2); } | TYPENAME declarator '=' expr ';' - { $$ = new Nterm ("", $1, - $2, $4); } + { $$ = Nterm ("", $1, $2, $4); } ; declarator @@ -149,13 +147,13 @@ yylex (yy::parser::semantic_type* lvalp, yy::parser::location_type* llocp) = isupper (static_cast (form[0])) ? yy::parser::token::TYPENAME : yy::parser::token::ID; - *lvalp = new Term (form); + *lvalp = Term (form); } else { colNum += 1; tok = c; - *lvalp = nullptr; + lvalp = nullptr; } llocp->end.column = colNum-1; return tok; @@ -167,7 +165,7 @@ yylex (yy::parser::semantic_type* lvalp, yy::parser::location_type* llocp) static yy::parser::semantic_type stmtMerge (const yy::parser::semantic_type& x0, const yy::parser::semantic_type& x1) { - return new Nterm ("", x0, x1); + return Nterm ("", x0, x1); } int diff --git a/examples/c++/glr/local.mk b/examples/c++/glr/local.mk index e275cadd..cd25c3cf 100644 --- a/examples/c++/glr/local.mk +++ b/examples/c++/glr/local.mk @@ -34,7 +34,7 @@ CLEANDIRS += %D%/*.dSYM # Avoid using BUILT_SOURCES which is too global. $(%C%_c___types_OBJECTS): $(cxx_types_sources_generated) -if ENABLE_CXX +if ENABLE_CXX14 check_PROGRAMS += %D%/c++-types dist_%C%_c___types_SOURCES = \ %D%/ast.hh @@ -43,6 +43,7 @@ if ENABLE_CXX %D%/c++-types.hh # Don't use gnulib's system headers. %C%_c___types_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D% + %C%_c___types_CXXFLAGS = $(CXX14_CXXFLAGS) $(WARN_CXXFLAGS_TEST) TESTS += %D%/c++-types.test -endif ENABLE_CXX +endif ENABLE_CXX14 EXTRA_DIST += %D%/c++-types.yy %D%/c++-types.test diff --git a/examples/c++/local.mk b/examples/c++/local.mk index 6af9ac94..deb4b88f 100644 --- a/examples/c++/local.mk +++ b/examples/c++/local.mk @@ -30,7 +30,7 @@ simple_extracted = %D%/simple.yy simple_sources = $(simple_extracted) extracted += $(simple_extracted) -if ENABLE_CXX14 +if ENABLE_CXX11 check_PROGRAMS += %D%/simple nodist_%C%_simple_SOURCES = $(simple_sources)