Skip to content

Commit

Permalink
glr2.cc: example: use objects (not pointers) to represent the AST
Browse files Browse the repository at this point in the history
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.
* 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.
  • Loading branch information
akimd committed Dec 18, 2020
1 parent c2e5124 commit 734eb80
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 98 deletions.
1 change: 1 addition & 0 deletions THANKS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
46 changes: 35 additions & 11 deletions data/skeletons/glr2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -779,16 +779,15 @@ class glr_state
{}

/// 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)
Expand All @@ -800,17 +799,42 @@ class glr_state
, 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;]])[
// FIXME: destroy the value.
}

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
Expand Down
157 changes: 105 additions & 52 deletions examples/c++/glr/ast.hh
Original file line number Diff line number Diff line change
Expand Up @@ -16,102 +16,155 @@
*/

#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_;
};


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 () {}
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 <typename T,
std::enable_if_t<!std::is_same<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;
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;
};


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))
{
for (int i = 0; i < 3; ++i)
if (children_[i])
children_[i]->free ();
children_[0] = child0;
children_[1] = child1;
children_[2] = child2;
}

std::ostream& print (std::ostream& o) const
friend std::ostream& operator<< (std::ostream& o, const Nterm& t)
{
o << form_;
if (children_[0])
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];
};

class Term : public Node
class Term
{
public:
Term (const std::string &text)
: text_ (text)
Term (std::string text)
: text_ (std::move (text))
{}

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_;
};

#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
Loading

0 comments on commit 734eb80

Please sign in to comment.