/* -*- C -*- Copyright (C) 2020-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Simplified C++ Type and Expression Grammar. Written by Paul Hilfinger for Bison's test suite. */ %define api.pure %header %define api.header.include {"c++-types.h"} %locations %debug /* Nice error messages with details. */ %define parse.error detailed %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; } %define api.value.type union %code { #include #include #include #include #include #include static Node *new_nterm (char const *, Node *, Node *, Node *); static Node *new_term (char *); static void free_node (Node *); static char *node_to_string (const Node *); static void node_print (FILE *, const Node *); static Node *stmt_merge (YYSTYPE x0, YYSTYPE x1); static void yyerror (YYLTYPE const * const loc, const char *msg); static yytoken_kind_t yylex (YYSTYPE *lval, YYLTYPE *lloc); } %expect-rr 1 %token TYPENAME "typename" ID "identifier" %right '=' %left '+' %glr-parser %type stmt expr decl declarator TYPENAME ID %destructor { free_node ($$); } %printer { node_print (yyo, $$); } %% prog : %empty | prog stmt { YYLOCATION_PRINT (stdout, &@2); fputs (": ", stdout); node_print (stdout, $2); putc ('\n', stdout); fflush (stdout); free_node ($2); } ; stmt : expr ';' %merge { $$ = $1; } | decl %merge | error ';' { $$ = new_nterm ("", NULL, NULL, NULL); } ; expr : ID | TYPENAME '(' expr ')' { $$ = new_nterm ("(%s, %s)", $3, $1, NULL); } | expr '+' expr { $$ = new_nterm ("+(%s, %s)", $1, $3, NULL); } | expr '=' expr { $$ = new_nterm ("=(%s, %s)", $1, $3, NULL); } ; decl : TYPENAME declarator ';' { $$ = new_nterm ("(%s, %s)", $1, $2, NULL); } | TYPENAME declarator '=' expr ';' { $$ = new_nterm ("(%s, %s, %s)", $1, $2, $4); } ; declarator : ID | '(' declarator ')' { $$ = $2; } ; %% /* A C error reporting function. */ static void yyerror (YYLTYPE const * const loc, const char *msg) { YYLOCATION_PRINT (stderr, loc); fprintf (stderr, ": %s\n", msg); } /* The input file. */ FILE * input = NULL; yytoken_kind_t yylex (YYSTYPE *lval, YYLTYPE *lloc) { static int lineNum = 1; static int colNum = 0; while (1) { int c; assert (!feof (input)); c = getc (input); switch (c) { case EOF: return 0; case '\t': colNum = (colNum + 7) & ~7; break; case ' ': case '\f': colNum += 1; break; case '\n': lineNum += 1; colNum = 0; break; default: { yytoken_kind_t tok; lloc->first_line = lloc->last_line = lineNum; lloc->first_column = colNum; if (isalpha (c)) { char buffer[256]; unsigned i = 0; do { buffer[i++] = (char) c; colNum += 1; assert (i != sizeof buffer - 1); c = getc (input); } while (isalnum (c) || c == '_'); ungetc (c, input); buffer[i++] = 0; if (isupper ((unsigned char) buffer[0])) { tok = TYPENAME; lval->TYPENAME = new_term (strcpy (malloc (i), buffer)); } else { tok = ID; lval->ID = new_term (strcpy (malloc (i), buffer)); } } else { colNum += 1; tok = c; } lloc->last_column = colNum; return tok; } } } } static Node * new_nterm (char const *form, Node *child0, Node *child1, Node *child2) { Node *res = malloc (sizeof *res); 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 = malloc (sizeof *res); 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); free (node); } static char * node_to_string (const Node *node) { char *res; if (!node) { res = malloc (1); res[0] = 0; } else if (node->nodeInfo.isNterm == 1) { char *child0 = node_to_string (node->nterm.children[0]); char *child1 = node_to_string (node->nterm.children[1]); char *child2 = node_to_string (node->nterm.children[2]); res = malloc (strlen (node->nterm.form) + strlen (child0) + strlen (child1) + strlen (child2) + 1); sprintf (res, node->nterm.form, child0, child1, child2); free (child2); free (child1); free (child0); } else res = strdup (node->term.text); return res; } static void node_print (FILE *out, const Node *n) { char *str = node_to_string (n); fputs (str, out); free (str); } static Node * stmt_merge (YYSTYPE x0, YYSTYPE x1) { return new_nterm ("(%s, %s)", x0.stmt, x1.stmt, NULL); } static int process (const char *file) { int is_stdin = !file || strcmp (file, "-") == 0; if (is_stdin) input = stdin; else input = fopen (file, "r"); assert (input); int status = yyparse (); if (!is_stdin) fclose (input); return status; } int main (int argc, char **argv) { if (getenv ("YYDEBUG")) yydebug = 1; int ran = 0; for (int i = 1; i < argc; ++i) // Enable parse traces on option -p. if (strcmp (argv[i], "-p") == 0) yydebug = 1; else { int status = process (argv[i]); ran = 1; if (!status) return status; } if (!ran) { int status = process (NULL); if (!status) return status; } return 0; }