# Checking GLR Parsing. -*- Autotest -*- # Copyright (C) 2002-2015, 2018-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 . AT_BANNER([[C++ Type Syntax (GLR).]]) # _AT_TEST_GLR_CXXTYPES(DECL, RESOLVE1, RESOLVE2) # ----------------------------------------------- # Store into types.y the calc program, with DECL inserted as a declaration, # and with RESOLVE1 and RESOLVE2 as annotations on the conflicted rule for # stmt. Then compile the result. m4_define([_AT_TEST_GLR_CXXTYPES], [AT_BISON_OPTION_PUSHDEFS([%glr-parser $1]) AT_DATA_GRAMMAR([types.y], [[/* Simplified C++ Type and Expression Grammar. */ %define parse.trace $1 %code requires { #include 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 YYSTYPE Node * } %code { static Node *new_nterm (char const *, Node *, Node *, Node *); static Node *new_term (char *); static void free_node (Node *); static char *node_to_string (Node *); ]m4_bmatch([$2], [stmt_merge], [ static YYSTYPE stmt_merge (YYSTYPE x0, YYSTYPE x1);])[ #define YYINITDEPTH 10 #define YYSTACKEXPANDABLE 1 ]AT_YYERROR_DECLARE[ ]AT_YYLEX_DECLARE[ } %token TYPENAME ID %right '=' %left '+' %glr-parser %destructor { free_node ($$); } stmt expr decl declarator TYPENAME ID %% prog : | prog stmt { char *output;]AT_LOCATION_IF([ printf ("%d.%d-%d.%d: ", @2.first_line, @2.first_column, @2.last_line, @2.last_column);])[ output = node_to_string (]$[2); printf ("%s\n", output); free (output); free_node (]$[2); } ; stmt : expr ';' $2 { $$ = ]$[1; } | decl $3 | error ';' { $$ = new_nterm ("", YY_NULLPTR, YY_NULLPTR, YY_NULLPTR); } | '@' { YYACCEPT; } ; expr : ID | TYPENAME '(' expr ')' { $$ = new_nterm ("(%s,%s)", ]$[3, ]$[1, YY_NULLPTR); } | expr '+' expr { $$ = new_nterm ("+(%s,%s)", ]$[1, ]$[3, YY_NULLPTR); } | expr '=' expr { $$ = new_nterm ("=(%s,%s)", ]$[1, ]$[3, YY_NULLPTR); } ; decl : TYPENAME declarator ';' { $$ = new_nterm ("(%s,%s)", ]$[1, ]$[2, YY_NULLPTR); } | TYPENAME declarator '=' expr ';' { $$ = new_nterm ("(%s,%s,%s)", ]$[1, ]$[2, ]$[4); } ; declarator : ID | '(' declarator ')' { $$ = ]$[2; } ; %% #include #include #include #include #include int main (int argc, char **argv) { if (getenv ("YYDEBUG")) yydebug = 1; for (int i = 1; i < argc; ++i) // Enable parse traces on option -p. if (strcmp (argv[i], "-p") == 0) yydebug = 1; else { if (!freopen (argv[i], "r", stdin)) return 3; int status = yyparse (); if (!status) return status; } return 0; } ]AT_YYERROR_DEFINE[ ]AT_YYLEX_PROTOTYPE[ { static int lineNum = 1; static int colNum = 0; #if YYPURE # undef yylloc # define yylloc (*llocp) # undef yylval # define yylval (*lvalp) #endif while (1) { int c; assert (!feof (stdin)); c = getchar (); 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: { int tok;]AT_LOCATION_IF([[ yylloc.first_line = yylloc.last_line = lineNum; yylloc.first_column = colNum;]])[ if (isalpha (c)) { char buffer[256]; unsigned i = 0; do { buffer[i++] = YY_CAST (char, c); colNum += 1; assert (i != sizeof buffer - 1); c = getchar (); } while (isalnum (c) || c == '_'); ungetc (c, stdin); buffer[i++] = 0; tok = isupper (YY_CAST (unsigned char, buffer[0])) ? TYPENAME : ID; yylval = new_term (strcpy (YY_CAST (char *, malloc (i)), buffer)); } else { colNum += 1; tok = c; yylval = YY_NULLPTR; }]AT_LOCATION_IF([[ yylloc.last_column = colNum;]])[ return tok; } } } } static Node * new_nterm (char const *form, Node *child0, Node *child1, Node *child2) { Node *node = YY_CAST (Node *, malloc (sizeof (Node))); node->nterm.isNterm = 1; node->nterm.parents = 0; node->nterm.form = form; node->nterm.children[0] = child0; if (child0) child0->nodeInfo.parents += 1; node->nterm.children[1] = child1; if (child1) child1->nodeInfo.parents += 1; node->nterm.children[2] = child2; if (child2) child2->nodeInfo.parents += 1; return node; } static Node * new_term (char *text) { Node *node = YY_CAST (Node *, malloc (sizeof (Node))); node->term.isNterm = 0; node->term.parents = 0; node->term.text = text; return node; } 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 (Node *node) { char *res; if (!node) { res = YY_CAST (char *, 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 = YY_CAST (char *, 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; } ]] m4_bmatch([$2], [stmt_merge], [[static YYSTYPE stmt_merge (YYSTYPE x0, YYSTYPE x1) { return new_nterm ("(%s,%s)", x0, x1, YY_NULLPTR); } ]]) ) AT_DATA([test-input], [[ z + q; T x; T x = y; x = y; T (x) + y; T (x); T (y) = z + q; T (y y) = z + q; z + q; @ This is total garbage, but it should be ignored. ]]) AT_BISON_CHECK([-o types.c types.y], 0, [], ignore) AT_COMPILE([types]) AT_BISON_OPTION_POPDEFS ]) m4_define([_AT_RESOLVED_GLR_OUTPUT], [[+(z,q) (T,x) (T,x,y) =(x,y) +((x,T),y) (T,x) (T,y,+(z,q)) +(z,q) ]]) m4_define([_AT_RESOLVED_GLR_OUTPUT_WITH_LOC], [[3.0-3.6: +(z,q) 5.0-5.4: (T,x) 7.0-7.8: (T,x,y) 9.0-9.6: =(x,y) 11.0-11.10: +((x,T),y) 13.0-13.6: (T,x) 15.0-15.14: (T,y,+(z,q)) 17.0-17.16: 19.0-19.6: +(z,q) ]]) m4_define([_AT_AMBIG_GLR_OUTPUT], [[+(z,q) (T,x) (T,x,y) =(x,y) +((x,T),y) ((T,x),(x,T)) ((T,y,+(z,q)),=((y,T),+(z,q))) +(z,q) ]]) m4_define([_AT_AMBIG_GLR_OUTPUT_WITH_LOC], [[3.0-3.6: +(z,q) 5.0-5.4: (T,x) 7.0-7.8: (T,x,y) 9.0-9.6: =(x,y) 11.0-11.10: +((x,T),y) 13.0-13.6: ((T,x),(x,T)) 15.0-15.14: ((T,y,+(z,q)),=((y,T),+(z,q))) 17.0-17.16: 19.0-19.6: +(z,q) ]]) m4_define([_AT_GLR_STDERR], [[syntax error ]]) m4_define([_AT_GLR_STDERR_WITH_LOC], [[17.5: syntax error ]]) m4_define([_AT_VERBOSE_GLR_STDERR], [[syntax error, unexpected ID, expecting '=' or '+' or ')' ]]) m4_define([_AT_VERBOSE_GLR_STDERR_WITH_LOC], [[17.5: syntax error, unexpected ID, expecting '=' or '+' or ')' ]]) ## ---------------------------------------------------- ## ## Compile the grammar described in the documentation. ## ## ---------------------------------------------------- ## # AT_TEST([STDOUT], [STDERR]) m4_pushdef([AT_TEST], [AT_PARSER_CHECK([[types test-input]], 0, [$1], [$2]) AT_PARSER_CHECK([[types -p test-input]], 0, [$1], [ignore]) ]) AT_SETUP([GLR: Resolve ambiguity, impure, no locations]) _AT_TEST_GLR_CXXTYPES([], [%dprec 1], [%dprec 2]) AT_TEST([_AT_RESOLVED_GLR_OUTPUT], [_AT_GLR_STDERR]) AT_CLEANUP AT_SETUP([GLR: Resolve ambiguity, impure, locations]) _AT_TEST_GLR_CXXTYPES([%locations],[%dprec 1],[%dprec 2]) AT_TEST([_AT_RESOLVED_GLR_OUTPUT_WITH_LOC], [_AT_GLR_STDERR_WITH_LOC]) AT_CLEANUP AT_SETUP([GLR: Resolve ambiguity, pure, no locations]) _AT_TEST_GLR_CXXTYPES([%define api.pure], [%dprec 1], [%dprec 2]) AT_TEST([_AT_RESOLVED_GLR_OUTPUT], [_AT_GLR_STDERR]) AT_CLEANUP AT_SETUP([GLR: Resolve ambiguity, pure, locations]) _AT_TEST_GLR_CXXTYPES([%define api.pure %locations], [%dprec 1], [%dprec 2]) AT_TEST([_AT_RESOLVED_GLR_OUTPUT_WITH_LOC], [_AT_GLR_STDERR_WITH_LOC]) AT_CLEANUP AT_SETUP([GLR: Merge conflicting parses, impure, no locations]) _AT_TEST_GLR_CXXTYPES([], [%merge ], [%merge ]) AT_TEST([_AT_AMBIG_GLR_OUTPUT], [_AT_GLR_STDERR]) AT_CLEANUP AT_SETUP([GLR: Merge conflicting parses, impure, locations]) _AT_TEST_GLR_CXXTYPES([%locations], [%merge ], [%merge ]) AT_TEST([_AT_AMBIG_GLR_OUTPUT_WITH_LOC], [_AT_GLR_STDERR_WITH_LOC]) AT_CLEANUP AT_SETUP([GLR: Merge conflicting parses, pure, no locations]) _AT_TEST_GLR_CXXTYPES([%define api.pure], [%merge ], [%merge ]) AT_TEST([_AT_AMBIG_GLR_OUTPUT], [_AT_GLR_STDERR]) AT_CLEANUP AT_SETUP([GLR: Merge conflicting parses, pure, locations]) _AT_TEST_GLR_CXXTYPES([%define api.pure %locations], [%merge ],[%merge ]) AT_TEST([_AT_AMBIG_GLR_OUTPUT_WITH_LOC], [_AT_GLR_STDERR_WITH_LOC]) AT_CLEANUP AT_SETUP([GLR: Verbose messages, resolve ambiguity, impure, no locations]) _AT_TEST_GLR_CXXTYPES([%define parse.error verbose], [%merge ], [%merge ]) AT_TEST([_AT_AMBIG_GLR_OUTPUT], [_AT_VERBOSE_GLR_STDERR]) AT_CLEANUP m4_popdef([AT_TEST])