glr2.cc: make the example more C++

Currently the example really looks like C.  Instead of a union of
structs to implement the AST, use a hierarchy.  It would be nice to
feature a C++17 version with std variants.

* examples/c++/glr/c++-types.yy (Node, free_node, new_nterm)
(new_term): Move into...
* examples/c++/glr/ast.hh: here, a proper C++ hierarchy.
This commit is contained in:
Akim Demaille
2020-12-13 08:22:23 +01:00
parent 855d46678a
commit b2d35f5cb8
3 changed files with 125 additions and 122 deletions

100
examples/c++/glr/ast.hh Normal file
View File

@@ -0,0 +1,100 @@
#include <iostream>
#if __cplusplus < 201103L
# define nullptr 0
#endif
class Node
{
public:
Node ()
: parents_ (0)
{}
virtual ~Node ()
{}
void free ()
{
parents_ -= 1;
/* Free only if 0 (last parent) or -1 (no parents). */
if (parents_ <= 0)
delete this;
}
virtual std::ostream& print (std::ostream& o) const = 0;
protected:
friend class Nterm;
friend class Term;
int parents_;
};
static std::ostream&
operator<< (std::ostream& o, const Node &node)
{
return node.print (o);
}
class Nterm : public Node
{
public:
Nterm (char const *form,
Node *child0 = nullptr, Node *child1 = nullptr, Node *child2 = nullptr)
: form_ (form)
{
children_[0] = child0;
if (child0)
child0->parents_ += 1;
children_[1] = child1;
if (child1)
child1->parents_ += 1;
children_[2] = child2;
if (child2)
child2->parents_ += 1;
}
~Nterm ()
{
for (int i = 0; i < 3; ++i)
if (children_[i])
children_[i]->free ();
}
std::ostream& print (std::ostream& o) const
{
o << form_;
if (children_[0])
{
o << '(' << *children_[0];
if (children_[1])
o << ", " << *children_[1];
if (children_[2])
o << ", " << *children_[2];
o << ')';
}
return o;
}
private:
char const *form_;
Node *children_[3];
};
class Term : public Node
{
public:
Term (const std::string &text)
: text_ (text)
{}
std::ostream& print (std::ostream& o) const
{
o << text_;
return o;
}
private:
std::string text_;
};

View File

@@ -11,24 +11,7 @@
%code requires
{
union Node {
struct {
int isNterm;
int parents;
} nodeInfo;
struct {
int isNterm; /* 1 */
int parents;
char const *form;
union Node *children[3];
} nterm;
struct {
int isNterm; /* 0 */
int parents;
char *text;
} term;
};
typedef union Node Node;
#include "ast.hh"
}
%define api.value.type {Node *}
@@ -46,10 +29,6 @@
# define nullptr 0
#endif
static Node *new_nterm (char const *form, Node *child0 = nullptr, Node *child1 = nullptr, Node *child2 = nullptr);
static Node *new_term (char *);
static void free_node (Node *);
static std::ostream& operator<< (std::ostream& o, const Node &node);
static YYSTYPE stmtMerge (YYSTYPE x0, YYSTYPE x1);
static int yylex (YYSTYPE *lvalp, YYLTYPE *llocp);
@@ -64,31 +43,31 @@
%right '='
%left '+'
%destructor { free_node ($$); } stmt expr decl declarator TYPENAME ID
%destructor { $$->free (); } stmt expr decl declarator TYPENAME ID
%%
prog : %empty
| prog stmt { std::cout << @2 << ": " << *$2 << '\n'; free_node ($2); }
| prog stmt { std::cout << @2 << ": " << *$2 << '\n'; $2->free (); }
;
stmt : expr ';' %merge <stmtMerge> { $$ = $1; }
| decl %merge <stmtMerge>
| error ';' { $$ = new_nterm ("<error>"); }
| error ';' { $$ = new 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); }
{ $$ = new Nterm ("<cast>", $3, $1); }
| expr '+' expr { $$ = new Nterm ("+", $1, $3); }
| expr '=' expr { $$ = new Nterm ("=", $1, $3); }
;
decl : TYPENAME declarator ';'
{ $$ = new_nterm ("<declare>", $1, $2); }
{ $$ = new Nterm ("<declare>", $1, $2); }
| TYPENAME declarator '=' expr ';'
{ $$ = new_nterm ("<init-declare>", $1,
{ $$ = new Nterm ("<init-declare>", $1,
$2, $4); }
;
@@ -98,18 +77,6 @@ declarator
;
%%
int
main (int argc, char **argv)
{
// Enable parse traces on option -p.
if (1 < argc && strcmp (argv[1], "-p") == 0)
yydebug = 1;
yy::parser parser;
return !!parser.parse ();
}
/* A C error reporting function. */
void yy::parser::error (const location_type& l, const std::string& m)
{
@@ -147,25 +114,21 @@ int yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
llocp->begin.column = colNum;
if (isalpha (c))
{
char buffer[256];
unsigned i = 0;
std::string form;
do
{
buffer[i++] = static_cast<char> (c);
form += static_cast<char> (c);
colNum += 1;
assert (i != sizeof buffer - 1);
c = getchar ();
}
while (isalnum (c) || c == '_');
ungetc (c, stdin);
buffer[i++] = 0;
tok
= isupper (static_cast <unsigned char> (buffer[0]))
= isupper (static_cast <unsigned char> (form[0]))
? yy::parser::token::TYPENAME
: yy::parser::token::ID;
*lvalp = new_term (strcpy (static_cast<char*> (malloc (i)), buffer));
*lvalp = new Term (form);
}
else
{
@@ -180,79 +143,18 @@ int yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
}
}
static Node *
new_nterm (char const *form, Node *child0, Node *child1, Node *child2)
{
Node *res = new Node;
res->nterm.isNterm = 1;
res->nterm.parents = 0;
res->nterm.form = form;
res->nterm.children[0] = child0;
if (child0)
child0->nodeInfo.parents += 1;
res->nterm.children[1] = child1;
if (child1)
child1->nodeInfo.parents += 1;
res->nterm.children[2] = child2;
if (child2)
child2->nodeInfo.parents += 1;
return res;
}
static Node *
new_term (char *text)
{
Node *res = new Node;
res->term.isNterm = 0;
res->term.parents = 0;
res->term.text = text;
return res;
}
static void
free_node (Node *node)
{
if (!node)
return;
node->nodeInfo.parents -= 1;
/* Free only if 0 (last parent) or -1 (no parents). */
if (node->nodeInfo.parents > 0)
return;
if (node->nodeInfo.isNterm == 1)
{
free_node (node->nterm.children[0]);
free_node (node->nterm.children[1]);
free_node (node->nterm.children[2]);
}
else
free (node->term.text);
delete node;
}
static std::ostream&
operator<< (std::ostream& o, const Node &node)
{
if (node.nodeInfo.isNterm == 1)
{
o << node.nterm.form;
if (node.nterm.children[0])
{
o << '(' << *node.nterm.children[0];
if (node.nterm.children[1])
o << ", " << *node.nterm.children[1];
if (node.nterm.children[2])
o << ", " << *node.nterm.children[2];
o << ")";
}
}
else
o << node.term.text;
return o;
}
static YYSTYPE
stmtMerge (YYSTYPE x0, YYSTYPE x1)
{
return new_nterm ("<OR>", x0, x1);
return new Nterm ("<OR>", x0, x1);
}
int
main (int argc, char **argv)
{
// Enable parse traces on option -p.
if (1 < argc && strcmp (argv[1], "-p") == 0)
yydebug = 1;
yy::parser parser;
return !!parser.parse ();
}

View File

@@ -37,6 +37,7 @@ $(%C%_c___types_OBJECTS): $(cxx_types_sources_generated)
if ENABLE_CXX
check_PROGRAMS += %D%/c++-types
nodist_%C%_c___types_SOURCES = \
%D%/ast.hh \
%D%/c++-types.cc \
%D%/c++-types.hh
# Don't use gnulib's system headers.