mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 12:23:04 +00:00
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:
1
THANKS
1
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
|
||||
|
||||
17
TODO
17
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.
|
||||
|
||||
|
||||
@@ -783,12 +783,11 @@ public:
|
||||
: yyresolved (true)
|
||||
, yylrState (lrState)
|
||||
, yyposn (posn)
|
||||
, yypred(0)]b4_locations_if([[
|
||||
, 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
|
||||
|
||||
@@ -15,113 +15,159 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#if __cplusplus < 201103L
|
||||
# define nullptr 0
|
||||
#endif
|
||||
// Type erasure 101 <https://stackoverflow.com/a/26199467/1353549>.
|
||||
class NodeInterface;
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
Node ()
|
||||
: parents_ (0)
|
||||
{}
|
||||
Node (const Node& node) = default;
|
||||
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;
|
||||
/* 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<NodeInterface> 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
|
||||
{
|
||||
children_[0] = child0;
|
||||
if (child0)
|
||||
child0->parents_ += 1;
|
||||
children_[1] = child1;
|
||||
if (child1)
|
||||
child1->parents_ += 1;
|
||||
children_[2] = child2;
|
||||
if (child2)
|
||||
child2->parents_ += 1;
|
||||
if (impl_)
|
||||
impl_->print (o);
|
||||
return o;
|
||||
}
|
||||
|
||||
~Nterm ();
|
||||
|
||||
std::ostream& print (std::ostream& o) const
|
||||
template <typename T,
|
||||
std::enable_if_t<!std::is_same<std::nullptr_t, std::decay_t<T>>::value, int>* = nullptr>
|
||||
struct NodeImpl : public NodeInterface
|
||||
{
|
||||
o << form_;
|
||||
if (children_[0])
|
||||
template <typename U>
|
||||
explicit NodeImpl (U&& u)
|
||||
: t{std::forward<U> (u)}
|
||||
{}
|
||||
virtual ~NodeImpl () = default;
|
||||
virtual std::ostream& print (std::ostream& o) const
|
||||
{
|
||||
o << '(' << *children_[0];
|
||||
if (children_[1])
|
||||
o << ", " << *children_[1];
|
||||
if (children_[2])
|
||||
o << ", " << *children_[2];
|
||||
return o << t;
|
||||
}
|
||||
|
||||
T t;
|
||||
};
|
||||
|
||||
|
||||
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))
|
||||
{
|
||||
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 << '(' << 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 ("<OR>",
|
||||
Nterm ("<declare>", Term ("T"), Term ("x")),
|
||||
Nterm ("<cast>", Term ("x"), Term ("T")));
|
||||
std::cout << n1 << '\n';
|
||||
|
||||
n = n1;
|
||||
std::cout << n1 << '\n';
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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 <stmtMerge> { $$ = $1; }
|
||||
| decl %merge <stmtMerge>
|
||||
| error ';' { $$ = new Nterm ("<error>"); }
|
||||
| error ';' { $$ = Nterm ("<error>"); }
|
||||
| '@' { $$ = $1; YYACCEPT; }
|
||||
;
|
||||
|
||||
expr : ID
|
||||
| TYPENAME '(' expr ')'
|
||||
{ $$ = new Nterm ("<cast>", $3, $1); }
|
||||
| expr '+' expr { $$ = new Nterm ("+", $1, $3); }
|
||||
| expr '=' expr { $$ = new Nterm ("=", $1, $3); }
|
||||
{ $$ = Nterm ("<cast>", $3, $1); }
|
||||
| expr '+' expr { $$ = Nterm ("+", $1, $3); }
|
||||
| expr '=' expr { $$ = Nterm ("=", $1, $3); }
|
||||
;
|
||||
|
||||
decl : TYPENAME declarator ';'
|
||||
{ $$ = new Nterm ("<declare>", $1, $2); }
|
||||
{ $$ = Nterm ("<declare>", $1, $2); }
|
||||
| TYPENAME declarator '=' expr ';'
|
||||
{ $$ = new Nterm ("<init-declare>", $1,
|
||||
$2, $4); }
|
||||
{ $$ = Nterm ("<init-declare>", $1, $2, $4); }
|
||||
;
|
||||
|
||||
declarator
|
||||
@@ -149,13 +147,13 @@ yylex (yy::parser::semantic_type* lvalp, yy::parser::location_type* llocp)
|
||||
= isupper (static_cast <unsigned char> (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 ("<OR>", x0, x1);
|
||||
return Nterm ("<OR>", x0, x1);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user