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 <bruno.belanyi@epita.fr>.
* 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.
This commit is contained in:
Akim Demaille
2020-12-14 20:26:41 +01:00
parent 611348e67b
commit 0a82316e54
7 changed files with 177 additions and 89 deletions

1
THANKS
View File

@@ -34,6 +34,7 @@ Bob Rossi bob@brasko.net
Brandon Lucia blucia@gmail.com Brandon Lucia blucia@gmail.com
Brooks Moses bmoses@google.com Brooks Moses bmoses@google.com
Bruce Lilly blilly@erols.com Bruce Lilly blilly@erols.com
Bruno Belanyi bruno.belanyi@epita.fr
Bruno Haible bruno@clisp.org Bruno Haible bruno@clisp.org
Charles-Henri de Boysson de-boy_c@epita.fr Charles-Henri de Boysson de-boy_c@epita.fr
Christian Burger cburger@sunysb.edu Christian Burger cburger@sunysb.edu

17
TODO
View File

@@ -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, There are many macros that should obey api.prefix: YY_CPLUSPLUS, YY_MOVE,
etc. 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 ** yyerrok in Java
And add tests in calc.at, to prepare work for D. And add tests in calc.at, to prepare work for D.

View File

@@ -779,16 +779,15 @@ public:
{} {}
/// Build with a semantic value. /// Build with a semantic value.
glr_state(state_num lrState, size_t posn, YYSTYPE sval]b4_locations_if([[, YYLTYPE loc]])[) glr_state (state_num lrState, size_t posn, YYSTYPE sval]b4_locations_if([[, YYLTYPE loc]])[)
: yyresolved(true) : yyresolved (true)
, yylrState(lrState) , yylrState (lrState)
, yyposn(posn) , yyposn (posn)
, yypred(0)]b4_locations_if([[ , yypred (0)
, yyloc(loc)]])[]b4_parse_assert_if([[ , yysval (sval)]b4_locations_if([[
, yyloc (loc)]])[]b4_parse_assert_if([[
, magic_ (MAGIC)]])[ , magic_ (MAGIC)]])[
{ {}
semanticVal() = sval;
}
/// Build with a semantic option. /// Build with a semantic option.
glr_state(state_num lrState, size_t posn) glr_state(state_num lrState, size_t posn)
@@ -800,17 +799,43 @@ public:
, magic_ (MAGIC)]])[ , 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) void copyFrom (const glr_state& other)
{]b4_parse_assert_if([[ {]b4_parse_assert_if([[
check_ (); check_ ();
other.check_ ();]])[ other.check_ ();]])[
*this = other; if (!yyresolved && other.yyresolved)
new (&yysval) YYSTYPE;
yyresolved = other.yyresolved;
yylrState = other.yylrState;
yyposn = other.yyposn;
setPred(other.pred()); setPred(other.pred());
if (other.yyresolved) { if (other.yyresolved) {
semanticVal() = other.semanticVal(); semanticVal() = other.semanticVal();
} else { } else {
setFirstVal(other.firstVal()); setFirstVal(other.firstVal());
} }]b4_locations_if([[
yyloc = other.yyloc;]])[
} }
/** Type tag for the semantic value. If true, yysval applies, otherwise /** Type tag for the semantic value. If true, yysval applies, otherwise
@@ -1691,7 +1716,7 @@ public:
#endif #endif
yys.yyresolved = s->yyresolved; yys.yyresolved = s->yyresolved;
if (s->yyresolved) if (s->yyresolved)
yys.semanticVal() = s->semanticVal(); new (&yys.semanticVal()) YYSTYPE(s->semanticVal());
else else
/* The effect of using semanticVal or yyloc (in an immediate rule) is /* The effect of using semanticVal or yyloc (in an immediate rule) is
* undefined. */ * undefined. */
@@ -2633,7 +2658,7 @@ public:
{ {
yys.yyresolved = true; yys.yyresolved = true;
YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
yys.semanticVal() = yysval; new (&yys.semanticVal()) YYSTYPE(yysval);
YY_IGNORE_MAYBE_UNINITIALIZED_END YY_IGNORE_MAYBE_UNINITIALIZED_END
} }
else else

View File

@@ -15,113 +15,159 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstddef> // nullptr_t
#include <iostream> #include <iostream>
#include <memory>
#if __cplusplus < 201103L // Type erasure 101 <https://stackoverflow.com/a/26199467/1353549>.
# define nullptr 0 class NodeInterface;
#endif
class Node class Node
{ {
public: public:
Node () Node (const Node& node) = default;
: parents_ (0) Node (Node&& node) = default;
{} Node () = default;
~Node () = default;
virtual ~Node (); template <typename T,
// SFINAE block using this ctor as a copy/move ctor:
std::enable_if_t<!std::is_same<Node, std::decay_t<T>>::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; return impl_ != nullptr;
/* Free only if 0 (last parent) or -1 (no parents). */
if (parents_ <= 0)
delete this;
} }
virtual std::ostream& print (std::ostream& o) const = 0; std::ostream& print (std::ostream& o) const;
protected: std::shared_ptr<NodeInterface> impl_;
friend class Nterm;
friend class Term;
int parents_;
}; };
Node::~Node ()
{}
static std::ostream& static std::ostream&
operator<< (std::ostream& o, const Node &node) operator<< (std::ostream& o, const Node &node)
{ {
return node.print (o); return node.print (o);
} }
class Nterm : public Node class NodeInterface
{ {
public: public:
Nterm (char const *form, virtual ~NodeInterface () = default;
Node *child0 = nullptr, Node *child1 = nullptr, Node *child2 = nullptr) virtual std::ostream& print (std::ostream& o) const = 0;
: form_ (form) };
std::ostream& Node::print (std::ostream& o) const
{
if (impl_)
impl_->print (o);
return o;
}
template <typename T,
std::enable_if_t<!std::is_same<std::nullptr_t, std::decay_t<T>>::value, int>* = nullptr>
struct NodeImpl : public NodeInterface
{
template <typename U>
explicit NodeImpl (U&& u)
: t{std::forward<U> (u)}
{}
virtual ~NodeImpl () = default;
virtual std::ostream& print (std::ostream& o) const
{ {
children_[0] = child0; return o << t;
if (child0)
child0->parents_ += 1;
children_[1] = child1;
if (child1)
child1->parents_ += 1;
children_[2] = child2;
if (child2)
child2->parents_ += 1;
} }
~Nterm (); T t;
};
std::ostream& print (std::ostream& o) const
template <typename T,
std::enable_if_t<!std::is_same<Node, std::decay_t<T>>::value, int>*>
Node::Node (T&& t)
: impl_ (new NodeImpl<std::decay_t<T>>{std::forward<T> (t)})
{}
class Nterm
{
public:
Nterm (std::string form,
Node child0 = Node (), Node child1 = Node (), Node child2 = Node ())
: form_ (std::move (form))
{ {
o << form_; children_[0] = child0;
if (children_[0]) 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]; o << '(' << t.children_[0];
if (children_[1]) if (t.children_[1])
o << ", " << *children_[1]; o << ", " << t.children_[1];
if (children_[2]) if (t.children_[2])
o << ", " << *children_[2]; o << ", " << t.children_[2];
o << ')'; o << ')';
} }
return o; return o;
} }
private: private:
char const *form_; std::string form_;
Node *children_[3]; 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: public:
Term (const std::string &text) Term (std::string text)
: text_ (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 << t.text_;
return o;
} }
private: private:
std::string text_; 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 ("<OR>",
Nterm ("<declare>", Term ("T"), Term ("x")),
Nterm ("<cast>", Term ("x"), Term ("T")));
std::cout << n1 << '\n';
n = n1;
std::cout << n1 << '\n';
} }
#endif

View File

@@ -20,6 +20,7 @@
%glr-parser %glr-parser
%skeleton "glr2.cc" %skeleton "glr2.cc"
%define parse.assert
%header %header
%locations %locations
%debug %debug
@@ -32,7 +33,7 @@
#include "ast.hh" #include "ast.hh"
} }
%define api.value.type {Node *} %define api.value.type {Node}
%code %code
{ {
@@ -61,32 +62,29 @@
%right '=' %right '='
%left '+' %left '+'
%destructor { $$->free (); } stmt expr decl declarator TYPENAME ID
%% %%
prog : %empty prog : %empty
| prog stmt { std::cout << @2 << ": " << *$2 << '\n'; $2->free (); } | prog stmt { std::cout << @2 << ": " << $2 << '\n'; }
; ;
stmt : expr ';' %merge <stmtMerge> { $$ = $1; } stmt : expr ';' %merge <stmtMerge> { $$ = $1; }
| decl %merge <stmtMerge> | decl %merge <stmtMerge>
| error ';' { $$ = new Nterm ("<error>"); } | error ';' { $$ = Nterm ("<error>"); }
| '@' { $$ = $1; YYACCEPT; } | '@' { $$ = $1; YYACCEPT; }
; ;
expr : ID expr : ID
| TYPENAME '(' expr ')' | TYPENAME '(' expr ')'
{ $$ = new Nterm ("<cast>", $3, $1); } { $$ = Nterm ("<cast>", $3, $1); }
| expr '+' expr { $$ = new Nterm ("+", $1, $3); } | expr '+' expr { $$ = Nterm ("+", $1, $3); }
| expr '=' expr { $$ = new Nterm ("=", $1, $3); } | expr '=' expr { $$ = Nterm ("=", $1, $3); }
; ;
decl : TYPENAME declarator ';' decl : TYPENAME declarator ';'
{ $$ = new Nterm ("<declare>", $1, $2); } { $$ = Nterm ("<declare>", $1, $2); }
| TYPENAME declarator '=' expr ';' | TYPENAME declarator '=' expr ';'
{ $$ = new Nterm ("<init-declare>", $1, { $$ = Nterm ("<init-declare>", $1, $2, $4); }
$2, $4); }
; ;
declarator declarator
@@ -149,13 +147,13 @@ yylex (yy::parser::semantic_type* lvalp, yy::parser::location_type* llocp)
= isupper (static_cast <unsigned char> (form[0])) = isupper (static_cast <unsigned char> (form[0]))
? yy::parser::token::TYPENAME ? yy::parser::token::TYPENAME
: yy::parser::token::ID; : yy::parser::token::ID;
*lvalp = new Term (form); *lvalp = Term (form);
} }
else else
{ {
colNum += 1; colNum += 1;
tok = c; tok = c;
*lvalp = nullptr; lvalp = nullptr;
} }
llocp->end.column = colNum-1; llocp->end.column = colNum-1;
return tok; return tok;
@@ -167,7 +165,7 @@ yylex (yy::parser::semantic_type* lvalp, yy::parser::location_type* llocp)
static yy::parser::semantic_type static yy::parser::semantic_type
stmtMerge (const yy::parser::semantic_type& x0, const yy::parser::semantic_type& x1) stmtMerge (const yy::parser::semantic_type& x0, const yy::parser::semantic_type& x1)
{ {
return new Nterm ("<OR>", x0, x1); return Nterm ("<OR>", x0, x1);
} }
int int

View File

@@ -34,7 +34,7 @@ CLEANDIRS += %D%/*.dSYM
# Avoid using BUILT_SOURCES which is too global. # Avoid using BUILT_SOURCES which is too global.
$(%C%_c___types_OBJECTS): $(cxx_types_sources_generated) $(%C%_c___types_OBJECTS): $(cxx_types_sources_generated)
if ENABLE_CXX if ENABLE_CXX14
check_PROGRAMS += %D%/c++-types check_PROGRAMS += %D%/c++-types
dist_%C%_c___types_SOURCES = \ dist_%C%_c___types_SOURCES = \
%D%/ast.hh %D%/ast.hh
@@ -43,6 +43,7 @@ if ENABLE_CXX
%D%/c++-types.hh %D%/c++-types.hh
# Don't use gnulib's system headers. # Don't use gnulib's system headers.
%C%_c___types_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D% %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 TESTS += %D%/c++-types.test
endif ENABLE_CXX endif ENABLE_CXX14
EXTRA_DIST += %D%/c++-types.yy %D%/c++-types.test EXTRA_DIST += %D%/c++-types.yy %D%/c++-types.test

View File

@@ -30,7 +30,7 @@ simple_extracted = %D%/simple.yy
simple_sources = $(simple_extracted) simple_sources = $(simple_extracted)
extracted += $(simple_extracted) extracted += $(simple_extracted)
if ENABLE_CXX14 if ENABLE_CXX11
check_PROGRAMS += %D%/simple check_PROGRAMS += %D%/simple
nodist_%C%_simple_SOURCES = $(simple_sources) nodist_%C%_simple_SOURCES = $(simple_sources)