[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 4/4] glr2.cc: examples: use values to represent the AST
From: |
Akim Demaille |
Subject: |
[PATCH 4/4] glr2.cc: examples: use values to represent the AST |
Date: |
Fri, 18 Dec 2020 07:42:04 +0100 |
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.
---
THANKS | 1 +
TODO | 17 ++++
data/skeletons/glr2.cc | 46 +++++++---
examples/c++/glr/ast.hh | 157 +++++++++++++++++++++++-----------
examples/c++/glr/c++-types.yy | 69 ++++++++-------
5 files changed, 192 insertions(+), 98 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 e6d4717d..98275515 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,42 @@ 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;]])[
+ // 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
diff --git a/examples/c++/glr/ast.hh b/examples/c++/glr/ast.hh
index 223fc119..b5d125ee 100644
--- a/examples/c++/glr/ast.hh
+++ b/examples/c++/glr/ast.hh
@@ -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
diff --git a/examples/c++/glr/c++-types.yy b/examples/c++/glr/c++-types.yy
index 53262c25..0b20dc8a 100644
--- a/examples/c++/glr/c++-types.yy
+++ b/examples/c++/glr/c++-types.yy
@@ -32,24 +32,25 @@
#include "ast.hh"
}
-%define api.value.type {Node *}
+%define api.value.type {Node}
%code
{
+ #include <cassert>
+ #include <cctype>
+ #include <cstdio>
+ #include <cstdlib>
+ #include <cstring>
-#include <cassert>
-#include <cctype>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
+ #if __cplusplus < 201103L
+ # define nullptr 0
+ #endif
-#if __cplusplus < 201103L
-# define nullptr 0
-#endif
+ static yy::parser::semantic_type
+ stmtMerge (const yy::parser::semantic_type& x0, const
yy::parser::semantic_type& x1);
- static YYSTYPE stmtMerge (YYSTYPE x0, YYSTYPE x1);
-
- static int yylex (YYSTYPE *lvalp, YYLTYPE *llocp);
+ static int
+ yylex (yy::parser::semantic_type* val, yy::parser::location_type* loc);
}
%expect-rr 1
@@ -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
@@ -96,21 +94,22 @@ declarator
%%
/* A C error reporting function. */
-void yy::parser::error (const location_type& l, const std::string& m)
+void
+yy::parser::error (const location_type& l, const std::string& m)
{
std::cerr << l << ": " << m << '\n';
}
-int yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
+static int
+yylex (yy::parser::semantic_type* lvalp, yy::parser::location_type* llocp)
{
static int lineNum = 1;
static int colNum = 0;
while (1)
{
- int c;
assert (!feof (stdin));
- c = getchar ();
+ int c = getchar ();
switch (c)
{
case EOF:
@@ -127,9 +126,9 @@ int yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
break;
default:
{
- int tok;
llocp->begin.line = llocp->end.line = lineNum;
llocp->begin.column = colNum;
+ int tok;
if (isalpha (c))
{
std::string form;
@@ -146,13 +145,13 @@ int yylex (YYSTYPE *lvalp, YYLTYPE *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;
@@ -161,18 +160,18 @@ int yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
}
}
-static YYSTYPE
-stmtMerge (YYSTYPE x0, YYSTYPE x1)
+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
main (int argc, char **argv)
{
+ yy::parser parse;
// Enable parse traces on option -p.
if (1 < argc && strcmp (argv[1], "-p") == 0)
- yydebug = 1;
- yy::parser parser;
- return !!parser.parse ();
+ parse.set_debug_level (1);
+ return parse ();
}
--
2.29.2