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
|
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
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,
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user