From b2d35f5cb8a6638271db2e8d4ae4a1df1dcc5c0b Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Sun, 13 Dec 2020 08:22:23 +0100 Subject: [PATCH] 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. --- examples/c++/glr/ast.hh | 100 +++++++++++++++++++++++ examples/c++/glr/c++-types.yy | 146 ++++++---------------------------- examples/c++/glr/local.mk | 1 + 3 files changed, 125 insertions(+), 122 deletions(-) create mode 100644 examples/c++/glr/ast.hh diff --git a/examples/c++/glr/ast.hh b/examples/c++/glr/ast.hh new file mode 100644 index 00000000..20fab2c5 --- /dev/null +++ b/examples/c++/glr/ast.hh @@ -0,0 +1,100 @@ +#include + +#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_; +}; diff --git a/examples/c++/glr/c++-types.yy b/examples/c++/glr/c++-types.yy index ad044c09..1ed5ee09 100644 --- a/examples/c++/glr/c++-types.yy +++ b/examples/c++/glr/c++-types.yy @@ -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 { $$ = $1; } | decl %merge - | error ';' { $$ = new_nterm (""); } + | error ';' { $$ = new Nterm (""); } | '@' { $$ = $1; YYACCEPT; } ; expr : ID | TYPENAME '(' expr ')' - { $$ = new_nterm ("", $3, $1); } - | expr '+' expr { $$ = new_nterm ("+", $1, $3); } - | expr '=' expr { $$ = new_nterm ("=", $1, $3); } + { $$ = new Nterm ("", $3, $1); } + | expr '+' expr { $$ = new Nterm ("+", $1, $3); } + | expr '=' expr { $$ = new Nterm ("=", $1, $3); } ; decl : TYPENAME declarator ';' - { $$ = new_nterm ("", $1, $2); } + { $$ = new Nterm ("", $1, $2); } | TYPENAME declarator '=' expr ';' - { $$ = new_nterm ("", $1, + { $$ = new Nterm ("", $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 (c); + form += static_cast (c); colNum += 1; - assert (i != sizeof buffer - 1); c = getchar (); } while (isalnum (c) || c == '_'); ungetc (c, stdin); - buffer[i++] = 0; tok - = isupper (static_cast (buffer[0])) + = isupper (static_cast (form[0])) ? yy::parser::token::TYPENAME : yy::parser::token::ID; - *lvalp = new_term (strcpy (static_cast (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 ("", x0, x1); + return new Nterm ("", 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 (); } diff --git a/examples/c++/glr/local.mk b/examples/c++/glr/local.mk index 8558651a..ea53aed1 100644 --- a/examples/c++/glr/local.mk +++ b/examples/c++/glr/local.mk @@ -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.