ecpg: re-implement preprocessor's string management.
authorTom Lane <[email protected]>
Mon, 14 Oct 2024 17:44:42 +0000 (13:44 -0400)
committerTom Lane <[email protected]>
Mon, 14 Oct 2024 17:44:42 +0000 (13:44 -0400)
Most productions in the preprocessor grammar construct strings
representing SQL or C statements or fragments thereof.  Instead
of returning these as <str> results of the productions, return
them as "location" values, taking advantage of Bison's flexibility
about what a location is.  We aren't really giving up anything
thereby, since ecpg's error reports have always just given line
numbers, and that's tracked separately.  The advantage of this
is that a single instance of the YYLLOC_DEFAULT macro can
perform all the work needed by the vast majority of productions,
including all the ones made automatically by parse.pl.  This
avoids having large numbers of effectively-identical productions,
which tickles an optimization inefficiency in recent versions of
clang.  (This patch reduces the compilation time for preproc.o
by more than 100-fold with clang 16, and is visibly helpful with
gcc too.)  The compiled parser is noticeably smaller as well.

A disadvantage of this approach is that YYLLOC_DEFAULT is applied
before running the production's semantic action (if any).  This
means it cannot use the method favored by cat_str() of free'ing
all the input strings; if the action needs to look at the input
strings, it'd be looking at dangling storage.  As this stands,
therefore, it leaks memory like a sieve.  This is already a big
patch though, and fixing the memory management seems like a
separable problem, so let's leave that for the next step.
(This does remove some free() calls that I'd have had to touch
anyway, in the expectation that the next step will manage
memory reclamation quite differently.)

Most of the changes here are mindless substitution of "@N" for
"$N" in grammar rules; see the changes to README.parser for
an explanation.

Discussion: https://postgr.es/m/2011420.1713493114@sss.pgh.pa.us

src/interfaces/ecpg/preproc/README.parser
src/interfaces/ecpg/preproc/ecpg.addons
src/interfaces/ecpg/preproc/ecpg.header
src/interfaces/ecpg/preproc/ecpg.trailer
src/interfaces/ecpg/preproc/ecpg.type
src/interfaces/ecpg/preproc/output.c
src/interfaces/ecpg/preproc/parse.pl
src/interfaces/ecpg/preproc/parser.c
src/interfaces/ecpg/preproc/preproc_extern.h

index 5698f5ab328e7cdfcf76689be89605d469376b03..378cb9344c42eefb832a6f7e71196e236997e982 100644 (file)
@@ -4,8 +4,8 @@ Some notes:
 
 1) Most input matching core grammar productions is simply converted
    to strings and concatenated together to form the SQL string
-   passed to the server.  parse.pl can automatically build the
-   grammar actions needed to do this.
+   passed to the server.  This is handled mostly automatically,
+   as described below.
 2) Some grammar rules need special actions that are added to or
    completely override the default token-concatenation behavior.
    This is controlled by ecpg.addons as explained below.
@@ -14,11 +14,31 @@ Some notes:
 4) ecpg.header contains the "prologue" part of preproc.y, including
    support functions, Bison options, etc.
 5) Additional terminals added by ECPG must be defined in ecpg.tokens.
-   Additional nonterminals added by ECPG must be defined in ecpg.type.
+   Additional nonterminals added by ECPG must be defined in ecpg.type,
+   but only if they have non-void result type, which most don't.
 
 ecpg.header, ecpg.tokens, ecpg.type, and ecpg.trailer are just
 copied verbatim into preproc.y at appropriate points.
 
+
+In the pre-v18 implementation of ecpg, the strings constructed
+by grammar rules were returned as the Bison result of each rule.
+This led to a large number of effectively-identical rule actions,
+which caused compilation-time problems with some versions of clang.
+Now, rules that need to return a string are declared as having
+void type (which in Bison means leaving out any %type declaration
+for them).  Instead, we abuse Bison's "location tracking" mechanism
+to carry the string results, which allows a single YYLLOC_DEFAULT
+call to handle the standard token-concatenation behavior for the
+vast majority of the rules.  Rules that don't need to do anything
+else can omit a semantic action altogether.  Rules that need to
+construct an output string specially can do so, but they should
+assign it to "@$" rather than the usual "$$"; also, to reference
+the string value of the N'th input token, write "@N" not "$N".
+(But rules that return something other than a simple string
+continue to use the normal Bison notations.)
+
+
 ecpg.addons contains entries that begin with a line like
        ECPG: concattokens ruletype
 and typically have one or more following lines that are the code
@@ -69,9 +89,9 @@ parse.pl contains some tables that list backend grammar
 productions to be ignored or modified.
 
 Nonterminals that construct strings (as described above) should be
-given <str> type, which is parse.pl's default assumption for
-nonterminals found in gram.y.  That can be overridden at need by
-making an entry in parse.pl's %replace_types table.  %replace_types
+given void type, which is parse.pl's default assumption for
+nonterminals found in gram.y.  If the result should be of some other
+type, make an entry in parse.pl's %replace_types table.  %replace_types
 can also be used to suppress output of a nonterminal's rules
 altogether (in which case ecpg.trailer had better provide replacement
 rules, since the nonterminal will still be referred to elsewhere).
index 6a1893553b4edde9066a24143548726e81a1d658..24ee54554e37b07a4a4fbda3369e1875aa6f30eb 100644 (file)
@@ -3,36 +3,35 @@ ECPG: stmtClosePortalStmt block
    {
        if (INFORMIX_MODE)
        {
-           if (pg_strcasecmp($1 + strlen("close "), "database") == 0)
+           if (pg_strcasecmp(@1 + strlen("close "), "database") == 0)
            {
                if (connection)
                    mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CLOSE DATABASE statement");
 
                fprintf(base_yyout, "{ ECPGdisconnect(__LINE__, \"CURRENT\");");
                whenever_action(2);
-               free($1);
                break;
            }
        }
 
-       output_statement($1, 0, ECPGst_normal);
+       output_statement(@1, 0, ECPGst_normal);
    }
 ECPG: stmtDeallocateStmt block
    {
-       output_deallocate_prepare_statement($1);
+       output_deallocate_prepare_statement(@1);
    }
 ECPG: stmtDeclareCursorStmt block
    {
-       output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+       output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
    }
 ECPG: stmtDiscardStmt block
 ECPG: stmtFetchStmt block
-   { output_statement($1, 1, ECPGst_normal); }
+   { output_statement(@1, 1, ECPGst_normal); }
 ECPG: stmtDeleteStmt block
 ECPG: stmtInsertStmt block
 ECPG: stmtSelectStmt block
 ECPG: stmtUpdateStmt block
-   { output_statement($1, 1, ECPGst_prepnormal); }
+   { output_statement(@1, 1, ECPGst_prepnormal); }
 ECPG: stmtExecuteStmt block
    {
        check_declared_list($1.name);
@@ -94,50 +93,45 @@ ECPG: stmtPrepareStmt block
    }
 ECPG: stmtTransactionStmt block
    {
-       fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
+       fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
        whenever_action(2);
-       free($1);
    }
 ECPG: toplevel_stmtTransactionStmtLegacy block
    {
-       fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
+       fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
        whenever_action(2);
-       free($1);
    }
 ECPG: stmtViewStmt rule
    | ECPGAllocateDescr
    {
-       fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", $1);
+       fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", @1);
        whenever_action(0);
-       free($1);
    }
    | ECPGConnect
    {
        if (connection)
            mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CONNECT statement");
 
-       fprintf(base_yyout, "{ ECPGconnect(__LINE__, %d, %s, %d); ", compat, $1, autocommit);
+       fprintf(base_yyout, "{ ECPGconnect(__LINE__, %d, %s, %d); ", compat, @1, autocommit);
        reset_variables();
        whenever_action(2);
-       free($1);
    }
    | ECPGDeclareStmt
    {
-       output_simple_statement($1, 0);
+       output_simple_statement(@1, 0);
    }
    | ECPGCursorStmt
    {
-       output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+       output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
    }
    | ECPGDeallocateDescr
    {
-       fprintf(base_yyout, "ECPGdeallocate_desc(__LINE__, %s);", $1);
+       fprintf(base_yyout, "ECPGdeallocate_desc(__LINE__, %s);", @1);
        whenever_action(0);
-       free($1);
    }
    | ECPGDeclare
    {
-       output_simple_statement($1, 0);
+       output_simple_statement(@1, 0);
    }
    | ECPGDescribe
    {
@@ -157,27 +151,25 @@ ECPG: stmtViewStmt rule
            mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in DISCONNECT statement");
 
        fprintf(base_yyout, "{ ECPGdisconnect(__LINE__, %s);",
-               $1 ? $1 : "\"CURRENT\"");
+               @1 ? @1 : "\"CURRENT\"");
        whenever_action(2);
-       free($1);
    }
    | ECPGExecuteImmediateStmt
    {
-       output_statement($1, 0, ECPGst_exec_immediate);
+       output_statement(@1, 0, ECPGst_exec_immediate);
    }
    | ECPGFree
    {
        const char *con = connection ? connection : "NULL";
 
-       if (strcmp($1, "all") == 0)
+       if (strcmp(@1, "all") == 0)
            fprintf(base_yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
-       else if ($1[0] == ':')
-           fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, %s);", compat, con, $1 + 1);
+       else if (@1[0] == ':')
+           fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, %s);", compat, con, @1 + 1);
        else
-           fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, $1);
+           fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, @1);
 
        whenever_action(2);
-       free($1);
    }
    | ECPGGetDescriptor
    {
@@ -188,15 +180,14 @@ ECPG: stmtViewStmt rule
    }
    | ECPGGetDescriptorHeader
    {
-       lookup_descriptor($1, connection);
-       output_get_descr_header($1);
-       free($1);
+       lookup_descriptor(@1, connection);
+       output_get_descr_header(@1);
    }
    | ECPGOpen
    {
        struct cursor *ptr;
 
-       if ((ptr = add_additional_variables($1, true)) != NULL)
+       if ((ptr = add_additional_variables(@1, true)) != NULL)
        {
            connection = ptr->connection ? mm_strdup(ptr->connection) : NULL;
            output_statement(mm_strdup(ptr->command), 0, ECPGst_normal);
@@ -205,18 +196,16 @@ ECPG: stmtViewStmt rule
    }
    | ECPGSetAutocommit
    {
-       fprintf(base_yyout, "{ ECPGsetcommit(__LINE__, \"%s\", %s);", $1, connection ? connection : "NULL");
+       fprintf(base_yyout, "{ ECPGsetcommit(__LINE__, \"%s\", %s);", @1, connection ? connection : "NULL");
        whenever_action(2);
-       free($1);
    }
    | ECPGSetConnection
    {
        if (connection)
            mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in SET CONNECTION statement");
 
-       fprintf(base_yyout, "{ ECPGsetconn(__LINE__, %s);", $1);
+       fprintf(base_yyout, "{ ECPGsetconn(__LINE__, %s);", @1);
        whenever_action(2);
-       free($1);
    }
    | ECPGSetDescriptor
    {
@@ -227,17 +216,15 @@ ECPG: stmtViewStmt rule
    }
    | ECPGSetDescriptorHeader
    {
-       lookup_descriptor($1, connection);
-       output_set_descr_header($1);
-       free($1);
+       lookup_descriptor(@1, connection);
+       output_set_descr_header(@1);
    }
    | ECPGTypedef
    {
        if (connection)
            mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in TYPE statement");
 
-       fprintf(base_yyout, "%s", $1);
-       free($1);
+       fprintf(base_yyout, "%s", @1);
        output_line_number();
    }
    | ECPGVar
@@ -245,180 +232,169 @@ ECPG: stmtViewStmt rule
        if (connection)
            mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in VAR statement");
 
-       output_simple_statement($1, 0);
+       output_simple_statement(@1, 0);
    }
    | ECPGWhenever
    {
        if (connection)
            mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in WHENEVER statement");
 
-       output_simple_statement($1, 0);
+       output_simple_statement(@1, 0);
    }
 ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block
    {
-       char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+       char       *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
 
-       $$ = cat_str(2, mm_strdup("where current of"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("where current of"), cursor_marker);
    }
 ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listcopy_fromopt_programcopy_file_namecopy_delimiteropt_withcopy_optionswhere_clause addon
-       if (strcmp($6, "from") == 0 &&
-           (strcmp($7, "stdin") == 0 || strcmp($7, "stdout") == 0))
+       if (strcmp(@6, "from") == 0 &&
+           (strcmp(@7, "stdin") == 0 || strcmp(@7, "stdout") == 0))
            mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
 ECPG: var_valueNumericOnly addon
-       if ($1[0] == '$')
-       {
-           free($1);
-           $1 = mm_strdup("$0");
-       }
+       if (@1[0] == '$')
+           @$ = mm_strdup("$0");
 ECPG: fetch_argscursor_name addon
-       struct cursor *ptr = add_additional_variables($1, false);
+       struct cursor *ptr = add_additional_variables(@1, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
-       if ($1[0] == ':')
-       {
-           free($1);
-           $1 = mm_strdup("$0");
-       }
+       if (@1[0] == ':')
+           @$ = mm_strdup("$0");
 ECPG: fetch_argsfrom_incursor_name addon
-       struct cursor *ptr = add_additional_variables($2, false);
+       struct cursor *ptr = add_additional_variables(@2, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
-       if ($2[0] == ':')
-       {
-           free($2);
-           $2 = mm_strdup("$0");
-       }
+       if (@2[0] == ':')
+           @$ = cat2_str(mm_strdup(@1), mm_strdup("$0"));
 ECPG: fetch_argsNEXTopt_from_incursor_name addon
 ECPG: fetch_argsPRIORopt_from_incursor_name addon
 ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
 ECPG: fetch_argsLAST_Popt_from_incursor_name addon
 ECPG: fetch_argsALLopt_from_incursor_name addon
-       struct cursor *ptr = add_additional_variables($3, false);
+       struct cursor *ptr = add_additional_variables(@3, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
-       if ($3[0] == ':')
-       {
-           free($3);
-           $3 = mm_strdup("$0");
-       }
+       if (@3[0] == ':')
+           @$ = cat_str(3, mm_strdup(@1), mm_strdup(@2), mm_strdup("$0"));
 ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
-       struct cursor *ptr = add_additional_variables($3, false);
+       struct cursor *ptr = add_additional_variables(@3, false);
+       bool    replace = false;
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
-       if ($3[0] == ':')
+       if (@3[0] == ':')
        {
-           free($3);
-           $3 = mm_strdup("$0");
+           @3 = mm_strdup("$0");
+           replace = true;
        }
-       if ($1[0] == '$')
+       if (@1[0] == '$')
        {
-           free($1);
-           $1 = mm_strdup("$0");
+           @1 = mm_strdup("$0");
+           replace = true;
        }
+       if (replace)
+           @$ = cat_str(3, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3));
 ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
-       struct cursor *ptr = add_additional_variables($4, false);
+       struct cursor *ptr = add_additional_variables(@4, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
-       if ($4[0] == ':')
-       {
-           free($4);
-           $4 = mm_strdup("$0");
-       }
+       if (@4[0] == ':')
+           @$ = cat_str(4, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3), mm_strdup("$0"));
 ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
-       struct cursor *ptr = add_additional_variables($4, false);
+       struct cursor *ptr = add_additional_variables(@4, false);
+       bool    replace = false;
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
-       if ($4[0] == ':')
+       if (@4[0] == ':')
        {
-           free($4);
-           $4 = mm_strdup("$0");
+           @4 = mm_strdup("$0");
+           replace = true;
        }
-       if ($2[0] == '$')
+       if (@2[0] == '$')
        {
-           free($2);
-           $2 = mm_strdup("$0");
+           @2 = mm_strdup("$0");
+           replace = true;
        }
-ECPG: cursor_namename rule
+       if (replace)
+           @$ = cat_str(4, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3), mm_strdup(@4));
+ECPG: cursor_namename block
    | char_civar
    {
-       char       *curname = mm_alloc(strlen($1) + 2);
+       char       *curname = mm_alloc(strlen(@1) + 2);
 
-       sprintf(curname, ":%s", $1);
-       free($1);
-       $1 = curname;
-       $$ = $1;
+       sprintf(curname, ":%s", @1);
+       @$ = curname;
    }
 ECPG: ExplainableStmtExecuteStmt block
    {
-       $$ = $1.name;
+       @$ = $1.name;
    }
 ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
    {
-       $$.name = $2;
-       $$.type = $3;
-       $$.stmt = $5;
+       $$.name = @2;
+       $$.type = @3;
+       $$.stmt = @5;
    }
    | PREPARE prepared_name FROM execstring
    {
-       $$.name = $2;
+       $$.name = @2;
        $$.type = NULL;
-       $$.stmt = $4;
+       $$.stmt = @4;
    }
 ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
    {
-       $$.name = $2;
-       $$.type = $3;
+       $$.name = @2;
+       $$.type = @3;
    }
 ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block
    {
-       $$.name = cat_str(8, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as execute"), $7, $8, $9);
+       $$.name = @$;
    }
 ECPG: ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block
    {
-       $$.name = cat_str(8, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as execute"), $10, $11, $12);
+       $$.name = @$;
    }
 ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
    {
        struct cursor *ptr,
                   *this;
-       char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
+       char       *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : mm_strdup(@2);
        char       *comment,
                   *c1,
                   *c2;
-       int         (*strcmp_fn) (const char *, const char *) = (($2[0] == ':' || $2[0] == '"') ? strcmp : pg_strcasecmp);
+       int         (*strcmp_fn) (const char *, const char *) = ((@2[0] == ':' || @2[0] == '"') ? strcmp : pg_strcasecmp);
 
-       if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
+       if (INFORMIX_MODE && pg_strcasecmp(@2, "database") == 0)
            mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
 
        for (ptr = cur; ptr != NULL; ptr = ptr->next)
        {
-           if (strcmp_fn($2, ptr->name) == 0)
+           if (strcmp_fn(@2, ptr->name) == 0)
            {
-               if ($2[0] == ':')
-                   mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not supported", $2 + 1);
+               if (@2[0] == ':')
+                   mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not supported", @2 + 1);
                else
-                   mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", $2);
+                   mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", @2);
            }
        }
 
        this = (struct cursor *) mm_alloc(sizeof(struct cursor));
 
        this->next = cur;
-       this->name = $2;
+       this->name = mm_strdup(@2);
        this->function = (current_function ? mm_strdup(current_function) : NULL);
        this->connection = connection ? mm_strdup(connection) : NULL;
        this->opened = false;
-       this->command = cat_str(7, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for"), $7);
+       this->command = cat_str(7, mm_strdup("declare"), cursor_marker, @3, mm_strdup("cursor"), @5, mm_strdup("for"), @7);
        this->argsinsert = argsinsert;
        this->argsinsert_oos = NULL;
        this->argsresult = argsresult;
@@ -435,47 +411,47 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
        }
        comment = cat_str(3, mm_strdup("/*"), c1, mm_strdup("*/"));
 
-       $$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
+       @$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
    }
 ECPG: ClosePortalStmtCLOSEcursor_name block
    {
-       char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
+       char       *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : @2;
        struct cursor *ptr = NULL;
 
        for (ptr = cur; ptr != NULL; ptr = ptr->next)
        {
-           if (strcmp($2, ptr->name) == 0)
+           if (strcmp(@2, ptr->name) == 0)
            {
                if (ptr->connection)
                    connection = mm_strdup(ptr->connection);
                break;
            }
        }
-       $$ = cat2_str(mm_strdup("close"), cursor_marker);
+       @$ = cat2_str(mm_strdup("close"), cursor_marker);
    }
 ECPG: opt_hold block
    {
        if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
-           $$ = mm_strdup("with hold");
+           @$ = mm_strdup("with hold");
        else
-           $$ = EMPTY;
+           @$ = EMPTY;
    }
 ECPG: into_clauseINTOOptTempTableName block
    {
        FoundInto = 1;
-       $$ = cat2_str(mm_strdup("into"), $2);
+       @$ = cat2_str(mm_strdup("into"), @2);
    }
    | ecpg_into
    {
-       $$ = EMPTY;
+       @$ = EMPTY;
    }
 ECPG: TypenameSimpleTypenameopt_array_bounds block
    {
-       $$ = cat2_str($1, $2.str);
+       @$ = cat2_str(@1, $2.str);
    }
 ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
    {
-       $$ = cat_str(3, mm_strdup("setof"), $2, $3.str);
+       @$ = cat_str(3, mm_strdup("setof"), @2, $3.str);
    }
 ECPG: opt_array_boundsopt_array_bounds'['']' block
    {
@@ -492,10 +468,10 @@ ECPG: opt_array_boundsopt_array_bounds'['']' block
        $$.index1 = $1.index1;
        $$.index2 = $1.index2;
        if (strcmp($1.index1, "-1") == 0)
-           $$.index1 = mm_strdup($3);
+           $$.index1 = mm_strdup(@3);
        else if (strcmp($1.index2, "-1") == 0)
-           $$.index2 = mm_strdup($3);
-       $$.str = cat_str(4, $1.str, mm_strdup("["), $3, mm_strdup("]"));
+           $$.index2 = mm_strdup(@3);
+       $$.str = cat_str(4, $1.str, mm_strdup("["), @3, mm_strdup("]"));
    }
 ECPG: opt_array_bounds block
    {
@@ -505,108 +481,100 @@ ECPG: opt_array_bounds block
    }
 ECPG: IconstICONST block
    {
-       $$ = make_name();
+       @$ = make_name();
    }
 ECPG: AexprConstNULL_P rule
-   | civar                         { $$ = $1; }
-   | civarind                      { $$ = $1; }
+   | civar
+   | civarind
 ECPG: VariableShowStmtSHOWALL block
    {
        mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
-       $$ = EMPTY;
    }
 ECPG: FetchStmtMOVEfetch_args rule
    | FETCH fetch_args ecpg_fetch_into
-   {
-       $$ = cat2_str(mm_strdup("fetch"), $2);
-   }
    | FETCH FORWARD cursor_name opt_ecpg_fetch_into
    {
-       char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-       struct cursor *ptr = add_additional_variables($3, false);
+       char       *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+       struct cursor *ptr = add_additional_variables(@3, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("fetch forward"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("fetch forward"), cursor_marker);
    }
    | FETCH FORWARD from_in cursor_name opt_ecpg_fetch_into
    {
-       char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
-       struct cursor *ptr = add_additional_variables($4, false);
+       char       *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+       struct cursor *ptr = add_additional_variables(@4, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("fetch forward from"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("fetch forward from"), cursor_marker);
    }
    | FETCH BACKWARD cursor_name opt_ecpg_fetch_into
    {
-       char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-       struct cursor *ptr = add_additional_variables($3, false);
+       char       *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+       struct cursor *ptr = add_additional_variables(@3, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("fetch backward"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("fetch backward"), cursor_marker);
    }
    | FETCH BACKWARD from_in cursor_name opt_ecpg_fetch_into
    {
-       char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
-       struct cursor *ptr = add_additional_variables($4, false);
+       char       *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+       struct cursor *ptr = add_additional_variables(@4, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker);
    }
    | MOVE FORWARD cursor_name
    {
-       char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-       struct cursor *ptr = add_additional_variables($3, false);
+       char       *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+       struct cursor *ptr = add_additional_variables(@3, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("move forward"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("move forward"), cursor_marker);
    }
    | MOVE FORWARD from_in cursor_name
    {
-       char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
-       struct cursor *ptr = add_additional_variables($4, false);
+       char       *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+       struct cursor *ptr = add_additional_variables(@4, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("move forward from"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("move forward from"), cursor_marker);
    }
    | MOVE BACKWARD cursor_name
    {
-       char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-       struct cursor *ptr = add_additional_variables($3, false);
+       char       *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+       struct cursor *ptr = add_additional_variables(@3, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("move backward"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("move backward"), cursor_marker);
    }
    | MOVE BACKWARD from_in cursor_name
    {
-       char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
-       struct cursor *ptr = add_additional_variables($4, false);
+       char       *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+       struct cursor *ptr = add_additional_variables(@4, false);
 
        if (ptr->connection)
            connection = mm_strdup(ptr->connection);
 
-       $$ = cat_str(2, mm_strdup("move backward from"), cursor_marker);
+       @$ = cat_str(2, mm_strdup("move backward from"), cursor_marker);
    }
 ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block
    {
        mmerror(PARSE_ERROR, ET_WARNING, "no longer supported LIMIT #,# syntax passed to server");
-       $$ = cat_str(4, mm_strdup("limit"), $2, mm_strdup(","), $4);
    }
 ECPG: SignedIconstIconst rule
    | civar
-   {
-       $$ = $1;
-   }
index 28e1b2aac4055e07d81636f560c1d5ffa0048050..8df6248c976dd3616c87dafd09431b5ddd651fd8 100644 (file)
 extern int base_yychar;
 extern int base_yynerrs;
 
-/* Location tracking support --- simpler than bison's default */
-#define YYLLOC_DEFAULT(Current, Rhs, N) \
-   do { \
-       if (N)                      \
-           (Current) = (Rhs)[1];   \
-       else                        \
-           (Current) = (Rhs)[0];   \
-   } while (0)
 
 /*
  * The %name-prefix option below will make bison call base_yylex, but we
@@ -200,6 +192,61 @@ make3_str(char *str1, char *str2, char *str3)
    return res_str;
 }
 
+/*
+ * "Location tracking" support.  We commandeer Bison's location tracking
+ * mechanism to manage the output string for productions that ordinarily would
+ * return a <str> result.  This allows the majority of those productions to
+ * have default semantic actions, reducing the size of the parser, and also
+ * greatly reducing its compilation time on some versions of clang.
+ *
+ * To do this, we make YYLTYPE be a pointer to a malloc'd string, and then
+ * merge the location strings of the input tokens in the default YYLLOC
+ * computation.  Productions that are okay with the standard merge need not
+ * do anything more; otherwise, they can override it by assigning to @$.
+ */
+#define YYLLOC_DEFAULT(Current, Rhs, N) yylloc_default(&(Current), Rhs, N)
+
+static void
+yylloc_default(YYLTYPE *target, YYLTYPE *rhs, int N)
+{
+   if (N > 1)
+   {
+       /* Concatenate non-empty inputs with one space between them */
+       char       *result,
+                  *ptr;
+       size_t      needed = 0;
+
+       for (int i = 1; i <= N; i++)
+       {
+           size_t      thislen = strlen(rhs[i]);
+
+           if (needed > 0 && thislen > 0)
+               needed++;
+           needed += thislen;
+       }
+       result = (char *) mm_alloc(needed + 1);
+       ptr = result;
+       for (int i = 1; i <= N; i++)
+       {
+           size_t      thislen = strlen(rhs[i]);
+
+           if (ptr > result && thislen > 0)
+               *ptr++ = ' ';
+           memcpy(ptr, rhs[i], thislen);
+           ptr += thislen;
+       }
+       *ptr = '\0';
+       *target = result;
+   }
+   else if (N == 1)
+   {
+       /* Just re-use the single input */
+       *target = rhs[1];
+   }
+   else
+       *target = EMPTY;
+}
+
 /* and the rest */
 static char *
 make_name(void)
index f3ab73bed616ac46cbad438801c16767af427211..2a3949ca035e00c29f11dc7f578d6044f1c7ab74 100644 (file)
@@ -18,20 +18,17 @@ statement: ecpgstart at toplevel_stmt ';'
    }
    | ecpgstart ECPGVarDeclaration
    {
-       fprintf(base_yyout, "%s", $2);
-       free($2);
+       fprintf(base_yyout, "%s", @$);
        output_line_number();
    }
    | ECPGDeclaration
    | c_thing
    {
-       fprintf(base_yyout, "%s", $1);
-       free($1);
+       fprintf(base_yyout, "%s", @$);
    }
    | CPP_LINE
    {
-       fprintf(base_yyout, "%s", $1);
-       free($1);
+       fprintf(base_yyout, "%s", @$);
    }
    | '{'
    {
@@ -58,8 +55,6 @@ CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
    {
        if (FoundInto == 1)
            mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
-
-       $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as"), $7, $8);
    }
    | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS
    {
@@ -68,14 +63,12 @@ CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
    {
        if (FoundInto == 1)
            mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
-
-       $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as"), $10, $11);
    }
    ;
 
 at: AT connection_object
    {
-       connection = $2;
+       connection = @2;
 
        /*
         * Do we have a variable as connection target?  Remove the variable
@@ -91,55 +84,52 @@ at: AT connection_object
  */
 ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
    {
-       $$ = cat_str(5, $3, mm_strdup(","), $5, mm_strdup(","), $4);
+       @$ = cat_str(5, @3, mm_strdup(","), @5, mm_strdup(","), @4);
    }
    | SQL_CONNECT TO DEFAULT
    {
-       $$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\"");
+       @$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\"");
    }
    /* also allow ORACLE syntax */
    | SQL_CONNECT ora_user
    {
-       $$ = cat_str(3, mm_strdup("NULL,"), $2, mm_strdup(", NULL"));
+       @$ = cat_str(3, mm_strdup("NULL,"), @2, mm_strdup(", NULL"));
    }
    | DATABASE connection_target
    {
-       $$ = cat2_str($2, mm_strdup(", NULL, NULL, NULL"));
+       @$ = cat2_str(@2, mm_strdup(", NULL, NULL, NULL"));
    }
    ;
 
 connection_target: opt_database_name opt_server opt_port
    {
        /* old style: dbname[@server][:port] */
-       if (strlen($2) > 0 && *($2) != '@')
-           mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", $2);
+       if (strlen(@2) > 0 && *(@2) != '@')
+           mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", @2);
 
        /* C strings need to be handled differently */
-       if ($1[0] == '\"')
-           $$ = $1;
+       if (@1[0] == '\"')
+           @$ = @1;
        else
-           $$ = make3_str(mm_strdup("\""), make3_str($1, $2, $3), mm_strdup("\""));
+           @$ = make3_str(mm_strdup("\""), make3_str(@1, @2, @3), mm_strdup("\""));
    }
    | db_prefix ':' server opt_port '/' opt_database_name opt_options
    {
        /* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
-       if (strncmp($1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp($1, "tcp:postgresql", strlen("tcp:postgresql")) != 0)
+       if (strncmp(@1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp(@1, "tcp:postgresql", strlen("tcp:postgresql")) != 0)
            mmerror(PARSE_ERROR, ET_ERROR, "only protocols \"tcp\" and \"unix\" and database type \"postgresql\" are supported");
 
-       if (strncmp($3, "//", strlen("//")) != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", $3);
+       if (strncmp(@3, "//", strlen("//")) != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", @3);
 
-       if (strncmp($1, "unix", strlen("unix")) == 0 &&
-           strncmp($3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
-           strncmp($3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", $3 + strlen("//"));
+       if (strncmp(@1, "unix", strlen("unix")) == 0 &&
+           strncmp(@3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
+           strncmp(@3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", @3 + strlen("//"));
 
-       $$ = make3_str(make3_str(mm_strdup("\""), $1, mm_strdup(":")), $3, make3_str(make3_str($4, mm_strdup("/"), $6), $7, mm_strdup("\"")));
+       @$ = make3_str(make3_str(mm_strdup("\""), @1, mm_strdup(":")), @3, make3_str(make3_str(@4, mm_strdup("/"), @6), @7, mm_strdup("\"")));
    }
    | char_variable
-   {
-       $$ = $1;
-   }
    | ecpg_sconst
    {
        /*
@@ -147,128 +137,107 @@ connection_target: opt_database_name opt_server opt_port
         * so we change the quotes. Note, that the rule for ecpg_sconst adds
         * these single quotes.
         */
-       $1[0] = '\"';
-       $1[strlen($1) - 1] = '\"';
-       $$ = $1;
+       @1[0] = '\"';
+       @1[strlen(@1) - 1] = '\"';
+       @$ = @1;
    }
    ;
 
 opt_database_name: name
-   {
-       $$ = $1;
-   }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 db_prefix: ecpg_ident cvariable
    {
-       if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", $2);
+       if (strcmp(@2, "postgresql") != 0 && strcmp(@2, "postgres") != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", @2);
 
-       if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", $1);
+       if (strcmp(@1, "tcp") != 0 && strcmp(@1, "unix") != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", @1);
 
-       $$ = make3_str($1, mm_strdup(":"), $2);
+       @$ = make3_str(@1, mm_strdup(":"), @2);
    }
    ;
 
 server: Op server_name
    {
-       if (strcmp($1, "@") != 0 && strcmp($1, "//") != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", $1);
+       if (strcmp(@1, "@") != 0 && strcmp(@1, "//") != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", @1);
 
-       $$ = make2_str($1, $2);
+       @$ = make2_str(@1, @2);
    }
    ;
 
 opt_server: server
-   {
-       $$ = $1;
-   }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 server_name: ColId
-   {
-       $$ = $1;
-   }
    | ColId '.' server_name
-   {
-       $$ = make3_str($1, mm_strdup("."), $3);
-   }
    | IP
    {
-       $$ = make_name();
+       @$ = make_name();
    }
    ;
 
 opt_port: ':' Iconst
    {
-       $$ = make2_str(mm_strdup(":"), $2);
+       @$ = make2_str(mm_strdup(":"), @2);
    }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 opt_connection_name: AS connection_object
    {
-       $$ = $2;
+       @$ = @2;
    }
    | /* EMPTY */
    {
-       $$ = mm_strdup("NULL");
+       @$ = mm_strdup("NULL");
    }
    ;
 
 opt_user: USER ora_user
    {
-       $$ = $2;
+       @$ = @2;
    }
    | /* EMPTY */
    {
-       $$ = mm_strdup("NULL, NULL");
+       @$ = mm_strdup("NULL, NULL");
    }
    ;
 
 ora_user: user_name
    {
-       $$ = cat2_str($1, mm_strdup(", NULL"));
+       @$ = cat2_str(@1, mm_strdup(", NULL"));
    }
    | user_name '/' user_name
    {
-       $$ = cat_str(3, $1, mm_strdup(","), $3);
+       @$ = cat_str(3, @1, mm_strdup(","), @3);
    }
    | user_name SQL_IDENTIFIED BY user_name
    {
-       $$ = cat_str(3, $1, mm_strdup(","), $4);
+       @$ = cat_str(3, @1, mm_strdup(","), @4);
    }
    | user_name USING user_name
    {
-       $$ = cat_str(3, $1, mm_strdup(","), $3);
+       @$ = cat_str(3, @1, mm_strdup(","), @3);
    }
    ;
 
 user_name: RoleId
    {
-       if ($1[0] == '\"')
-           $$ = $1;
+       if (@1[0] == '\"')
+           @$ = @1;
        else
-           $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+           @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
    }
    | ecpg_sconst
    {
-       if ($1[0] == '\"')
-           $$ = $1;
+       if (@1[0] == '\"')
+           @$ = @1;
        else
-           $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+           @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
    }
    | civar
    {
@@ -280,16 +249,16 @@ user_name: RoleId
 
        /* handle varchars */
        if (type == ECPGt_varchar)
-           $$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
+           @$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
        else
-           $$ = mm_strdup(argsinsert->variable->name);
+           @$ = mm_strdup(argsinsert->variable->name);
    }
    ;
 
 char_variable: cvariable
    {
        /* check if we have a string variable */
-       struct variable *p = find_variable($1);
+       struct variable *p = find_variable(@1);
        enum ECPGttype type = p->type->type;
 
        /* If we have just one character this is not a string */
@@ -306,14 +275,14 @@ char_variable: cvariable
                case ECPGt_char:
                case ECPGt_unsigned_char:
                case ECPGt_string:
-                   $$ = $1;
+                   @$ = @1;
                    break;
                case ECPGt_varchar:
-                   $$ = make2_str($1, mm_strdup(".arr"));
+                   @$ = make2_str(@1, mm_strdup(".arr"));
                    break;
                default:
                    mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
-                   $$ = $1;
+                   @$ = @1;
                    break;
            }
        }
@@ -322,72 +291,63 @@ char_variable: cvariable
 
 opt_options: Op connect_options
    {
-       if (strlen($1) == 0)
+       if (strlen(@1) == 0)
            mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");
 
-       if (strcmp($1, "?") != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $1);
+       if (strcmp(@1, "?") != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @1);
 
-       $$ = make2_str(mm_strdup("?"), $2);
+       @$ = make2_str(mm_strdup("?"), @2);
    }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 connect_options: ColId opt_opt_value
    {
-       $$ = make2_str($1, $2);
+       @$ = make2_str(@1, @2);
    }
    | ColId opt_opt_value Op connect_options
    {
-       if (strlen($3) == 0)
+       if (strlen(@3) == 0)
            mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");
 
-       if (strcmp($3, "&") != 0)
-           mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $3);
+       if (strcmp(@3, "&") != 0)
+           mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @3);
 
-       $$ = make3_str(make2_str($1, $2), $3, $4);
+       @$ = make3_str(make2_str(@1, @2), @3, @4);
    }
    ;
 
 opt_opt_value: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | '=' Iconst
    {
-       $$ = make2_str(mm_strdup("="), $2);
+       @$ = make2_str(mm_strdup("="), @2);
    }
    | '=' ecpg_ident
    {
-       $$ = make2_str(mm_strdup("="), $2);
+       @$ = make2_str(mm_strdup("="), @2);
    }
    | '=' civar
    {
-       $$ = make2_str(mm_strdup("="), $2);
+       @$ = make2_str(mm_strdup("="), @2);
    }
    ;
 
 prepared_name: name
    {
-       if ($1[0] == '\"' && $1[strlen($1) - 1] == '\"')    /* already quoted? */
-           $$ = $1;
+       if (@1[0] == '\"' && @1[strlen(@1) - 1] == '\"')    /* already quoted? */
+           @$ = @1;
        else                    /* not quoted => convert to lowercase */
        {
            size_t      i;
 
-           for (i = 0; i < strlen($1); i++)
-               $1[i] = tolower((unsigned char) $1[i]);
+           for (i = 0; i < strlen(@1); i++)
+               @1[i] = tolower((unsigned char) @1[i]);
 
-           $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+           @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
        }
    }
    | char_variable
-   {
-       $$ = $1;
-   }
    ;
 
 /*
@@ -400,7 +360,7 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
        /* Check whether the declared name has been defined or not */
        for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
        {
-           if (strcmp($2, ptr->name) == 0)
+           if (strcmp(@2, ptr->name) == 0)
            {
                /* re-definition is not allowed */
                mmerror(PARSE_ERROR, ET_ERROR, "name \"%s\" is already declared", ptr->name);
@@ -413,7 +373,7 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
        if (ptr)
        {
            /* initial definition */
-           ptr->name = $2;
+           ptr->name = @2;
            if (connection)
                ptr->connection = mm_strdup(connection);
            else
@@ -423,7 +383,7 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
            g_declared_list = ptr;
        }
 
-       $$ = cat_str(3, mm_strdup("/* declare "), mm_strdup($2), mm_strdup(" as an SQL identifier */"));
+       @$ = cat_str(3, mm_strdup("/* declare "), mm_strdup(@2), mm_strdup(" as an SQL identifier */"));
    }
    ;
 
@@ -435,26 +395,26 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_
    {
        struct cursor *ptr,
                   *this;
-       char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
-       int         (*strcmp_fn) (const char *, const char *) = (($2[0] == ':' || $2[0] == '"') ? strcmp : pg_strcasecmp);
+       char       *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : mm_strdup(@2);
+       int         (*strcmp_fn) (const char *, const char *) = ((@2[0] == ':' || @2[0] == '"') ? strcmp : pg_strcasecmp);
        struct variable *thisquery = (struct variable *) mm_alloc(sizeof(struct variable));
        char       *comment;
        char       *con;
 
-       if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
+       if (INFORMIX_MODE && pg_strcasecmp(@2, "database") == 0)
            mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
 
-       check_declared_list($7);
+       check_declared_list(@7);
        con = connection ? connection : "NULL";
        for (ptr = cur; ptr != NULL; ptr = ptr->next)
        {
-           if (strcmp_fn($2, ptr->name) == 0)
+           if (strcmp_fn(@2, ptr->name) == 0)
            {
                /* re-definition is a bug */
-               if ($2[0] == ':')
-                   mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not supported", $2 + 1);
+               if (@2[0] == ':')
+                   mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not supported", @2 + 1);
                else
-                   mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", $2);
+                   mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", @2);
            }
        }
 
@@ -462,24 +422,24 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_
 
        /* initial definition */
        this->next = cur;
-       this->name = $2;
+       this->name = @2;
        this->function = (current_function ? mm_strdup(current_function) : NULL);
        this->connection = connection ? mm_strdup(connection) : NULL;
-       this->command = cat_str(6, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for $1"));
+       this->command = cat_str(6, mm_strdup("declare"), cursor_marker, @3, mm_strdup("cursor"), @5, mm_strdup("for $1"));
        this->argsresult = NULL;
        this->argsresult_oos = NULL;
 
        thisquery->type = &ecpg_query;
        thisquery->brace_level = 0;
        thisquery->next = NULL;
-       thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen($7));
-       sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
+       thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen(@7));
+       sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, @7);
 
        this->argsinsert = NULL;
        this->argsinsert_oos = NULL;
-       if ($2[0] == ':')
+       if (@2[0] == ':')
        {
-           struct variable *var = find_variable($2 + 1);
+           struct variable *var = find_variable(@2 + 1);
 
            remove_variable_from_list(&argsinsert, var);
            add_variable_to_head(&(this->argsinsert), var, &no_indicator);
@@ -490,7 +450,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_
 
        comment = cat_str(3, mm_strdup("/*"), mm_strdup(this->command), mm_strdup("*/"));
 
-       $$ = cat_str(2, adjust_outofscope_cursor_vars(this),
+       @$ = cat_str(2, adjust_outofscope_cursor_vars(this),
                     comment);
    }
    ;
@@ -501,7 +461,7 @@ ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
         * execute immediate means prepare the statement and immediately
         * execute it
         */
-       $$ = $3;
+       @$ = @3;
    }
    ;
 
@@ -511,36 +471,24 @@ ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
 ECPGVarDeclaration: single_vt_declaration;
 
 single_vt_declaration: type_declaration
-   {
-       $$ = $1;
-   }
    | var_declaration
-   {
-       $$ = $1;
-   }
    ;
 
 precision: NumericOnly
-   {
-       $$ = $1;
-   }
    ;
 
 opt_scale: ',' NumericOnly
    {
-       $$ = $2;
+       @$ = @2;
    }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
-ecpg_interval: opt_interval            { $$ = $1; }
-   | YEAR_P TO MINUTE_P            { $$ = mm_strdup("year to minute"); }
-   | YEAR_P TO SECOND_P            { $$ = mm_strdup("year to second"); }
-   | DAY_P TO DAY_P                { $$ = mm_strdup("day to day"); }
-   | MONTH_P TO MONTH_P            { $$ = mm_strdup("month to month"); }
+ecpg_interval: opt_interval
+   | YEAR_P TO MINUTE_P
+   | YEAR_P TO SECOND_P
+   | DAY_P TO DAY_P
+   | MONTH_P TO MONTH_P
    ;
 
 /*
@@ -552,8 +500,7 @@ ECPGDeclaration: sql_startdeclare
    }
    var_type_declarations sql_enddeclare
    {
-       fprintf(base_yyout, "%s/* exec sql end declare section */", $3);
-       free($3);
+       fprintf(base_yyout, "%s/* exec sql end declare section */", @3);
        output_line_number();
    }
    ;
@@ -569,41 +516,17 @@ sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';'
    ;
 
 var_type_declarations: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | vt_declarations
-   {
-       $$ = $1;
-   }
    ;
 
 vt_declarations: single_vt_declaration
-   {
-       $$ = $1;
-   }
    | CPP_LINE
-   {
-       $$ = $1;
-   }
    | vt_declarations single_vt_declaration
-   {
-       $$ = cat2_str($1, $2);
-   }
    | vt_declarations CPP_LINE
-   {
-       $$ = cat2_str($1, $2);
-   }
    ;
 
 variable_declarations: var_declaration
-   {
-       $$ = $1;
-   }
    | variable_declarations var_declaration
-   {
-       $$ = cat2_str($1, $2);
-   }
    ;
 
 type_declaration: S_TYPEDEF
@@ -614,18 +537,18 @@ type_declaration: S_TYPEDEF
    }
    var_type    opt_pointer ECPGColLabel opt_array_bounds ';'
    {
-       add_typedef($5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *$4 ? 1 : 0);
+       add_typedef(@5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *@4 ? 1 : 0);
 
-       fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *$4 ? "*" : "", $5, $6.str);
+       fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *@4 ? "*" : "", @5, $6.str);
        output_line_number();
-       $$ = mm_strdup("");
+       @$ = EMPTY;
    }
    ;
 
 var_declaration:
    storage_declaration var_type
    {
-       actual_type[struct_level].type_storage = $1;
+       actual_type[struct_level].type_storage = @1;
        actual_type[struct_level].type_enum = $2.type_enum;
        actual_type[struct_level].type_str = $2.type_str;
        actual_type[struct_level].type_dimension = $2.type_dimension;
@@ -636,7 +559,7 @@ var_declaration:
    }
    variable_list ';'
    {
-       $$ = cat_str(5, actual_startline[struct_level], $1, $2.type_str, $4, mm_strdup(";\n"));
+       @$ = cat_str(5, actual_startline[struct_level], @1, $2.type_str, @4, mm_strdup(";\n"));
    }
    | var_type
    {
@@ -651,46 +574,31 @@ var_declaration:
    }
    variable_list ';'
    {
-       $$ = cat_str(4, actual_startline[struct_level], $1.type_str, $3, mm_strdup(";\n"));
+       @$ = cat_str(4, actual_startline[struct_level], $1.type_str, @3, mm_strdup(";\n"));
    }
    | struct_union_type_with_symbol ';'
    {
-       $$ = cat2_str($1, mm_strdup(";"));
+       @$ = cat2_str(@1, mm_strdup(";"));
    }
    ;
 
 opt_bit_field: ':' Iconst
-   {
-       $$ = cat2_str(mm_strdup(":"), $2);
-   }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 storage_declaration: storage_clause storage_modifier
-   {
-       $$ = cat2_str($1, $2);
-   }
    | storage_clause
-   {
-       $$ = $1;
-   }
    | storage_modifier
-   {
-       $$ = $1;
-   }
    ;
 
-storage_clause: S_EXTERN               { $$ = mm_strdup("extern"); }
-   | S_STATIC                          { $$ = mm_strdup("static"); }
-   | S_REGISTER                        { $$ = mm_strdup("register"); }
-   | S_AUTO                            { $$ = mm_strdup("auto"); }
+storage_clause: S_EXTERN
+   | S_STATIC
+   | S_REGISTER
+   | S_AUTO
    ;
 
-storage_modifier: S_CONST              { $$ = mm_strdup("const"); }
-   | S_VOLATILE                        { $$ = mm_strdup("volatile"); }
+storage_modifier: S_CONST
+   | S_VOLATILE
    ;
 
 var_type: simple_type
@@ -703,11 +611,11 @@ var_type: simple_type
    }
    | struct_union_type
    {
-       $$.type_str = $1;
+       $$.type_str = @1;
        $$.type_dimension = mm_strdup("-1");
        $$.type_index = mm_strdup("-1");
 
-       if (strncmp($1, "struct", sizeof("struct") - 1) == 0)
+       if (strncmp(@1, "struct", sizeof("struct") - 1) == 0)
        {
            $$.type_enum = ECPGt_struct;
            $$.type_sizeof = ECPGstruct_sizeof;
@@ -720,7 +628,7 @@ var_type: simple_type
    }
    | enum_type
    {
-       $$.type_str = $1;
+       $$.type_str = @1;
        $$.type_enum = ECPGt_int;
        $$.type_dimension = mm_strdup("-1");
        $$.type_index = mm_strdup("-1");
@@ -749,12 +657,12 @@ var_type: simple_type
         * will show up here as a plain identifier, and we need this duplicate
         * code to recognize them.
         */
-       if (strcmp($1, "numeric") == 0)
+       if (strcmp(@1, "numeric") == 0)
        {
            $$.type_enum = ECPGt_numeric;
            $$.type_str = mm_strdup("numeric");
        }
-       else if (strcmp($1, "decimal") == 0)
+       else if (strcmp(@1, "decimal") == 0)
        {
            $$.type_enum = ECPGt_decimal;
            $$.type_str = mm_strdup("decimal");
@@ -858,10 +766,10 @@ var_type: simple_type
         * here, but not above because those are not currently SQL keywords.
         * If they ever become so, they must gain duplicate productions above.
         */
-       if (strlen($2) != 0 && strcmp($1, "datetime") != 0 && strcmp($1, "interval") != 0)
+       if (strlen(@2) != 0 && strcmp(@1, "datetime") != 0 && strcmp(@1, "interval") != 0)
            mmerror(PARSE_ERROR, ET_ERROR, "interval specification not allowed here");
 
-       if (strcmp($1, "varchar") == 0)
+       if (strcmp(@1, "varchar") == 0)
        {
            $$.type_enum = ECPGt_varchar;
            $$.type_str = EMPTY;    /* mm_strdup("varchar"); */
@@ -869,7 +777,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "bytea") == 0)
+       else if (strcmp(@1, "bytea") == 0)
        {
            $$.type_enum = ECPGt_bytea;
            $$.type_str = EMPTY;
@@ -877,7 +785,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "float") == 0)
+       else if (strcmp(@1, "float") == 0)
        {
            $$.type_enum = ECPGt_float;
            $$.type_str = mm_strdup("float");
@@ -885,7 +793,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "double") == 0)
+       else if (strcmp(@1, "double") == 0)
        {
            $$.type_enum = ECPGt_double;
            $$.type_str = mm_strdup("double");
@@ -893,7 +801,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "numeric") == 0)
+       else if (strcmp(@1, "numeric") == 0)
        {
            $$.type_enum = ECPGt_numeric;
            $$.type_str = mm_strdup("numeric");
@@ -901,7 +809,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "decimal") == 0)
+       else if (strcmp(@1, "decimal") == 0)
        {
            $$.type_enum = ECPGt_decimal;
            $$.type_str = mm_strdup("decimal");
@@ -909,7 +817,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "date") == 0)
+       else if (strcmp(@1, "date") == 0)
        {
            $$.type_enum = ECPGt_date;
            $$.type_str = mm_strdup("date");
@@ -917,7 +825,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "timestamp") == 0)
+       else if (strcmp(@1, "timestamp") == 0)
        {
            $$.type_enum = ECPGt_timestamp;
            $$.type_str = mm_strdup("timestamp");
@@ -925,7 +833,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "interval") == 0)
+       else if (strcmp(@1, "interval") == 0)
        {
            $$.type_enum = ECPGt_interval;
            $$.type_str = mm_strdup("interval");
@@ -933,7 +841,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if (strcmp($1, "datetime") == 0)
+       else if (strcmp(@1, "datetime") == 0)
        {
            $$.type_enum = ECPGt_timestamp;
            $$.type_str = mm_strdup("timestamp");
@@ -941,7 +849,7 @@ var_type: simple_type
            $$.type_index = mm_strdup("-1");
            $$.type_sizeof = NULL;
        }
-       else if ((strcmp($1, "string") == 0) && INFORMIX_MODE)
+       else if ((strcmp(@1, "string") == 0) && INFORMIX_MODE)
        {
            $$.type_enum = ECPGt_string;
            $$.type_str = mm_strdup("char");
@@ -952,7 +860,7 @@ var_type: simple_type
        else
        {
            /* Otherwise, it must be a user-defined typedef name */
-           struct typedefs *this = get_typedef($1, false);
+           struct typedefs *this = get_typedef(@1, false);
 
            $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
            $$.type_enum = this->type->type_enum;
@@ -1001,23 +909,11 @@ var_type: simple_type
    ;
 
 enum_type: ENUM_P symbol enum_definition
-   {
-       $$ = cat_str(3, mm_strdup("enum"), $2, $3);
-   }
    | ENUM_P enum_definition
-   {
-       $$ = cat2_str(mm_strdup("enum"), $2);
-   }
    | ENUM_P symbol
-   {
-       $$ = cat2_str(mm_strdup("enum"), $2);
-   }
    ;
 
 enum_definition: '{' c_list '}'
-   {
-       $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}"));
-   }
    ;
 
 struct_union_type_with_symbol: s_struct_union_symbol
@@ -1071,14 +967,11 @@ struct_union_type_with_symbol: s_struct_union_symbol
        this->struct_member_list = struct_member_list[struct_level];
 
        types = this;
-       $$ = cat_str(4, su_type.type_str, mm_strdup("{"), $4, mm_strdup("}"));
+       @$ = cat_str(4, su_type.type_str, mm_strdup("{"), @4, mm_strdup("}"));
    }
    ;
 
 struct_union_type: struct_union_type_with_symbol
-   {
-       $$ = $1;
-   }
    | s_struct_union
    {
        struct_member_list[struct_level++] = NULL;
@@ -1090,20 +983,20 @@ struct_union_type: struct_union_type_with_symbol
        ECPGfree_struct_member(struct_member_list[struct_level]);
        struct_member_list[struct_level] = NULL;
        struct_level--;
-       $$ = cat_str(4, $1, mm_strdup("{"), $4, mm_strdup("}"));
+       @$ = cat_str(4, @1, mm_strdup("{"), @4, mm_strdup("}"));
    }
    ;
 
 s_struct_union_symbol: SQL_STRUCT symbol
    {
        $$.su = mm_strdup("struct");
-       $$.symbol = $2;
+       $$.symbol = @2;
        ECPGstruct_sizeof = cat_str(3, mm_strdup("sizeof("), cat2_str(mm_strdup($$.su), mm_strdup($$.symbol)), mm_strdup(")"));
    }
    | UNION symbol
    {
        $$.su = mm_strdup("union");
-       $$.symbol = $2;
+       $$.symbol = @2;
    }
    ;
 
@@ -1111,15 +1004,15 @@ s_struct_union: SQL_STRUCT
    {
        ECPGstruct_sizeof = mm_strdup("");  /* This must not be NULL to
                                             * distinguish from simple types. */
-       $$ = mm_strdup("struct");
+       @$ = mm_strdup("struct");
    }
    | UNION
    {
-       $$ = mm_strdup("union");
+       @$ = mm_strdup("union");
    }
    ;
 
-simple_type: unsigned_type             { $$ = $1; }
+simple_type: unsigned_type
    | opt_signed signed_type            { $$ = $2; }
    ;
 
@@ -1151,15 +1044,12 @@ opt_signed: SQL_SIGNED
    ;
 
 variable_list: variable
-   {
-       $$ = $1;
-   }
    | variable_list ',' variable
    {
        if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
-           $$ = cat_str(4, $1, mm_strdup(";"), mm_strdup(actual_type[struct_level].type_storage), $3);
+           @$ = cat_str(4, @1, mm_strdup(";"), mm_strdup(actual_type[struct_level].type_storage), @3);
        else
-           $$ = cat_str(3, $1, mm_strdup(","), $3);
+           @$ = cat_str(3, @1, mm_strdup(","), @3);
    }
    ;
 
@@ -1173,7 +1063,7 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
        int        *varlen_type_counter;
        char       *struct_name;
 
-       adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1), false);
+       adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen(@1), false);
        switch (actual_type[struct_level].type_enum)
        {
            case ECPGt_struct:
@@ -1183,7 +1073,7 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                else
                    type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum, actual_type[struct_level].type_str, actual_type[struct_level].type_sizeof), dimension);
 
-               $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
+               @$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
                break;
 
            case ECPGt_varchar:
@@ -1222,9 +1112,9 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
                sprintf(vcn, "%d", *varlen_type_counter);
                if (strcmp(dimension, "0") == 0)
-                   $$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
+                   @$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup(@2), @4, @5);
                else
-                   $$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
+                   @$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup(@2), dim_str, @4, @5);
                (*varlen_type_counter)++;
                break;
 
@@ -1233,7 +1123,7 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
            case ECPGt_string:
                if (atoi(dimension) == -1)
                {
-                   int         i = strlen($5);
+                   int         i = strlen(@5);
 
                    if (atoi(length) == -1 && i > 0)    /* char <var>[] =
                                                         * "string" */
@@ -1244,14 +1134,14 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                         */
                        free(length);
                        length = mm_alloc(i + sizeof("sizeof()"));
-                       sprintf(length, "sizeof(%s)", $5 + 2);
+                       sprintf(length, "sizeof(%s)", @5 + 2);
                    }
                    type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0);
                }
                else
                    type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0), dimension);
 
-               $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
+               @$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
                break;
 
            default:
@@ -1260,41 +1150,29 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                else
                    type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, mm_strdup("1"), 0), dimension);
 
-               $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
+               @$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
                break;
        }
 
        if (struct_level == 0)
-           new_variable($2, type, braces_open);
+           new_variable(@2, type, braces_open);
        else
-           ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
-
-       free($2);
+           ECPGmake_struct_member(@2, type, &(struct_member_list[struct_level - 1]));
    }
    ;
 
 opt_initializer: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | '=' c_term
    {
        initializer = 1;
-       $$ = cat2_str(mm_strdup("="), $2);
    }
    ;
 
 opt_pointer: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | '*'
-   {
-       $$ = mm_strdup("*");
-   }
    | '*' '*'
    {
-       $$ = mm_strdup("**");
+       @$ = mm_strdup("**");
    }
    ;
 
@@ -1304,7 +1182,7 @@ opt_pointer: /* EMPTY */
 ECPGDeclare: DECLARE STATEMENT ecpg_ident
    {
        /* this is only supported for compatibility */
-       $$ = cat_str(3, mm_strdup("/* declare statement"), $3, mm_strdup("*/"));
+       @$ = cat_str(3, mm_strdup("/* declare statement"), @3, mm_strdup("*/"));
    }
    ;
 /*
@@ -1312,49 +1190,40 @@ ECPGDeclare: DECLARE STATEMENT ecpg_ident
  */
 ECPGDisconnect: SQL_DISCONNECT dis_name
    {
-       $$ = $2;
+       @$ = @2;
    }
    ;
 
 dis_name: connection_object
-   {
-       $$ = $1;
-   }
    | CURRENT_P
    {
-       $$ = mm_strdup("\"CURRENT\"");
+       @$ = mm_strdup("\"CURRENT\"");
    }
    | ALL
    {
-       $$ = mm_strdup("\"ALL\"");
+       @$ = mm_strdup("\"ALL\"");
    }
    | /* EMPTY */
    {
-       $$ = mm_strdup("\"CURRENT\"");
+       @$ = mm_strdup("\"CURRENT\"");
    }
    ;
 
 connection_object: name
    {
-       $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+       @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
    }
    | DEFAULT
    {
-       $$ = mm_strdup("\"DEFAULT\"");
+       @$ = mm_strdup("\"DEFAULT\"");
    }
    | char_variable
-   {
-       $$ = $1;
-   }
    ;
 
 execstring: char_variable
-   {
-       $$ = $1;
-   }
    | CSTRING
    {
-       $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+       @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
    }
    ;
 
@@ -1364,11 +1233,11 @@ execstring: char_variable
  */
 ECPGFree: SQL_FREE cursor_name
    {
-       $$ = $2;
+       @$ = @2;
    }
    | SQL_FREE ALL
    {
-       $$ = mm_strdup("all");
+       @$ = mm_strdup("all");
    }
    ;
 
@@ -1377,60 +1246,51 @@ ECPGFree: SQL_FREE cursor_name
  */
 ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
    {
-       if ($2[0] == ':')
-           remove_variable_from_list(&argsinsert, find_variable($2 + 1));
-       $$ = $2;
+       if (@2[0] == ':')
+           remove_variable_from_list(&argsinsert, find_variable(@2 + 1));
+       @$ = @2;
    }
    ;
 
 opt_ecpg_using: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | ecpg_using
-   {
-       $$ = $1;
-   }
    ;
 
 ecpg_using: USING using_list
    {
-       $$ = EMPTY;
+       @$ = EMPTY;
    }
    | using_descriptor
-   {
-       $$ = $1;
-   }
    ;
 
 using_descriptor: USING SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
    {
-       add_variable_to_head(&argsinsert, descriptor_variable($4, 0), &no_indicator);
-       $$ = EMPTY;
+       add_variable_to_head(&argsinsert, descriptor_variable(@4, 0), &no_indicator);
+       @$ = EMPTY;
    }
    | USING SQL_DESCRIPTOR name
    {
-       add_variable_to_head(&argsinsert, sqlda_variable($3), &no_indicator);
-       $$ = EMPTY;
+       add_variable_to_head(&argsinsert, sqlda_variable(@3), &no_indicator);
+       @$ = EMPTY;
    }
    ;
 
 into_descriptor: INTO SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
    {
-       add_variable_to_head(&argsresult, descriptor_variable($4, 1), &no_indicator);
-       $$ = EMPTY;
+       add_variable_to_head(&argsresult, descriptor_variable(@4, 1), &no_indicator);
+       @$ = EMPTY;
    }
    | INTO SQL_DESCRIPTOR name
    {
-       add_variable_to_head(&argsresult, sqlda_variable($3), &no_indicator);
-       $$ = EMPTY;
+       add_variable_to_head(&argsresult, sqlda_variable(@3), &no_indicator);
+       @$ = EMPTY;
    }
    ;
 
 into_sqlda: INTO name
    {
-       add_variable_to_head(&argsresult, sqlda_variable($2), &no_indicator);
-       $$ = EMPTY;
+       add_variable_to_head(&argsresult, sqlda_variable(@2), &no_indicator);
+       @$ = EMPTY;
    }
    ;
 
@@ -1441,55 +1301,28 @@ UsingValue: UsingConst
    {
        char       *length = mm_alloc(32);
 
-       sprintf(length, "%zu", strlen($1));
-       add_variable_to_head(&argsinsert, new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
+       sprintf(length, "%zu", strlen(@1));
+       add_variable_to_head(&argsinsert, new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
    }
    | civar
    {
-       $$ = EMPTY;
+       @$ = EMPTY;
    }
    | civarind
    {
-       $$ = EMPTY;
+       @$ = EMPTY;
    }
    ;
 
 UsingConst: Iconst
-   {
-       $$ = $1;
-   }
    | '+' Iconst
-   {
-       $$ = cat_str(2, mm_strdup("+"), $2);
-   }
    | '-' Iconst
-   {
-       $$ = cat_str(2, mm_strdup("-"), $2);
-   }
    | ecpg_fconst
-   {
-       $$ = $1;
-   }
    | '+' ecpg_fconst
-   {
-       $$ = cat_str(2, mm_strdup("+"), $2);
-   }
    | '-' ecpg_fconst
-   {
-       $$ = cat_str(2, mm_strdup("-"), $2);
-   }
    | ecpg_sconst
-   {
-       $$ = $1;
-   }
    | ecpg_bconst
-   {
-       $$ = $1;
-   }
    | ecpg_xconst
-   {
-       $$ = $1;
-   }
    ;
 
 /*
@@ -1498,7 +1331,7 @@ UsingConst: Iconst
 ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
    {
        $$.input = 1;
-       $$.stmt_name = $3;
+       $$.stmt_name = @3;
    }
    | SQL_DESCRIBE opt_output prepared_name using_descriptor
    {
@@ -1509,33 +1342,27 @@ ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
        add_variable_to_head(&argsresult, var, &no_indicator);
 
        $$.input = 0;
-       $$.stmt_name = $3;
+       $$.stmt_name = @3;
    }
    | SQL_DESCRIBE opt_output prepared_name into_descriptor
    {
        $$.input = 0;
-       $$.stmt_name = $3;
+       $$.stmt_name = @3;
    }
    | SQL_DESCRIBE INPUT_P prepared_name into_sqlda
    {
        $$.input = 1;
-       $$.stmt_name = $3;
+       $$.stmt_name = @3;
    }
    | SQL_DESCRIBE opt_output prepared_name into_sqlda
    {
        $$.input = 0;
-       $$.stmt_name = $3;
+       $$.stmt_name = @3;
    }
    ;
 
 opt_output: SQL_OUTPUT
-   {
-       $$ = mm_strdup("output");
-   }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 /*
@@ -1549,8 +1376,8 @@ opt_output: SQL_OUTPUT
  */
 ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
    {
-       add_descriptor($3, connection);
-       $$ = $3;
+       add_descriptor(@3, connection);
+       @$ = @3;
    }
    ;
 
@@ -1560,8 +1387,8 @@ ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
  */
 ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
    {
-       drop_descriptor($3, connection);
-       $$ = $3;
+       drop_descriptor(@3, connection);
+       @$ = @3;
    }
    ;
 
@@ -1571,7 +1398,7 @@ ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
 
 ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar ECPGGetDescHeaderItems
    {
-       $$ = $3;
+       @$ = @3;
    }
    ;
 
@@ -1581,13 +1408,13 @@ ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
 
 ECPGGetDescHeaderItem: cvariable '=' desc_header_item
    {
-       push_assignment($1, $3);
+       push_assignment(@1, $3);
    }
    ;
 
 ECPGSetDescriptorHeader: SET SQL_DESCRIPTOR quoted_ident_stringvar ECPGSetDescHeaderItems
    {
-       $$ = $3;
+       @$ = @3;
    }
    ;
 
@@ -1597,7 +1424,7 @@ ECPGSetDescHeaderItems: ECPGSetDescHeaderItem
 
 ECPGSetDescHeaderItem: desc_header_item '=' IntConstVar
    {
-       push_assignment($3, $1);
+       push_assignment(@3, $1);
    }
    ;
 
@@ -1605,14 +1432,10 @@ IntConstVar: Iconst
    {
        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
 
-       sprintf(length, "%zu", strlen($1));
-       new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-       $$ = $1;
+       sprintf(length, "%zu", strlen(@1));
+       new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
    }
    | cvariable
-   {
-       $$ = $1;
-   }
    ;
 
 desc_header_item: SQL_COUNT
@@ -1627,8 +1450,8 @@ desc_header_item: SQL_COUNT
 
 ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGGetDescItems
    {
-       $$.str = $5;
-       $$.name = $3;
+       $$.str = @5;
+       $$.name = @3;
    }
    ;
 
@@ -1638,14 +1461,14 @@ ECPGGetDescItems: ECPGGetDescItem
 
 ECPGGetDescItem: cvariable '=' descriptor_item
    {
-       push_assignment($1, $3);
+       push_assignment(@1, $3);
    }
    ;
 
 ECPGSetDescriptor: SET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGSetDescItems
    {
-       $$.str = $5;
-       $$.name = $3;
+       $$.str = @5;
+       $$.name = @3;
    }
    ;
 
@@ -1655,7 +1478,7 @@ ECPGSetDescItems: ECPGSetDescItem
 
 ECPGSetDescItem: descriptor_item '=' AllConstVar
    {
-       push_assignment($3, $1);
+       push_assignment(@3, $1);
    }
    ;
 
@@ -1663,41 +1486,37 @@ AllConstVar: ecpg_fconst
    {
        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
 
-       sprintf(length, "%zu", strlen($1));
-       new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-       $$ = $1;
+       sprintf(length, "%zu", strlen(@1));
+       new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
    }
    | IntConstVar
-   {
-       $$ = $1;
-   }
    | '-' ecpg_fconst
    {
        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-       char       *var = cat2_str(mm_strdup("-"), $2);
+       char       *var = cat2_str(mm_strdup("-"), @2);
 
        sprintf(length, "%zu", strlen(var));
        new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-       $$ = var;
+       @$ = var;
    }
    | '-' Iconst
    {
        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-       char       *var = cat2_str(mm_strdup("-"), $2);
+       char       *var = cat2_str(mm_strdup("-"), @2);
 
        sprintf(length, "%zu", strlen(var));
        new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-       $$ = var;
+       @$ = var;
    }
    | ecpg_sconst
    {
        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-       char       *var = $1 + 1;
+       char       *var = @1 + 1;
 
        var[strlen(var) - 1] = '\0';
        sprintf(length, "%zu", strlen(var));
        new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-       $$ = var;
+       @$ = var;
    }
    ;
 
@@ -1724,22 +1543,16 @@ descriptor_item: SQL_CARDINALITY        { $$ = ECPGd_cardinality; }
  */
 ECPGSetAutocommit: SET SQL_AUTOCOMMIT '=' on_off
    {
-       $$ = $4;
+       @$ = @4;
    }
    | SET SQL_AUTOCOMMIT TO on_off
    {
-       $$ = $4;
+       @$ = @4;
    }
    ;
 
 on_off: ON
-   {
-       $$ = mm_strdup("on");
-   }
    | OFF
-   {
-       $$ = mm_strdup("off");
-   }
    ;
 
 /*
@@ -1748,15 +1561,15 @@ on_off: ON
  */
 ECPGSetConnection: SET CONNECTION TO connection_object
    {
-       $$ = $4;
+       @$ = @4;
    }
    | SET CONNECTION '=' connection_object
    {
-       $$ = $4;
+       @$ = @4;
    }
    | SET CONNECTION connection_object
    {
-       $$ = $3;
+       @$ = @3;
    }
    ;
 
@@ -1771,23 +1584,17 @@ ECPGTypedef: TYPE_P
    }
    ECPGColLabel IS var_type opt_array_bounds opt_reference
    {
-       add_typedef($3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *$7 ? 1 : 0);
+       add_typedef(@3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *@7 ? 1 : 0);
 
        if (auto_create_c == false)
-           $$ = cat_str(7, mm_strdup("/* exec sql type"), mm_strdup($3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), $7, mm_strdup("*/"));
+           @$ = cat_str(7, mm_strdup("/* exec sql type"), mm_strdup(@3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), @7, mm_strdup("*/"));
        else
-           $$ = cat_str(6, mm_strdup("typedef "), mm_strdup($5.type_str), *$7 ? mm_strdup("*") : mm_strdup(""), mm_strdup($3), mm_strdup($6.str), mm_strdup(";"));
+           @$ = cat_str(6, mm_strdup("typedef "), mm_strdup($5.type_str), *@7 ? mm_strdup("*") : mm_strdup(""), mm_strdup(@3), mm_strdup($6.str), mm_strdup(";"));
    }
    ;
 
 opt_reference: SQL_REFERENCE
-   {
-       $$ = mm_strdup("reference");
-   }
    | /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    ;
 
 /*
@@ -1801,7 +1608,7 @@ ECPGVar: SQL_VAR
    }
    ColLabel    IS var_type opt_array_bounds opt_reference
    {
-       struct variable *p = find_variable($3);
+       struct variable *p = find_variable(@3);
        char       *dimension = $6.index1;
        char       *length = $6.index2;
        struct ECPGtype *type;
@@ -1812,7 +1619,7 @@ ECPGVar: SQL_VAR
            mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in EXEC SQL VAR command");
        else
        {
-           adjust_array($5.type_enum, &dimension, &length, $5.type_dimension, $5.type_index, *$7 ? 1 : 0, false);
+           adjust_array($5.type_enum, &dimension, &length, $5.type_dimension, $5.type_index, *@7 ? 1 : 0, false);
 
            switch ($5.type_enum)
            {
@@ -1856,7 +1663,7 @@ ECPGVar: SQL_VAR
            p->type = type;
        }
 
-                   $$ = cat_str(7, mm_strdup("/* exec sql var"), mm_strdup($3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), $7, mm_strdup("*/"));
+                   @$ = cat_str(7, mm_strdup("/* exec sql var"), mm_strdup(@3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), @7, mm_strdup("*/"));
    }
    ;
 
@@ -1868,19 +1675,19 @@ ECPGWhenever: SQL_WHENEVER SQL_SQLERROR action
    {
        when_error.code = $<action>3.code;
        when_error.command = $<action>3.command;
-       $$ = cat_str(3, mm_strdup("/* exec sql whenever sqlerror "), $3.str, mm_strdup("; */"));
+       @$ = cat_str(3, mm_strdup("/* exec sql whenever sqlerror "), $3.str, mm_strdup("; */"));
    }
    | SQL_WHENEVER NOT SQL_FOUND action
    {
        when_nf.code = $<action>4.code;
        when_nf.command = $<action>4.command;
-       $$ = cat_str(3, mm_strdup("/* exec sql whenever not found "), $4.str, mm_strdup("; */"));
+       @$ = cat_str(3, mm_strdup("/* exec sql whenever not found "), $4.str, mm_strdup("; */"));
    }
    | SQL_WHENEVER SQL_SQLWARNING action
    {
        when_warn.code = $<action>3.code;
        when_warn.command = $<action>3.command;
-       $$ = cat_str(3, mm_strdup("/* exec sql whenever sql_warning "), $3.str, mm_strdup("; */"));
+       @$ = cat_str(3, mm_strdup("/* exec sql whenever sql_warning "), $3.str, mm_strdup("; */"));
    }
    ;
 
@@ -1905,19 +1712,19 @@ action: CONTINUE_P
    | SQL_GOTO name
    {
        $<action>$.code = W_GOTO;
-       $<action>$.command = mm_strdup($2);
-       $<action>$.str = cat2_str(mm_strdup("goto "), $2);
+       $<action>$.command = mm_strdup(@2);
+       $<action>$.str = cat2_str(mm_strdup("goto "), @2);
    }
    | SQL_GO TO name
    {
        $<action>$.code = W_GOTO;
-       $<action>$.command = mm_strdup($3);
-       $<action>$.str = cat2_str(mm_strdup("goto "), $3);
+       $<action>$.command = mm_strdup(@3);
+       $<action>$.str = cat2_str(mm_strdup("goto "), @3);
    }
    | DO name '(' c_args ')'
    {
        $<action>$.code = W_DO;
-       $<action>$.command = cat_str(4, $2, mm_strdup("("), $4, mm_strdup(")"));
+       $<action>$.command = cat_str(4, @2, mm_strdup("("), @4, mm_strdup(")"));
        $<action>$.str = cat2_str(mm_strdup("do"), mm_strdup($<action>$.command));
    }
    | DO SQL_BREAK
@@ -1935,13 +1742,13 @@ action: CONTINUE_P
    | CALL name '(' c_args ')'
    {
        $<action>$.code = W_DO;
-       $<action>$.command = cat_str(4, $2, mm_strdup("("), $4, mm_strdup(")"));
+       $<action>$.command = cat_str(4, @2, mm_strdup("("), @4, mm_strdup(")"));
        $<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
    }
    | CALL name
    {
        $<action>$.code = W_DO;
-       $<action>$.command = cat2_str($2, mm_strdup("()"));
+       $<action>$.command = cat2_str(@2, mm_strdup("()"));
        $<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
    }
    ;
@@ -1949,63 +1756,63 @@ action: CONTINUE_P
 /* some other stuff for ecpg */
 
 /* additional unreserved keywords */
-ECPGKeywords: ECPGKeywords_vanames     { $$ = $1; }
-   | ECPGKeywords_rest                 { $$ = $1; }
-   ;
-
-ECPGKeywords_vanames: SQL_BREAK            { $$ = mm_strdup("break"); }
-   | SQL_CARDINALITY                   { $$ = mm_strdup("cardinality"); }
-   | SQL_COUNT                         { $$ = mm_strdup("count"); }
-   | SQL_DATETIME_INTERVAL_CODE        { $$ = mm_strdup("datetime_interval_code"); }
-   | SQL_DATETIME_INTERVAL_PRECISION   { $$ = mm_strdup("datetime_interval_precision"); }
-   | SQL_FOUND                         { $$ = mm_strdup("found"); }
-   | SQL_GO                            { $$ = mm_strdup("go"); }
-   | SQL_GOTO                          { $$ = mm_strdup("goto"); }
-   | SQL_IDENTIFIED                    { $$ = mm_strdup("identified"); }
-   | SQL_INDICATOR                     { $$ = mm_strdup("indicator"); }
-   | SQL_KEY_MEMBER                    { $$ = mm_strdup("key_member"); }
-   | SQL_LENGTH                        { $$ = mm_strdup("length"); }
-   | SQL_NULLABLE                      { $$ = mm_strdup("nullable"); }
-   | SQL_OCTET_LENGTH                  { $$ = mm_strdup("octet_length"); }
-   | SQL_RETURNED_LENGTH               { $$ = mm_strdup("returned_length"); }
-   | SQL_RETURNED_OCTET_LENGTH         { $$ = mm_strdup("returned_octet_length"); }
-   | SQL_SCALE                         { $$ = mm_strdup("scale"); }
-   | SQL_SECTION                       { $$ = mm_strdup("section"); }
-   | SQL_SQLERROR                      { $$ = mm_strdup("sqlerror"); }
-   | SQL_SQLPRINT                      { $$ = mm_strdup("sqlprint"); }
-   | SQL_SQLWARNING                    { $$ = mm_strdup("sqlwarning"); }
-   | SQL_STOP                          { $$ = mm_strdup("stop"); }
-   ;
-
-ECPGKeywords_rest: SQL_CONNECT         { $$ = mm_strdup("connect"); }
-   | SQL_DESCRIBE                      { $$ = mm_strdup("describe"); }
-   | SQL_DISCONNECT                    { $$ = mm_strdup("disconnect"); }
-   | SQL_OPEN                          { $$ = mm_strdup("open"); }
-   | SQL_VAR                           { $$ = mm_strdup("var"); }
-   | SQL_WHENEVER                      { $$ = mm_strdup("whenever"); }
+ECPGKeywords: ECPGKeywords_vanames
+   | ECPGKeywords_rest
+   ;
+
+ECPGKeywords_vanames: SQL_BREAK
+   | SQL_CARDINALITY
+   | SQL_COUNT
+   | SQL_DATETIME_INTERVAL_CODE
+   | SQL_DATETIME_INTERVAL_PRECISION
+   | SQL_FOUND
+   | SQL_GO
+   | SQL_GOTO
+   | SQL_IDENTIFIED
+   | SQL_INDICATOR
+   | SQL_KEY_MEMBER
+   | SQL_LENGTH
+   | SQL_NULLABLE
+   | SQL_OCTET_LENGTH
+   | SQL_RETURNED_LENGTH
+   | SQL_RETURNED_OCTET_LENGTH
+   | SQL_SCALE
+   | SQL_SECTION
+   | SQL_SQLERROR
+   | SQL_SQLPRINT
+   | SQL_SQLWARNING
+   | SQL_STOP
+   ;
+
+ECPGKeywords_rest: SQL_CONNECT
+   | SQL_DESCRIBE
+   | SQL_DISCONNECT
+   | SQL_OPEN
+   | SQL_VAR
+   | SQL_WHENEVER
    ;
 
 /* additional keywords that can be SQL type names (but not ECPGColLabels) */
-ECPGTypeName: SQL_BOOL                 { $$ = mm_strdup("bool"); }
-   | SQL_LONG                          { $$ = mm_strdup("long"); }
-   | SQL_OUTPUT                        { $$ = mm_strdup("output"); }
-   | SQL_SHORT                         { $$ = mm_strdup("short"); }
-   | SQL_STRUCT                        { $$ = mm_strdup("struct"); }
-   | SQL_SIGNED                        { $$ = mm_strdup("signed"); }
-   | SQL_UNSIGNED                      { $$ = mm_strdup("unsigned"); }
+ECPGTypeName: SQL_BOOL
+   | SQL_LONG
+   | SQL_OUTPUT
+   | SQL_SHORT
+   | SQL_STRUCT
+   | SQL_SIGNED
+   | SQL_UNSIGNED
    ;
 
-symbol: ColLabel                       { $$ = $1; }
+symbol: ColLabel
    ;
 
-ECPGColId: ecpg_ident                  { $$ = $1; }
-   | unreserved_keyword                { $$ = $1; }
-   | col_name_keyword                  { $$ = $1; }
-   | ECPGunreserved_interval           { $$ = $1; }
-   | ECPGKeywords                      { $$ = $1; }
-   | ECPGCKeywords                     { $$ = $1; }
-   | CHAR_P                            { $$ = mm_strdup("char"); }
-   | VALUES                            { $$ = mm_strdup("values"); }
+ECPGColId: ecpg_ident
+   | unreserved_keyword
+   | col_name_keyword
+   | ECPGunreserved_interval
+   | ECPGKeywords
+   | ECPGCKeywords
+   | CHAR_P
+   | VALUES
    ;
 
 /*
@@ -2018,58 +1825,58 @@ ECPGColId: ecpg_ident                   { $$ = $1; }
 
 /* Column identifier --- names that can be column, table, etc names.
  */
-ColId: ecpg_ident                      { $$ = $1; }
-   | all_unreserved_keyword            { $$ = $1; }
-   | col_name_keyword                  { $$ = $1; }
-   | ECPGKeywords                      { $$ = $1; }
-   | ECPGCKeywords                     { $$ = $1; }
-   | CHAR_P                            { $$ = mm_strdup("char"); }
-   | VALUES                            { $$ = mm_strdup("values"); }
+ColId: ecpg_ident
+   | all_unreserved_keyword
+   | col_name_keyword
+   | ECPGKeywords
+   | ECPGCKeywords
+   | CHAR_P
+   | VALUES
    ;
 
 /* Type/function identifier --- names that can be type or function names.
  */
-type_function_name: ecpg_ident         { $$ = $1; }
-   | all_unreserved_keyword            { $$ = $1; }
-   | type_func_name_keyword            { $$ = $1; }
-   | ECPGKeywords                      { $$ = $1; }
-   | ECPGCKeywords                     { $$ = $1; }
-   | ECPGTypeName                      { $$ = $1; }
+type_function_name: ecpg_ident
+   | all_unreserved_keyword
+   | type_func_name_keyword
+   | ECPGKeywords
+   | ECPGCKeywords
+   | ECPGTypeName
    ;
 
 /* Column label --- allowed labels in "AS" clauses.
  * This presently includes *all* Postgres keywords.
  */
-ColLabel: ECPGColLabel                 { $$ = $1; }
-   | ECPGTypeName                      { $$ = $1; }
-   | CHAR_P                            { $$ = mm_strdup("char"); }
-   | CURRENT_P                         { $$ = mm_strdup("current"); }
-   | INPUT_P                           { $$ = mm_strdup("input"); }
-   | INT_P                             { $$ = mm_strdup("int"); }
-   | TO                                { $$ = mm_strdup("to"); }
-   | UNION                             { $$ = mm_strdup("union"); }
-   | VALUES                            { $$ = mm_strdup("values"); }
-   | ECPGCKeywords                     { $$ = $1; }
-   | ECPGunreserved_interval           { $$ = $1; }
-   ;
-
-ECPGColLabel: ecpg_ident               { $$ = $1; }
-   | unreserved_keyword                { $$ = $1; }
-   | col_name_keyword                  { $$ = $1; }
-   | type_func_name_keyword            { $$ = $1; }
-   | reserved_keyword                  { $$ = $1; }
-   | ECPGKeywords_vanames              { $$ = $1; }
-   | ECPGKeywords_rest                 { $$ = $1; }
-   | CONNECTION                        { $$ = mm_strdup("connection"); }
-   ;
-
-ECPGCKeywords: S_AUTO                  { $$ = mm_strdup("auto"); }
-   | S_CONST                           { $$ = mm_strdup("const"); }
-   | S_EXTERN                          { $$ = mm_strdup("extern"); }
-   | S_REGISTER                        { $$ = mm_strdup("register"); }
-   | S_STATIC                          { $$ = mm_strdup("static"); }
-   | S_TYPEDEF                         { $$ = mm_strdup("typedef"); }
-   | S_VOLATILE                        { $$ = mm_strdup("volatile"); }
+ColLabel: ECPGColLabel
+   | ECPGTypeName
+   | CHAR_P
+   | CURRENT_P
+   | INPUT_P
+   | INT_P
+   | TO
+   | UNION
+   | VALUES
+   | ECPGCKeywords
+   | ECPGunreserved_interval
+   ;
+
+ECPGColLabel: ecpg_ident
+   | unreserved_keyword
+   | col_name_keyword
+   | type_func_name_keyword
+   | reserved_keyword
+   | ECPGKeywords_vanames
+   | ECPGKeywords_rest
+   | CONNECTION
+   ;
+
+ECPGCKeywords: S_AUTO
+   | S_CONST
+   | S_EXTERN
+   | S_REGISTER
+   | S_STATIC
+   | S_TYPEDEF
+   | S_VOLATILE
    ;
 
 /* "Unreserved" keywords --- available for use as any kind of name.
@@ -2086,17 +1893,17 @@ ECPGCKeywords: S_AUTO                   { $$ = mm_strdup("auto"); }
  *
  * The mentioned exclusions are done by $replace_line settings in parse.pl.
  */
-all_unreserved_keyword: unreserved_keyword { $$ = $1; }
-   | ECPGunreserved_interval           { $$ = $1; }
-   | CONNECTION                        { $$ = mm_strdup("connection"); }
+all_unreserved_keyword: unreserved_keyword
+   | ECPGunreserved_interval
+   | CONNECTION
    ;
 
-ECPGunreserved_interval: DAY_P         { $$ = mm_strdup("day"); }
-   | HOUR_P                            { $$ = mm_strdup("hour"); }
-   | MINUTE_P                          { $$ = mm_strdup("minute"); }
-   | MONTH_P                           { $$ = mm_strdup("month"); }
-   | SECOND_P                          { $$ = mm_strdup("second"); }
-   | YEAR_P                            { $$ = mm_strdup("year"); }
+ECPGunreserved_interval: DAY_P
+   | HOUR_P
+   | MINUTE_P
+   | MONTH_P
+   | SECOND_P
+   | YEAR_P
    ;
 
 into_list: coutputvariable | into_list ',' coutputvariable
@@ -2106,73 +1913,66 @@ ecpgstart: SQL_START
    {
        reset_variables();
        pacounter = 1;
+       @$ = EMPTY;
    }
    ;
 
 c_args: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | c_list
-   {
-       $$ = $1;
-   }
    ;
 
 coutputvariable: cvariable indicator
    {
-       add_variable_to_head(&argsresult, find_variable($1), find_variable($2));
+       add_variable_to_head(&argsresult, find_variable(@1), find_variable(@2));
    }
    | cvariable
    {
-       add_variable_to_head(&argsresult, find_variable($1), &no_indicator);
+       add_variable_to_head(&argsresult, find_variable(@1), &no_indicator);
    }
    ;
 
 
 civarind: cvariable indicator
    {
-       if (find_variable($2)->type->type == ECPGt_array)
+       if (find_variable(@2)->type->type == ECPGt_array)
            mmerror(PARSE_ERROR, ET_ERROR, "arrays of indicators are not allowed on input");
 
-       add_variable_to_head(&argsinsert, find_variable($1), find_variable($2));
-       $$ = create_questionmarks($1, false);
+       add_variable_to_head(&argsinsert, find_variable(@1), find_variable(@2));
+       @$ = create_questionmarks(@1, false);
    }
    ;
 
 char_civar: char_variable
    {
-       char       *ptr = strstr($1, ".arr");
+       char       *ptr = strstr(@1, ".arr");
 
        if (ptr)                /* varchar, we need the struct name here, not
                                 * the struct element */
            *ptr = '\0';
-       add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
-       $$ = $1;
+       add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
    }
    ;
 
 civar: cvariable
    {
-       add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
-       $$ = create_questionmarks($1, false);
+       add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
+       @$ = create_questionmarks(@1, false);
    }
    ;
 
 indicator: cvariable
    {
-       check_indicator((find_variable($1))->type);
-       $$ = $1;
+       check_indicator((find_variable(@1))->type);
    }
    | SQL_INDICATOR cvariable
    {
-       check_indicator((find_variable($2))->type);
-       $$ = $2;
+       check_indicator((find_variable(@2))->type);
+       @$ = @2;
    }
    | SQL_INDICATOR name
    {
-       check_indicator((find_variable($2))->type);
-       $$ = $2;
+       check_indicator((find_variable(@2))->type);
+       @$ = @2;
    }
    ;
 
@@ -2182,7 +1982,7 @@ cvariable: CVARIABLE
         * As long as multidimensional arrays are not implemented we have to
         * check for those here
         */
-       char       *ptr = $1;
+       char       *ptr = @1;
        int         brace_open = 0,
                    brace = false;
 
@@ -2209,57 +2009,44 @@ cvariable: CVARIABLE
                    break;
            }
        }
-       $$ = $1;
    }
    ;
 
 ecpg_param: PARAM
    {
-       $$ = make_name();
+       @$ = make_name();
    }
    ;
 
 ecpg_bconst: BCONST
-   {
-       $$ = $1;
-   }
    ;
 
 ecpg_fconst: FCONST
    {
-       $$ = make_name();
+       @$ = make_name();
    }
    ;
 
 ecpg_sconst: SCONST
-   {
-       $$ = $1;
-   }
    ;
 
 ecpg_xconst: XCONST
-   {
-       $$ = $1;
-   }
    ;
 
 ecpg_ident: IDENT
-   {
-       $$ = $1;
-   }
    | CSTRING
    {
-       $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+       @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
    }
    ;
 
 quoted_ident_stringvar: name
    {
-       $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+       @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
    }
    | char_variable
    {
-       $$ = make3_str(mm_strdup("("), $1, mm_strdup(")"));
+       @$ = make3_str(mm_strdup("("), @1, mm_strdup(")"));
    }
    ;
 
@@ -2268,221 +2055,151 @@ quoted_ident_stringvar: name
  */
 
 c_stuff_item: c_anything
-   {
-       $$ = $1;
-   }
    | '(' ')'
    {
-       $$ = mm_strdup("()");
+       @$ = mm_strdup("()");
    }
    | '(' c_stuff ')'
-   {
-       $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")"));
-   }
    ;
 
 c_stuff: c_stuff_item
-   {
-       $$ = $1;
-   }
    | c_stuff c_stuff_item
-   {
-       $$ = cat2_str($1, $2);
-   }
    ;
 
 c_list: c_term
-   {
-       $$ = $1;
-   }
    | c_list ',' c_term
-   {
-       $$ = cat_str(3, $1, mm_strdup(","), $3);
-   }
    ;
 
 c_term: c_stuff
-   {
-       $$ = $1;
-   }
    | '{' c_list '}'
-   {
-       $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}"));
-   }
-   ;
-
-c_thing: c_anything                    { $$ = $1; }
-   | '('                           { $$ = mm_strdup("("); }
-   | ')'                           { $$ = mm_strdup(")"); }
-   | ','                           { $$ = mm_strdup(","); }
-   | ';'                           { $$ = mm_strdup(";"); }
-   ;
-
-c_anything: ecpg_ident             { $$ = $1; }
-   | Iconst                        { $$ = $1; }
-   | ecpg_fconst                   { $$ = $1; }
-   | ecpg_sconst                   { $$ = $1; }
-   | '*'                           { $$ = mm_strdup("*"); }
-   | '+'                           { $$ = mm_strdup("+"); }
-   | '-'                           { $$ = mm_strdup("-"); }
-   | '/'                           { $$ = mm_strdup("/"); }
-   | '%'                           { $$ = mm_strdup("%"); }
-   | NULL_P                        { $$ = mm_strdup("NULL"); }
-   | S_ADD                         { $$ = mm_strdup("+="); }
-   | S_AND                         { $$ = mm_strdup("&&"); }
-   | S_ANYTHING                    { $$ = make_name(); }
-   | S_AUTO                        { $$ = mm_strdup("auto"); }
-   | S_CONST                       { $$ = mm_strdup("const"); }
-   | S_DEC                         { $$ = mm_strdup("--"); }
-   | S_DIV                         { $$ = mm_strdup("/="); }
-   | S_DOTPOINT                    { $$ = mm_strdup(".*"); }
-   | S_EQUAL                       { $$ = mm_strdup("=="); }
-   | S_EXTERN                      { $$ = mm_strdup("extern"); }
-   | S_INC                         { $$ = mm_strdup("++"); }
-   | S_LSHIFT                      { $$ = mm_strdup("<<"); }
-   | S_MEMBER                      { $$ = mm_strdup("->"); }
-   | S_MEMPOINT                    { $$ = mm_strdup("->*"); }
-   | S_MOD                         { $$ = mm_strdup("%="); }
-   | S_MUL                         { $$ = mm_strdup("*="); }
-   | S_NEQUAL                      { $$ = mm_strdup("!="); }
-   | S_OR                          { $$ = mm_strdup("||"); }
-   | S_REGISTER                    { $$ = mm_strdup("register"); }
-   | S_RSHIFT                      { $$ = mm_strdup(">>"); }
-   | S_STATIC                      { $$ = mm_strdup("static"); }
-   | S_SUB                         { $$ = mm_strdup("-="); }
-   | S_TYPEDEF                     { $$ = mm_strdup("typedef"); }
-   | S_VOLATILE                    { $$ = mm_strdup("volatile"); }
-   | SQL_BOOL                      { $$ = mm_strdup("bool"); }
-   | ENUM_P                        { $$ = mm_strdup("enum"); }
-   | HOUR_P                        { $$ = mm_strdup("hour"); }
-   | INT_P                         { $$ = mm_strdup("int"); }
-   | SQL_LONG                      { $$ = mm_strdup("long"); }
-   | MINUTE_P                      { $$ = mm_strdup("minute"); }
-   | MONTH_P                       { $$ = mm_strdup("month"); }
-   | SECOND_P                      { $$ = mm_strdup("second"); }
-   | SQL_SHORT                     { $$ = mm_strdup("short"); }
-   | SQL_SIGNED                    { $$ = mm_strdup("signed"); }
-   | SQL_STRUCT                    { $$ = mm_strdup("struct"); }
-   | SQL_UNSIGNED                  { $$ = mm_strdup("unsigned"); }
-   | YEAR_P                        { $$ = mm_strdup("year"); }
-   | CHAR_P                        { $$ = mm_strdup("char"); }
-   | FLOAT_P                       { $$ = mm_strdup("float"); }
-   | TO                            { $$ = mm_strdup("to"); }
-   | UNION                         { $$ = mm_strdup("union"); }
-   | VARCHAR                       { $$ = mm_strdup("varchar"); }
-   | '['                           { $$ = mm_strdup("["); }
-   | ']'                           { $$ = mm_strdup("]"); }
-   | '='                           { $$ = mm_strdup("="); }
-   | ':'                           { $$ = mm_strdup(":"); }
+   ;
+
+c_thing: c_anything
+   | '('
+   | ')'
+   | ','
+   | ';'
+   ;
+
+/*
+ * Note: NULL_P is treated specially to force it to be output in upper case,
+ * since it's likely meant as a reference to the standard C macro NULL.
+ */
+c_anything: ecpg_ident
+   | Iconst
+   | ecpg_fconst
+   | ecpg_sconst
+   | '*'
+   | '+'
+   | '-'
+   | '/'
+   | '%'
+   | NULL_P                        { @$ = mm_strdup("NULL"); }
+   | S_ADD
+   | S_AND
+   | S_ANYTHING
+   | S_AUTO
+   | S_CONST
+   | S_DEC
+   | S_DIV
+   | S_DOTPOINT
+   | S_EQUAL
+   | S_EXTERN
+   | S_INC
+   | S_LSHIFT
+   | S_MEMBER
+   | S_MEMPOINT
+   | S_MOD
+   | S_MUL
+   | S_NEQUAL
+   | S_OR
+   | S_REGISTER
+   | S_RSHIFT
+   | S_STATIC
+   | S_SUB
+   | S_TYPEDEF
+   | S_VOLATILE
+   | SQL_BOOL
+   | ENUM_P
+   | HOUR_P
+   | INT_P
+   | SQL_LONG
+   | MINUTE_P
+   | MONTH_P
+   | SECOND_P
+   | SQL_SHORT
+   | SQL_SIGNED
+   | SQL_STRUCT
+   | SQL_UNSIGNED
+   | YEAR_P
+   | CHAR_P
+   | FLOAT_P
+   | TO
+   | UNION
+   | VARCHAR
+   | '['
+   | ']'
+   | '='
+   | ':'
    ;
 
 DeallocateStmt: DEALLOCATE prepared_name
    {
-       check_declared_list($2);
-       $$ = $2;
+       check_declared_list(@2);
+       @$ = @2;
    }
    | DEALLOCATE PREPARE prepared_name
    {
-       check_declared_list($3);
-       $$ = $3;
+       check_declared_list(@3);
+       @$ = @3;
    }
    | DEALLOCATE ALL
    {
-       $$ = mm_strdup("all");
+       @$ = mm_strdup("all");
    }
    | DEALLOCATE PREPARE ALL
    {
-       $$ = mm_strdup("all");
+       @$ = mm_strdup("all");
    }
    ;
 
 Iresult: Iconst
-   {
-       $$ = $1;
-   }
    | '(' Iresult ')'
-   {
-       $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")"));
-   }
    | Iresult '+' Iresult
-   {
-       $$ = cat_str(3, $1, mm_strdup("+"), $3);
-   }
    | Iresult '-' Iresult
-   {
-       $$ = cat_str(3, $1, mm_strdup("-"), $3);
-   }
    | Iresult '*' Iresult
-   {
-       $$ = cat_str(3, $1, mm_strdup("*"), $3);
-   }
    | Iresult '/' Iresult
-   {
-       $$ = cat_str(3, $1, mm_strdup("/"), $3);
-   }
    | Iresult '%' Iresult
-   {
-       $$ = cat_str(3, $1, mm_strdup("%"), $3);
-   }
    | ecpg_sconst
-   {
-       $$ = $1;
-   }
    | ColId
-   {
-       $$ = $1;
-   }
    | ColId '(' var_type ')'
    {
-       if (pg_strcasecmp($1, "sizeof") != 0)
+       if (pg_strcasecmp(@1, "sizeof") != 0)
            mmerror(PARSE_ERROR, ET_ERROR, "operator not allowed in variable definition");
        else
-           $$ = cat_str(4, $1, mm_strdup("("), $3.type_str, mm_strdup(")"));
+           @$ = cat_str(4, @1, mm_strdup("("), $3.type_str, mm_strdup(")"));
    }
    ;
 
 execute_rest: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | ecpg_using opt_ecpg_into
-   {
-       $$ = EMPTY;
-   }
    | ecpg_into ecpg_using
-   {
-       $$ = EMPTY;
-   }
    | ecpg_into
-   {
-       $$ = EMPTY;
-   }
    ;
 
 ecpg_into: INTO into_list
    {
-       $$ = EMPTY;
+       /* always suppress this from the constructed string */
+       @$ = EMPTY;
    }
    | into_descriptor
-   {
-       $$ = $1;
-   }
    ;
 
 opt_ecpg_into: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | ecpg_into
-   {
-       $$ = $1;
-   }
    ;
 
 ecpg_fetch_into: ecpg_into
-   {
-       $$ = $1;
-   }
    | using_descriptor
    {
        struct variable *var;
@@ -2490,18 +2207,11 @@ ecpg_fetch_into: ecpg_into
        var = argsinsert->variable;
        remove_variable_from_list(&argsinsert, var);
        add_variable_to_head(&argsresult, var, &no_indicator);
-       $$ = $1;
    }
    ;
 
 opt_ecpg_fetch_into: /* EMPTY */
-   {
-       $$ = EMPTY;
-   }
    | ecpg_fetch_into
-   {
-       $$ = $1;
-   }
    ;
 
 %%
index 4fe80a5a4bea256822ea557303c522b86833f09a..2929f358ff4aed346887c8eab293ababe9e60812 100644 (file)
@@ -1,131 +1,4 @@
 /* src/interfaces/ecpg/preproc/ecpg.type */
-%type <str> ECPGAllocateDescr
-%type <str> ECPGCKeywords
-%type <str> ECPGColId
-%type <str> ECPGColLabel
-%type <str> ECPGConnect
-%type <str> ECPGCursorStmt
-%type <str> ECPGDeallocateDescr
-%type <str> ECPGDeclaration
-%type <str> ECPGDeclare
-%type <str> ECPGDeclareStmt
-%type <str> ECPGDisconnect
-%type <str> ECPGExecuteImmediateStmt
-%type <str> ECPGFree
-%type <str> ECPGGetDescHeaderItem
-%type <str> ECPGGetDescItem
-%type <str> ECPGGetDescriptorHeader
-%type <str> ECPGKeywords
-%type <str> ECPGKeywords_rest
-%type <str> ECPGKeywords_vanames
-%type <str> ECPGOpen
-%type <str> ECPGSetAutocommit
-%type <str> ECPGSetConnection
-%type <str> ECPGSetDescHeaderItem
-%type <str> ECPGSetDescItem
-%type <str> ECPGSetDescriptorHeader
-%type <str> ECPGTypeName
-%type <str> ECPGTypedef
-%type <str> ECPGVar
-%type <str> ECPGVarDeclaration
-%type <str> ECPGWhenever
-%type <str> ECPGunreserved_interval
-%type <str> UsingConst
-%type <str> UsingValue
-%type <str> all_unreserved_keyword
-%type <str> c_anything
-%type <str> c_args
-%type <str> c_list
-%type <str> c_stuff
-%type <str> c_stuff_item
-%type <str> c_term
-%type <str> c_thing
-%type <str> char_variable
-%type <str> char_civar
-%type <str> civar
-%type <str> civarind
-%type <str> ColId
-%type <str> ColLabel
-%type <str> connect_options
-%type <str> connection_object
-%type <str> connection_target
-%type <str> coutputvariable
-%type <str> cvariable
-%type <str> db_prefix
-%type <str> CreateAsStmt
-%type <str> DeallocateStmt
-%type <str> dis_name
-%type <str> ecpg_bconst
-%type <str> ecpg_fconst
-%type <str> ecpg_ident
-%type <str> ecpg_interval
-%type <str> ecpg_into
-%type <str> ecpg_fetch_into
-%type <str> ecpg_param
-%type <str> ecpg_sconst
-%type <str> ecpg_using
-%type <str> ecpg_xconst
-%type <str> enum_definition
-%type <str> enum_type
-%type <str> execstring
-%type <str> execute_rest
-%type <str> indicator
-%type <str> into_descriptor
-%type <str> into_sqlda
-%type <str> Iresult
-%type <str> on_off
-%type <str> opt_bit_field
-%type <str> opt_connection_name
-%type <str> opt_database_name
-%type <str> opt_ecpg_into
-%type <str> opt_ecpg_fetch_into
-%type <str> opt_ecpg_using
-%type <str> opt_initializer
-%type <str> opt_options
-%type <str> opt_output
-%type <str> opt_pointer
-%type <str> opt_port
-%type <str> opt_reference
-%type <str> opt_scale
-%type <str> opt_server
-%type <str> opt_user
-%type <str> opt_opt_value
-%type <str> ora_user
-%type <str> precision
-%type <str> prepared_name
-%type <str> quoted_ident_stringvar
-%type <str> s_struct_union
-%type <str> server
-%type <str> server_name
-%type <str> single_vt_declaration
-%type <str> storage_clause
-%type <str> storage_declaration
-%type <str> storage_modifier
-%type <str> struct_union_type
-%type <str> struct_union_type_with_symbol
-%type <str> symbol
-%type <str> type_declaration
-%type <str> type_function_name
-%type <str> user_name
-%type <str> using_descriptor
-%type <str> var_declaration
-%type <str> var_type_declarations
-%type <str> variable
-%type <str> variable_declarations
-%type <str> variable_list
-%type <str> vt_declarations
-
-%type <str> Op
-%type <str> IntConstVar
-%type <str> AllConstVar
-%type <str> CSTRING
-%type <str> CPP_LINE
-%type <str> CVARIABLE
-%type <str> BCONST
-%type <str> SCONST
-%type <str> XCONST
-%type <str> IDENT
-
 %type  <struct_union> s_struct_union_symbol
 
 %type  <descriptor> ECPGGetDescriptor
index 6c0b8a27b1eb4b7d032065d99f9e775e7d61d514..8d2b6e7cb81226bd8e58ae7937564f034c720f2c 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "preproc_extern.h"
 
-static void output_escaped_str(char *str, bool quoted);
+static void output_escaped_str(const char *str, bool quoted);
 
 void
 output_line_number(void)
@@ -16,13 +16,12 @@ output_line_number(void)
 }
 
 void
-output_simple_statement(char *stmt, int whenever_mode)
+output_simple_statement(const char *stmt, int whenever_mode)
 {
    output_escaped_str(stmt, false);
    if (whenever_mode)
        whenever_action(whenever_mode);
    output_line_number();
-   free(stmt);
 }
 
 
@@ -133,7 +132,7 @@ static char *ecpg_statement_type_name[] = {
 };
 
 void
-output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+output_statement(const char *stmt, int whenever_mode, enum ECPG_statement_type st)
 {
    fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);
 
@@ -163,11 +162,10 @@ output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
    reset_variables();
 
    whenever_action(whenever_mode | 2);
-   free(stmt);
 }
 
 void
-output_prepare_statement(char *name, char *stmt)
+output_prepare_statement(const char *name, const char *stmt)
 {
    fprintf(base_yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
    output_escaped_str(name, true);
@@ -175,11 +173,10 @@ output_prepare_statement(char *name, char *stmt)
    output_escaped_str(stmt, true);
    fputs(");", base_yyout);
    whenever_action(2);
-   free(name);
 }
 
 void
-output_deallocate_prepare_statement(char *name)
+output_deallocate_prepare_statement(const char *name)
 {
    const char *con = connection ? connection : "NULL";
 
@@ -193,11 +190,10 @@ output_deallocate_prepare_statement(char *name)
        fprintf(base_yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
 
    whenever_action(2);
-   free(name);
 }
 
 static void
-output_escaped_str(char *str, bool quoted)
+output_escaped_str(const char *str, bool quoted)
 {
    int         i = 0;
    int         len = strlen(str);
index 3df94a24af0d8c1947cfbfa1fcae13c6395ca910..9ebe4bd793041229c9f42381aaf18ab5130771d4 100644 (file)
@@ -44,27 +44,10 @@ my %replace_token = (
    'IDENT' => 'ecpg_ident',
    'PARAM' => 'ecpg_param',);
 
-# Substitutions to apply to terminal token names to reconstruct the
-# literal form of the token.  (There is also a hard-wired substitution
-# rule that strips trailing '_P'.)
-my %replace_string = (
-   'FORMAT_LA' => 'format',
-   'NOT_LA' => 'not',
-   'NULLS_LA' => 'nulls',
-   'WITH_LA' => 'with',
-   'WITHOUT_LA' => 'without',
-   'TYPECAST' => '::',
-   'DOT_DOT' => '..',
-   'COLON_EQUALS' => ':=',
-   'EQUALS_GREATER' => '=>',
-   'LESS_EQUALS' => '<=',
-   'GREATER_EQUALS' => '>=',
-   'NOT_EQUALS' => '<>',);
-
-# This hash can provide a result type to override '<str>' for nonterminals
+# This hash can provide a result type to override "void" for nonterminals
 # that need that, or it can specify 'ignore' to cause us to skip the rule
-# for that nonterminal.  (In that case, ecpg.trailer had better provide
-# a substitute rule.)
+# for that nonterminal.  (In either case, ecpg.trailer had better provide
+# a substitute rule, since the default won't do.)
 my %replace_types = (
    'PrepareStmt' => '<prep>',
    'ExecuteStmt' => '<exec>',
@@ -175,11 +158,8 @@ my $non_term_id;
 # we plan to emit for the current rule.
 my $line = '';
 
-# @fields holds the items to be emitted in the token-concatenation action
-# for the current rule (assuming we emit one).  "$N" refers to the N'th
-# input token of the rule; anything else is a string to emit literally.
-# (We assume no such string can need to start with '$'.)
-my @fields;
+# count of tokens included in $line.
+my $line_count = 0;
 
 
 # Open parser / output file early, to raise errors early.
@@ -244,10 +224,6 @@ sub main
            $has_if_command = 1 if /^\s*if/;
        }
 
-       # We track %prec per-line, not per-rule, which is not quite right
-       # but there are no counterexamples in gram.y at present.
-       my $prec = 0;
-
        # Make sure any braces are split into separate fields
        s/{/ { /g;
        s/}/ } /g;
@@ -296,7 +272,7 @@ sub main
                }
 
                # If it's "<something>", it's a type in a %token declaration,
-               # which we can just drop.
+               # which we should just drop so that the tokens have void type.
                if (substr($a, 0, 1) eq '<')
                {
                    next;
@@ -376,7 +352,7 @@ sub main
                if ($copymode)
                {
                    # Print the accumulated rule.
-                   emit_rule(\@fields);
+                   emit_rule();
                    add_to_buffer('rules', ";\n\n");
                }
                else
@@ -386,8 +362,8 @@ sub main
                }
 
                # Reset for the next rule.
-               @fields = ();
                $line = '';
+               $line_count = 0;
                $in_rule = 0;
                $alt_count = 0;
                $has_feature_not_supported = 0;
@@ -401,11 +377,10 @@ sub main
                {
                    # Print the accumulated alternative.
                    # Increment $alt_count for each non-ignored alternative.
-                   $alt_count += emit_rule(\@fields);
+                   $alt_count += emit_rule();
                }
 
                # Reset for the next alternative.
-               @fields = ();
                # Start the next line with '|' if we've printed at least one
                # alternative.
                if ($alt_count > 1)
@@ -416,6 +391,7 @@ sub main
                {
                    $line = '';
                }
+               $line_count = 0;
                $has_feature_not_supported = 0;
                $has_if_command = 0;
                next;
@@ -444,13 +420,9 @@ sub main
                    $fieldIndexer++;
                }
 
-               # Check for %replace_types override of nonterminal's type
-               if (not defined $replace_types{$non_term_id})
-               {
-                   # By default, the type is <str>
-                   $replace_types{$non_term_id} = '<str>';
-               }
-               elsif ($replace_types{$non_term_id} eq 'ignore')
+               # Check for %replace_types entry indicating to ignore it.
+               if (defined $replace_types{$non_term_id}
+                   && $replace_types{$non_term_id} eq 'ignore')
                {
                    # We'll ignore this nonterminal and rule altogether.
                    $copymode = 0;
@@ -470,22 +442,26 @@ sub main
                    $stmt_mode = 0;
                }
 
-               # Emit appropriate %type declaration for this nonterminal.
-               my $tstr =
-                   '%type '
-                 . $replace_types{$non_term_id} . ' '
-                 . $non_term_id;
-               add_to_buffer('types', $tstr);
+               # Emit appropriate %type declaration for this nonterminal,
+               # if it has a type; otherwise omit that.
+               if (defined $replace_types{$non_term_id})
+               {
+                   my $tstr =
+                       '%type '
+                     . $replace_types{$non_term_id} . ' '
+                     . $non_term_id;
+                   add_to_buffer('types', $tstr);
+               }
 
                # Emit the target part of the rule.
                # Note: the leading space is just to match
                # the rather weird pre-v18 output logic.
-               $tstr = ' ' . $non_term_id . ':';
+               my $tstr = ' ' . $non_term_id . ':';
                add_to_buffer('rules', $tstr);
 
-               # Prepare for reading the fields (tokens) of the rule.
+               # Prepare for reading the tokens of the rule.
                $line = '';
-               @fields = ();
+               $line_count = 0;
                die "unterminated rule at grammar line $.\n"
                  if $in_rule;
                $in_rule = 1;
@@ -496,48 +472,7 @@ sub main
            {
                # Not a nonterminal declaration, so just add it to $line.
                $line = $line . ' ' . $arr[$fieldIndexer];
-           }
-
-           # %prec and whatever follows it should get added to $line,
-           # but not to @fields.
-           if ($arr[$fieldIndexer] eq '%prec')
-           {
-               $prec = 1;
-               next;
-           }
-
-           # Emit transformed version of token to @fields if appropriate.
-           if (   $copymode
-               && !$prec
-               && !$comment
-               && $in_rule)
-           {
-               my $S = $arr[$fieldIndexer];
-
-               # If it's a known terminal token (other than Op) or a literal
-               # character, we need to emit the equivalent string, which'll
-               # later get wrapped into a C string literal, perhaps after
-               # merging with adjacent strings.
-               if ($S ne 'Op'
-                   && (defined $tokens{$S}
-                       || $S =~ /^'.+'$/))
-               {
-                   # Apply replace_string substitution if any.
-                   $S = $replace_string{$S} if (exists $replace_string{$S});
-                   # Automatically strip _P if present.
-                   $S =~ s/_P$//;
-                   # And get rid of quotes if it's a literal character.
-                   $S =~ tr/'//d;
-                   # Finally, downcase and push into @fields.
-                   push(@fields, lc($S));
-               }
-               else
-               {
-                   # Otherwise, push a $N reference to this input token.
-                   # (We assume this cannot be confused with anything the
-                   # above code would produce.)
-                   push(@fields, '$' . (scalar(@fields) + 1));
-               }
+               $line_count++;
            }
        }
    }
@@ -568,13 +503,13 @@ sub include_file
 # by an ecpg.addons entry.
 sub emit_rule_action
 {
-   my ($tag, $fields) = @_;
+   my ($tag) = @_;
 
    # See if we have an addons entry; if not, just emit default action
    my $rec = $addons{$tag};
    if (!$rec)
    {
-       emit_default_action($fields, 0);
+       emit_default_action(0);
        return;
    }
 
@@ -585,7 +520,7 @@ sub emit_rule_action
    if ($rectype eq 'rule')
    {
        # Emit default action and then the code block.
-       emit_default_action($fields, 0);
+       emit_default_action(0);
    }
    elsif ($rectype eq 'addon')
    {
@@ -600,7 +535,7 @@ sub emit_rule_action
 
    if ($rectype eq 'addon')
    {
-       emit_default_action($fields, 1);
+       emit_default_action(1);
    }
    return;
 }
@@ -626,12 +561,11 @@ sub dump_buffer
 }
 
 # Emit the default action (usually token concatenation) for the current rule.
-#   Pass: fields array, brace_printed boolean
+#   Pass: brace_printed boolean
 # brace_printed should be true if caller already printed action's open brace.
 sub emit_default_action
 {
-   my ($flds, $brace_printed) = @_;
-   my $len = scalar(@$flds);
+   my ($brace_printed) = @_;
 
    if ($stmt_mode == 0)
    {
@@ -651,91 +585,21 @@ sub emit_default_action
            );
        }
 
-       if ($len == 0)
-       {
-           # Empty rule
-           if (!$brace_printed)
-           {
-               add_to_buffer('rules', ' { ');
-               $brace_printed = 1;
-           }
-           add_to_buffer('rules', ' $$=EMPTY; }');
-       }
-       else
-       {
-           # Go through each field and aggregate consecutive literal tokens
-           # into a single 'mm_strdup' call.
-           my @flds_new;
-           my $str;
-           for (my $z = 0; $z < $len; $z++)
-           {
-               if (substr($flds->[$z], 0, 1) eq '$')
-               {
-                   push(@flds_new, $flds->[$z]);
-                   next;
-               }
-
-               $str = $flds->[$z];
-
-               while (1)
-               {
-                   if ($z >= $len - 1
-                       || substr($flds->[ $z + 1 ], 0, 1) eq '$')
-                   {
-                       # Can't combine any more literals; push to @flds_new.
-                       # This code would need work if any literals contain
-                       # backslash or double quote, but right now that never
-                       # happens.
-                       push(@flds_new, "mm_strdup(\"$str\")");
-                       last;
-                   }
-                   $z++;
-                   $str = $str . ' ' . $flds->[$z];
-               }
-           }
-
-           # So - how many fields did we end up with ?
-           $len = scalar(@flds_new);
-           if ($len == 1)
-           {
-               # Single field can be handled by straight assignment
-               if (!$brace_printed)
-               {
-                   add_to_buffer('rules', ' { ');
-                   $brace_printed = 1;
-               }
-               $str = ' $$ = ' . $flds_new[0] . ';';
-               add_to_buffer('rules', $str);
-           }
-           else
-           {
-               # Need to concatenate the results to form our final string
-               if (!$brace_printed)
-               {
-                   add_to_buffer('rules', ' { ');
-                   $brace_printed = 1;
-               }
-               $str =
-                 ' $$ = cat_str(' . $len . ',' . join(',', @flds_new) . ');';
-               add_to_buffer('rules', $str);
-           }
-           add_to_buffer('rules', '}') if ($brace_printed);
-       }
+       add_to_buffer('rules', '}') if ($brace_printed);
    }
    else
    {
        # We're in the "stmt:" rule, where we need to output special actions.
        # This code assumes that no ecpg.addons entry applies.
-       if ($len)
+       if ($line_count)
        {
            # Any regular kind of statement calls output_statement
            add_to_buffer('rules',
-               ' { output_statement($1, 0, ECPGst_normal); }');
+               ' { output_statement(@1, 0, ECPGst_normal); }');
        }
        else
        {
            # The empty production for stmt: do nothing
-           add_to_buffer('rules', ' { $$ = NULL; }');
        }
    }
    return;
@@ -746,8 +610,6 @@ sub emit_default_action
 # entry in %replace_line, then do nothing and return 0.
 sub emit_rule
 {
-   my ($fields) = @_;
-
    # compute tag to be used as lookup key in %replace_line and %addons
    my $tag = $non_term_id . $line;
    $tag =~ tr/ |//d;
@@ -761,7 +623,8 @@ sub emit_rule
            return 0;
        }
 
-       # non-ignore entries replace the line, but we'd better keep any '|'
+       # non-ignore entries replace the line, but we'd better keep any '|';
+       # we don't bother to update $line_count here.
        if (index($line, '|') != -1)
        {
            $line = '| ' . $rep;
@@ -778,7 +641,7 @@ sub emit_rule
 
    # Emit $line, then print the appropriate action.
    add_to_buffer('rules', $line);
-   emit_rule_action($tag, $fields);
+   emit_rule_action($tag);
    return 1;
 }
 
index 9daeee330344ae1c820a3f457ad11ba586980f1d..ca0dead26d0c5f478803e5f80004d416e673fdde 100644 (file)
@@ -31,6 +31,7 @@ static YYSTYPE lookahead_yylval;  /* yylval for lookahead token */
 static YYLTYPE lookahead_yylloc;   /* yylloc for lookahead token */
 static char *lookahead_yytext; /* start current token */
 
+static int base_yylex_location(void);
 static bool check_uescapechar(unsigned char escape);
 static bool ecpg_isspace(char ch);
 
@@ -71,7 +72,7 @@ filtered_base_yylex(void)
        have_lookahead = false;
    }
    else
-       cur_token = base_yylex();
+       cur_token = base_yylex_location();
 
    /*
     * If this token isn't one that requires lookahead, just return it.
@@ -96,7 +97,7 @@ filtered_base_yylex(void)
    cur_yytext = base_yytext;
 
    /* Get next token, saving outputs into lookahead variables */
-   next_token = base_yylex();
+   next_token = base_yylex_location();
 
    lookahead_token = next_token;
    lookahead_yylval = base_yylval;
@@ -184,7 +185,7 @@ filtered_base_yylex(void)
                cur_yytext = base_yytext;
 
                /* Get third token */
-               next_token = base_yylex();
+               next_token = base_yylex_location();
 
                if (next_token != SCONST)
                    mmerror(PARSE_ERROR, ET_ERROR, "UESCAPE must be followed by a simple string literal");
@@ -203,6 +204,7 @@ filtered_base_yylex(void)
 
                /* Combine 3 tokens into 1 */
                base_yylval.str = psprintf("%s UESCAPE %s", base_yylval.str, escstr);
+               base_yylloc = mm_strdup(base_yylval.str);
 
                /* Clear have_lookahead, thereby consuming all three tokens */
                have_lookahead = false;
@@ -218,6 +220,56 @@ filtered_base_yylex(void)
    return cur_token;
 }
 
+/*
+ * Call base_yylex() and fill in base_yylloc.
+ *
+ * pgc.l does not worry about setting yylloc, and given what we want for
+ * that, trying to set it there would be pretty inconvenient.  What we
+ * want is: if the returned token has type <str>, then duplicate its
+ * string value as yylloc; otherwise, make a downcased copy of yytext.
+ * The downcasing is ASCII-only because all that we care about there
+ * is producing uniformly-cased output of keywords.  (That's mostly
+ * cosmetic, but there are places in ecpglib that expect to receive
+ * downcased keywords, plus it keeps us regression-test-compatible
+ * with the pre-v18 implementation of ecpg.)
+ */
+static int
+base_yylex_location(void)
+{
+   int         token = base_yylex();
+
+   switch (token)
+   {
+           /* List a token here if pgc.l assigns to base_yylval.str for it */
+       case Op:
+       case CSTRING:
+       case CPP_LINE:
+       case CVARIABLE:
+       case BCONST:
+       case SCONST:
+       case USCONST:
+       case XCONST:
+       case FCONST:
+       case IDENT:
+       case UIDENT:
+       case IP:
+           /* Duplicate the <str> value */
+           base_yylloc = mm_strdup(base_yylval.str);
+           break;
+       default:
+           /* Else just use the input, i.e., yytext */
+           base_yylloc = mm_strdup(base_yytext);
+           /* Apply an ASCII-only downcasing */
+           for (unsigned char *ptr = (unsigned char *) base_yylloc; *ptr; ptr++)
+           {
+               if (*ptr >= 'A' && *ptr <= 'Z')
+                   *ptr += 'a' - 'A';
+           }
+           break;
+   }
+   return token;
+}
+
 /*
  * check_uescapechar() and ecpg_isspace() should match their equivalents
  * in pgc.l.
index c5fd07fbd8b52e15fc76b5d4611233971dd0d359..da93967462036b2190e9bbadc381c5de00cc2d6a 100644 (file)
 #define STRUCT_DEPTH 128
 #define EMPTY mm_strdup("")
 
+/*
+ * "Location tracking" support --- see ecpg.header for more comments.
+ */
+typedef char *YYLTYPE;
+
+#define YYLTYPE_IS_DECLARED 1
+
 /* variables */
 
 extern bool autocommit,
@@ -65,10 +72,10 @@ extern const uint16 SQLScanKeywordTokens[];
 extern const char *get_dtype(enum ECPGdtype);
 extern void lex_init(void);
 extern void output_line_number(void);
-extern void output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st);
-extern void output_prepare_statement(char *name, char *stmt);
-extern void output_deallocate_prepare_statement(char *name);
-extern void output_simple_statement(char *stmt, int whenever_mode);
+extern void output_statement(const char *stmt, int whenever_mode, enum ECPG_statement_type st);
+extern void output_prepare_statement(const char *name, const char *stmt);
+extern void output_deallocate_prepare_statement(const char *name);
+extern void output_simple_statement(const char *stmt, int whenever_mode);
 extern char *hashline_number(void);
 extern int base_yyparse(void);
 extern int base_yylex(void);