summaryrefslogtreecommitdiffstats
path: root/src/interfaces/ecpg/preproc/ecpg.addons
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/ecpg/preproc/ecpg.addons')
-rw-r--r--src/interfaces/ecpg/preproc/ecpg.addons590
1 files changed, 590 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
new file mode 100644
index 0000000..94f7d4a
--- /dev/null
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -0,0 +1,590 @@
+/* src/interfaces/ecpg/preproc/ecpg.addons */
+ECPG: stmtClosePortalStmt block
+ {
+ if (INFORMIX_MODE)
+ {
+ 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);
+ }
+ECPG: stmtDeallocateStmt block
+ {
+ output_deallocate_prepare_statement($1);
+ }
+ECPG: stmtDeclareCursorStmt block
+ { 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); }
+ECPG: stmtDeleteStmt block
+ECPG: stmtInsertStmt block
+ECPG: stmtSelectStmt block
+ECPG: stmtUpdateStmt block
+ { output_statement($1, 1, ECPGst_prepnormal); }
+ECPG: stmtExecuteStmt block
+ {
+ check_declared_list($1.name);
+ if ($1.type == NULL || strlen($1.type) == 0)
+ output_statement($1.name, 1, ECPGst_execute);
+ else
+ {
+ if ($1.name[0] != '"')
+ /* case of char_variable */
+ add_variable_to_tail(&argsinsert, find_variable($1.name), &no_indicator);
+ else
+ {
+ /* case of ecpg_ident or CSTRING */
+ char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+ char *str = mm_strdup($1.name + 1);
+
+ /* It must be cut off double quotation because new_variable() double-quotes. */
+ str[strlen(str) - 1] = '\0';
+ sprintf(length, "%zu", strlen(str));
+ add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
+ }
+ output_statement(cat_str(3, mm_strdup("execute"), mm_strdup("$0"), $1.type), 0, ECPGst_exec_with_exprlist);
+ }
+ }
+ECPG: stmtPrepareStmt block
+ {
+ check_declared_list($1.name);
+ if ($1.type == NULL)
+ output_prepare_statement($1.name, $1.stmt);
+ else if (strlen($1.type) == 0)
+ {
+ char *stmt = cat_str(3, mm_strdup("\""), $1.stmt, mm_strdup("\""));
+ output_prepare_statement($1.name, stmt);
+ }
+ else
+ {
+ if ($1.name[0] != '"')
+ /* case of char_variable */
+ add_variable_to_tail(&argsinsert, find_variable($1.name), &no_indicator);
+ else
+ {
+ char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+ char *str = mm_strdup($1.name + 1);
+
+ /* It must be cut off double quotation because new_variable() double-quotes. */
+ str[strlen(str) - 1] = '\0';
+ sprintf(length, "%zu", strlen(str));
+ add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
+ }
+ output_statement(cat_str(5, mm_strdup("prepare"), mm_strdup("$0"), $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_prepare);
+ }
+ }
+ECPG: stmtTransactionStmt block
+ {
+ 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);
+ whenever_action(2);
+ free($1);
+ }
+ECPG: stmtViewStmt rule
+ | ECPGAllocateDescr
+ {
+ 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);
+ reset_variables();
+ whenever_action(2);
+ free($1);
+ }
+ | ECPGDeclareStmt
+ {
+ output_simple_statement($1, 0);
+ }
+ | ECPGCursorStmt
+ {
+ output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+ }
+ | ECPGDeallocateDescr
+ {
+ fprintf(base_yyout,"ECPGdeallocate_desc(__LINE__, %s);",$1);
+ whenever_action(0);
+ free($1);
+ }
+ | ECPGDeclare
+ {
+ output_simple_statement($1, 0);
+ }
+ | ECPGDescribe
+ {
+ check_declared_list($1.stmt_name);
+
+ fprintf(base_yyout, "{ ECPGdescribe(__LINE__, %d, %d, %s, %s,", compat, $1.input, connection ? connection : "NULL", $1.stmt_name);
+ dump_variables(argsresult, 1);
+ fputs("ECPGt_EORT);", base_yyout);
+ fprintf(base_yyout, "}");
+ output_line_number();
+
+ free($1.stmt_name);
+ }
+ | ECPGDisconnect
+ {
+ if (connection)
+ mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in DISCONNECT statement");
+
+ fprintf(base_yyout, "{ ECPGdisconnect(__LINE__, %s);",
+ $1 ? $1 : "\"CURRENT\"");
+ whenever_action(2);
+ free($1);
+ }
+ | ECPGExecuteImmediateStmt { output_statement($1, 0, ECPGst_exec_immediate); }
+ | ECPGFree
+ {
+ const char *con = connection ? connection : "NULL";
+
+ 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
+ fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, $1);
+
+ whenever_action(2);
+ free($1);
+ }
+ | ECPGGetDescriptor
+ {
+ lookup_descriptor($1.name, connection);
+ output_get_descr($1.name, $1.str);
+ free($1.name);
+ free($1.str);
+ }
+ | ECPGGetDescriptorHeader
+ {
+ lookup_descriptor($1, connection);
+ output_get_descr_header($1);
+ free($1);
+ }
+ | ECPGOpen
+ {
+ struct cursor *ptr;
+
+ 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);
+ ptr->opened = true;
+ }
+ }
+ | ECPGSetAutocommit
+ {
+ 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);
+ whenever_action(2);
+ free($1);
+ }
+ | ECPGSetDescriptor
+ {
+ lookup_descriptor($1.name, connection);
+ output_set_descr($1.name, $1.str);
+ free($1.name);
+ free($1.str);
+ }
+ | ECPGSetDescriptorHeader
+ {
+ lookup_descriptor($1, connection);
+ output_set_descr_header($1);
+ free($1);
+ }
+ | ECPGTypedef
+ {
+ if (connection)
+ mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in TYPE statement");
+
+ fprintf(base_yyout, "%s", $1);
+ free($1);
+ output_line_number();
+ }
+ | ECPGVar
+ {
+ if (connection)
+ mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in VAR statement");
+
+ 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);
+ }
+ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block
+ {
+ char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+ $$ = 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))
+ mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
+ECPG: var_valueNumericOnly addon
+ if ($1[0] == '$')
+ {
+ free($1);
+ $1 = mm_strdup("$0");
+ }
+ECPG: fetch_argscursor_name addon
+ 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");
+ }
+ECPG: fetch_argsfrom_incursor_name addon
+ 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");
+ }
+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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ if ($3[0] == ':')
+ {
+ free($3);
+ $3 = mm_strdup("$0");
+ }
+ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
+ 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 ($1[0] == '$')
+ {
+ free($1);
+ $1 = mm_strdup("$0");
+ }
+ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
+ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
+ 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");
+ }
+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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ if ($4[0] == ':')
+ {
+ free($4);
+ $4 = mm_strdup("$0");
+ }
+ if ($2[0] == '$')
+ {
+ free($2);
+ $2 = mm_strdup("$0");
+ }
+ECPG: cursor_namename rule
+ | char_civar
+ {
+ char *curname = mm_alloc(strlen($1) + 2);
+ sprintf(curname, ":%s", $1);
+ free($1);
+ $1 = curname;
+ $$ = $1;
+ }
+ECPG: ExplainableStmtExecuteStmt block
+ {
+ $$ = $1.name;
+ }
+ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
+ {
+ $$.name = $2;
+ $$.type = $3;
+ $$.stmt = $5;
+ }
+ | PREPARE prepared_name FROM execstring
+ {
+ $$.name = $2;
+ $$.type = NULL;
+ $$.stmt = $4;
+ }
+ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
+ {
+ $$.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);
+ }
+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);
+ }
+ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
+ {
+ struct cursor *ptr, *this;
+ 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);
+
+ 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 ($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);
+ }
+ }
+
+ this = (struct cursor *) mm_alloc(sizeof(struct cursor));
+
+ this->next = cur;
+ this->name = $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->argsinsert = argsinsert;
+ this->argsinsert_oos = NULL;
+ this->argsresult = argsresult;
+ this->argsresult_oos = NULL;
+ argsinsert = argsresult = NULL;
+ cur = this;
+
+ c1 = mm_strdup(this->command);
+ if ((c2 = strstr(c1, "*/")) != NULL)
+ {
+ /* We put this text into a comment, so we better remove [*][/]. */
+ c2[0] = '.';
+ c2[1] = '.';
+ }
+ comment = cat_str(3, mm_strdup("/*"), c1, mm_strdup("*/"));
+
+ $$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
+ }
+ECPG: ClosePortalStmtCLOSEcursor_name block
+ {
+ 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 (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ break;
+ }
+ }
+ $$ = cat2_str(mm_strdup("close"), cursor_marker);
+ }
+ECPG: opt_hold block
+ {
+ if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
+ $$ = mm_strdup("with hold");
+ else
+ $$ = EMPTY;
+ }
+ECPG: into_clauseINTOOptTempTableName block
+ {
+ FoundInto = 1;
+ $$= cat2_str(mm_strdup("into"), $2);
+ }
+ | ecpg_into { $$ = EMPTY; }
+ECPG: table_refselect_with_parensopt_alias_clause addon
+ if ($2 == NULL)
+ mmerror(PARSE_ERROR, ET_ERROR, "subquery in FROM must have an alias");
+ECPG: table_refLATERAL_Pselect_with_parensopt_alias_clause addon
+ if ($3 == NULL)
+ mmerror(PARSE_ERROR, ET_ERROR, "subquery in FROM must have an alias");
+ECPG: TypenameSimpleTypenameopt_array_bounds block
+ { $$ = cat2_str($1, $2.str); }
+ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
+ { $$ = cat_str(3, mm_strdup("setof"), $2, $3.str); }
+ECPG: opt_array_boundsopt_array_bounds'['']' block
+ {
+ $$.index1 = $1.index1;
+ $$.index2 = $1.index2;
+ if (strcmp($$.index1, "-1") == 0)
+ $$.index1 = mm_strdup("0");
+ else if (strcmp($1.index2, "-1") == 0)
+ $$.index2 = mm_strdup("0");
+ $$.str = cat_str(2, $1.str, mm_strdup("[]"));
+ }
+ | opt_array_bounds '[' Iresult ']'
+ {
+ $$.index1 = $1.index1;
+ $$.index2 = $1.index2;
+ if (strcmp($1.index1, "-1") == 0)
+ $$.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("]"));
+ }
+ECPG: opt_array_bounds
+ {
+ $$.index1 = mm_strdup("-1");
+ $$.index2 = mm_strdup("-1");
+ $$.str= EMPTY;
+ }
+ECPG: IconstICONST block
+ { $$ = make_name(); }
+ECPG: AexprConstNULL_P rule
+ | civar { $$ = $1; }
+ | civarind { $$ = $1; }
+ECPG: ColIdcol_name_keyword rule
+ | ECPGKeywords { $$ = $1; }
+ | ECPGCKeywords { $$ = $1; }
+ | CHAR_P { $$ = mm_strdup("char"); }
+ | VALUES { $$ = mm_strdup("values"); }
+ECPG: type_function_nametype_func_name_keyword rule
+ | ECPGKeywords { $$ = $1; }
+ | ECPGTypeName { $$ = $1; }
+ | ECPGCKeywords { $$ = $1; }
+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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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);
+ if (ptr -> connection)
+ connection = mm_strdup(ptr -> connection);
+
+ $$ = 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; }