diff --git a/src/interfaces/ecpg/preproc/README.parser b/src/interfaces/ecpg/preproc/README.parser index 378cb9344c4..85ecc803e09 100644 --- a/src/interfaces/ecpg/preproc/README.parser +++ b/src/interfaces/ecpg/preproc/README.parser @@ -40,19 +40,19 @@ continue to use the normal Bison notations.) ecpg.addons contains entries that begin with a line like - ECPG: concattokens ruletype + ECPG: ruletype tokenlist and typically have one or more following lines that are the code for a grammar action. Any line not starting with "ECPG:" is taken to be part of the code block for the preceding "ECPG:" line. -"concattokens" identifies which gram.y production this entry affects. -It is simply the target nonterminal and the tokens from the gram.y rule -concatenated together. For example, to modify the action for a gram.y -rule like this: +"tokenlist" identifies which gram.y production this entry affects. +It is simply a list of the target nonterminal and the input tokens +from the gram.y rule. For example, to modify the action for a +gram.y rule like this: target: tokenA tokenB tokenC {...} -"concattokens" would be "targettokenAtokenBtokenC". If we want to +"tokenlist" would be "target tokenA tokenB tokenC". If we want to modify a non-first alternative for a nonterminal, we still write the -nonterminal. For example, "concattokens" should be "targettokenDtokenE" +nonterminal. For example, "tokenlist" should be "target tokenD tokenE" to affect the second alternative in: target: tokenA tokenB tokenC {...} | tokenD tokenE {...} @@ -72,7 +72,7 @@ c) "rule" - the automatic action is emitted, but then the entry's code block is added verbatim afterwards. This typically is used to add new alternatives to a nonterminal of the core grammar. For example, given the entry: - ECPG: targettokenAtokenBtokenC rule + ECPG: rule target tokenA tokenB tokenC | tokenD tokenE { custom_action; } what will be emitted is target: tokenA tokenB tokenC { automatic_action; } diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons index 9c120fead24..935820c8d46 100644 --- a/src/interfaces/ecpg/preproc/ecpg.addons +++ b/src/interfaces/ecpg/preproc/ecpg.addons @@ -1,5 +1,5 @@ /* src/interfaces/ecpg/preproc/ecpg.addons */ -ECPG: stmtClosePortalStmt block +ECPG: block stmt ClosePortalStmt { if (INFORMIX_MODE) { @@ -16,23 +16,23 @@ ECPG: stmtClosePortalStmt block output_statement(@1, 0, ECPGst_normal); } -ECPG: stmtDeallocateStmt block +ECPG: block stmt DeallocateStmt { output_deallocate_prepare_statement(@1); } -ECPG: stmtDeclareCursorStmt block +ECPG: block stmt DeclareCursorStmt { output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0); } -ECPG: stmtDiscardStmt block -ECPG: stmtFetchStmt block +ECPG: block stmt DiscardStmt +ECPG: block stmt FetchStmt { output_statement(@1, 1, ECPGst_normal); } -ECPG: stmtDeleteStmt block -ECPG: stmtInsertStmt block -ECPG: stmtSelectStmt block -ECPG: stmtUpdateStmt block +ECPG: block stmt DeleteStmt +ECPG: block stmt InsertStmt +ECPG: block stmt SelectStmt +ECPG: block stmt UpdateStmt { output_statement(@1, 1, ECPGst_prepnormal); } -ECPG: stmtExecuteStmt block +ECPG: block stmt ExecuteStmt { check_declared_list($1.name); if ($1.type == NULL || strlen($1.type) == 0) @@ -57,7 +57,7 @@ ECPG: stmtExecuteStmt block output_statement(cat_str(3, "execute", "$0", $1.type), 0, ECPGst_exec_with_exprlist); } } -ECPG: stmtPrepareStmt block +ECPG: block stmt PrepareStmt { check_declared_list($1.name); if ($1.type == NULL) @@ -87,17 +87,17 @@ ECPG: stmtPrepareStmt block output_statement(cat_str(5, "prepare", "$0", $1.type, "as", $1.stmt), 0, ECPGst_prepare); } } -ECPG: stmtTransactionStmt block +ECPG: block stmt TransactionStmt { fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1); whenever_action(2); } -ECPG: toplevel_stmtTransactionStmtLegacy block +ECPG: block toplevel_stmt TransactionStmtLegacy { fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1); whenever_action(2); } -ECPG: stmtViewStmt rule +ECPG: rule stmt ViewStmt | ECPGAllocateDescr { fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", @1); @@ -231,45 +231,45 @@ ECPG: stmtViewStmt rule output_simple_statement(@1, 0); } -ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block +ECPG: block where_or_current_clause WHERE CURRENT_P OF cursor_name { const char *cursor_marker = @4[0] == ':' ? "$0" : @4; @$ = cat_str(2, "where current of", cursor_marker); } -ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listcopy_fromopt_programcopy_file_namecopy_delimiteropt_withcopy_optionswhere_clause addon +ECPG: addon CopyStmt COPY opt_binary qualified_name opt_column_list copy_from opt_program copy_file_name copy_delimiter opt_with copy_options where_clause 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 +ECPG: addon var_value NumericOnly if (@1[0] == '$') @$ = "$0"; -ECPG: fetch_argscursor_name addon +ECPG: addon fetch_args cursor_name struct cursor *ptr = add_additional_variables(@1, false); if (ptr->connection) connection = mm_strdup(ptr->connection); if (@1[0] == ':') @$ = "$0"; -ECPG: fetch_argsfrom_incursor_name addon +ECPG: addon fetch_args from_in cursor_name struct cursor *ptr = add_additional_variables(@2, false); if (ptr->connection) connection = mm_strdup(ptr->connection); if (@2[0] == ':') @$ = cat2_str(@1, "$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 +ECPG: addon fetch_args NEXT opt_from_in cursor_name +ECPG: addon fetch_args PRIOR opt_from_in cursor_name +ECPG: addon fetch_args FIRST_P opt_from_in cursor_name +ECPG: addon fetch_args LAST_P opt_from_in cursor_name +ECPG: addon fetch_args ALL opt_from_in cursor_name struct cursor *ptr = add_additional_variables(@3, false); if (ptr->connection) connection = mm_strdup(ptr->connection); if (@3[0] == ':') @$ = cat_str(3, @1, @2, "$0"); -ECPG: fetch_argsSignedIconstopt_from_incursor_name addon +ECPG: addon fetch_args SignedIconst opt_from_in cursor_name struct cursor *ptr = add_additional_variables(@3, false); bool replace = false; @@ -287,18 +287,18 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon } if (replace) @$ = cat_str(3, @1, @2, @3); -ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon -ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon +ECPG: addon fetch_args FORWARD ALL opt_from_in cursor_name +ECPG: addon fetch_args BACKWARD ALL opt_from_in cursor_name struct cursor *ptr = add_additional_variables(@4, false); if (ptr->connection) connection = mm_strdup(ptr->connection); if (@4[0] == ':') @$ = cat_str(4, @1, @2, @3, "$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 +ECPG: addon fetch_args ABSOLUTE_P SignedIconst opt_from_in cursor_name +ECPG: addon fetch_args RELATIVE_P SignedIconst opt_from_in cursor_name +ECPG: addon fetch_args FORWARD SignedIconst opt_from_in cursor_name +ECPG: addon fetch_args BACKWARD SignedIconst opt_from_in cursor_name struct cursor *ptr = add_additional_variables(@4, false); bool replace = false; @@ -316,7 +316,7 @@ ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon } if (replace) @$ = cat_str(4, @1, @2, @3, @4); -ECPG: cursor_namename block +ECPG: block cursor_name name | char_civar { char *curname = loc_alloc(strlen(@1) + 2); @@ -324,11 +324,11 @@ ECPG: cursor_namename block sprintf(curname, ":%s", @1); @$ = curname; } -ECPG: ExplainableStmtExecuteStmt block +ECPG: block ExplainableStmt ExecuteStmt { @$ = $1.name; } -ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block +ECPG: block PrepareStmt PREPARE prepared_name prep_type_clause AS PreparableStmt { $$.name = @2; $$.type = @3; @@ -340,20 +340,20 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block $$.type = NULL; $$.stmt = @4; } -ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block +ECPG: block ExecuteStmt EXECUTE prepared_name execute_param_clause execute_rest { $$.name = @2; $$.type = @3; } -ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block +ECPG: block ExecuteStmt CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest { $$.name = @$; } -ECPG: ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block +ECPG: block ExecuteStmt CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest { $$.name = @$; } -ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block +ECPG: block DeclareCursorStmt DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt { struct cursor *ptr, *this; @@ -403,7 +403,7 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt @$ = cat2_str(adjust_outofscope_cursor_vars(this), comment); } -ECPG: ClosePortalStmtCLOSEcursor_name block +ECPG: block ClosePortalStmt CLOSE cursor_name { const char *cursor_marker = @2[0] == ':' ? "$0" : @2; struct cursor *ptr = NULL; @@ -419,14 +419,14 @@ ECPG: ClosePortalStmtCLOSEcursor_name block } @$ = cat2_str("close", cursor_marker); } -ECPG: opt_hold block +ECPG: block opt_hold { if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit) @$ = "with hold"; else @$ = ""; } -ECPG: into_clauseINTOOptTempTableName block +ECPG: block into_clause INTO OptTempTableName { FoundInto = 1; @$ = cat2_str("into", @2); @@ -435,15 +435,15 @@ ECPG: into_clauseINTOOptTempTableName block { @$ = ""; } -ECPG: TypenameSimpleTypenameopt_array_bounds block +ECPG: block Typename SimpleTypename opt_array_bounds { @$ = cat2_str(@1, $2.str); } -ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block +ECPG: block Typename SETOF SimpleTypename opt_array_bounds { @$ = cat_str(3, "setof", @2, $3.str); } -ECPG: opt_array_boundsopt_array_bounds'['']' block +ECPG: block opt_array_bounds opt_array_bounds '[' ']' { $$.index1 = $1.index1; $$.index2 = $1.index2; @@ -463,20 +463,20 @@ ECPG: opt_array_boundsopt_array_bounds'['']' block $$.index2 = @3; $$.str = cat_str(4, $1.str, "[", @3, "]"); } -ECPG: opt_array_bounds block +ECPG: block opt_array_bounds { $$.index1 = "-1"; $$.index2 = "-1"; $$.str = ""; } -ECPG: AexprConstNULL_P rule +ECPG: rule AexprConst NULL_P | civar | civarind -ECPG: VariableShowStmtSHOWALL block +ECPG: block VariableShowStmt SHOW ALL { mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented"); } -ECPG: FetchStmtMOVEfetch_args rule +ECPG: rule FetchStmt MOVE fetch_args | FETCH fetch_args ecpg_fetch_into | FETCH FORWARD cursor_name opt_ecpg_fetch_into { @@ -558,9 +558,9 @@ ECPG: FetchStmtMOVEfetch_args rule @$ = cat_str(2, "move backward from", cursor_marker); } -ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block +ECPG: block limit_clause LIMIT select_limit_value ',' select_offset_value { mmerror(PARSE_ERROR, ET_WARNING, "no longer supported LIMIT #,# syntax passed to server"); } -ECPG: SignedIconstIconst rule +ECPG: rule SignedIconst Iconst | civar diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl index 91f76e719c0..86943ae2537 100644 --- a/src/interfaces/ecpg/preproc/parse.pl +++ b/src/interfaces/ecpg/preproc/parse.pl @@ -75,47 +75,47 @@ my %replace_types = ( my %replace_types_used; # This hash provides an "ignore" option or substitute expansion for any -# rule or rule alternative. The hash key is the same "concattokens" tag +# rule or rule alternative. The hash key is the same "tokenlist" tag # used for lookup in ecpg.addons. my %replace_line = ( # These entries excise certain keywords from the core keyword lists. # Be sure to account for these in ColLabel and related productions. - 'unreserved_keywordCONNECTION' => 'ignore', - 'unreserved_keywordCURRENT_P' => 'ignore', - 'unreserved_keywordDAY_P' => 'ignore', - 'unreserved_keywordHOUR_P' => 'ignore', - 'unreserved_keywordINPUT_P' => 'ignore', - 'unreserved_keywordMINUTE_P' => 'ignore', - 'unreserved_keywordMONTH_P' => 'ignore', - 'unreserved_keywordSECOND_P' => 'ignore', - 'unreserved_keywordYEAR_P' => 'ignore', - 'col_name_keywordCHAR_P' => 'ignore', - 'col_name_keywordINT_P' => 'ignore', - 'col_name_keywordVALUES' => 'ignore', - 'reserved_keywordTO' => 'ignore', - 'reserved_keywordUNION' => 'ignore', + 'unreserved_keyword CONNECTION' => 'ignore', + 'unreserved_keyword CURRENT_P' => 'ignore', + 'unreserved_keyword DAY_P' => 'ignore', + 'unreserved_keyword HOUR_P' => 'ignore', + 'unreserved_keyword INPUT_P' => 'ignore', + 'unreserved_keyword MINUTE_P' => 'ignore', + 'unreserved_keyword MONTH_P' => 'ignore', + 'unreserved_keyword SECOND_P' => 'ignore', + 'unreserved_keyword YEAR_P' => 'ignore', + 'col_name_keyword CHAR_P' => 'ignore', + 'col_name_keyword INT_P' => 'ignore', + 'col_name_keyword VALUES' => 'ignore', + 'reserved_keyword TO' => 'ignore', + 'reserved_keyword UNION' => 'ignore', # some other production rules have to be ignored or replaced - 'fetch_argsFORWARDopt_from_incursor_name' => 'ignore', - 'fetch_argsBACKWARDopt_from_incursor_name' => 'ignore', - "opt_array_boundsopt_array_bounds'['Iconst']'" => 'ignore', - 'VariableShowStmtSHOWvar_name' => 'SHOW var_name ecpg_into', - 'VariableShowStmtSHOWTIMEZONE' => 'SHOW TIME ZONE ecpg_into', - 'VariableShowStmtSHOWTRANSACTIONISOLATIONLEVEL' => + 'fetch_args FORWARD opt_from_in cursor_name' => 'ignore', + 'fetch_args BACKWARD opt_from_in cursor_name' => 'ignore', + "opt_array_bounds opt_array_bounds '[' Iconst ']'" => 'ignore', + 'VariableShowStmt SHOW var_name' => 'SHOW var_name ecpg_into', + 'VariableShowStmt SHOW TIME ZONE' => 'SHOW TIME ZONE ecpg_into', + 'VariableShowStmt SHOW TRANSACTION ISOLATION LEVEL' => 'SHOW TRANSACTION ISOLATION LEVEL ecpg_into', - 'VariableShowStmtSHOWSESSIONAUTHORIZATION' => + 'VariableShowStmt SHOW SESSION AUTHORIZATION' => 'SHOW SESSION AUTHORIZATION ecpg_into', - 'returning_clauseRETURNINGtarget_list' => + 'returning_clause RETURNING target_list' => 'RETURNING target_list opt_ecpg_into', - 'ExecuteStmtEXECUTEnameexecute_param_clause' => + 'ExecuteStmt EXECUTE name execute_param_clause' => 'EXECUTE prepared_name execute_param_clause execute_rest', - 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' + 'ExecuteStmt CREATE OptTemp TABLE create_as_target AS EXECUTE name execute_param_clause opt_with_data' => 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest', - 'ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' + 'ExecuteStmt CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE name execute_param_clause opt_with_data' => 'CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest', - 'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' => + 'PrepareStmt PREPARE name prep_type_clause AS PreparableStmt' => 'PREPARE prepared_name prep_type_clause AS PreparableStmt', - 'var_nameColId' => 'ECPGColId'); + 'var_name ColId' => 'ECPGColId'); my %replace_line_used; @@ -645,8 +645,9 @@ sub emit_default_action sub emit_rule { # compute tag to be used as lookup key in %replace_line and %addons - my $tag = $non_term_id . $line; - $tag =~ tr/ |//d; + my $tag = $non_term_id . ' ' . $line; + $tag =~ tr/|//d; + $tag = join(' ', split(/\s+/, $tag)); # apply replace_line substitution if any my $rep = $replace_line{$tag}; @@ -671,8 +672,9 @@ sub emit_rule } # recompute tag for use in emit_rule_action - $tag = $non_term_id . $line; - $tag =~ tr/ |//d; + $tag = $non_term_id . ' ' . $line; + $tag =~ tr/|//d; + $tag = join(' ', split(/\s+/, $tag)); } # Emit $line, then print the appropriate action. @@ -684,8 +686,8 @@ sub emit_rule =top load ecpg.addons into %addons hash. The result is something like %addons = { - stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 }, - stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 } + 'stmt ClosePortalStmt' => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 }, + 'stmt ViewStmt' => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 } } =cut @@ -704,14 +706,21 @@ sub preload_addons my $skip = 1; while (<$fh>) { - if (/^ECPG:\s+(\S+)\s+(\w+)\s*$/) + if (/^ECPG:\s+(\w+)\s+(.*)$/) { # Found an "ECPG:" line, so we're done skipping the header $skip = 0; + my $type = $1; + my $target = $2; + # Normalize target so there's exactly one space between tokens + $target = join(' ', split(/\s+/, $target)); # Validate record type and target - die "invalid record type $2 in addon rule for $1\n" - unless ($2 eq 'block' or $2 eq 'addon' or $2 eq 'rule'); - die "duplicate addon rule for $1\n" if (exists $addons{$1}); + die "invalid record type $type in addon rule for $target\n" + unless ($type eq 'block' + or $type eq 'addon' + or $type eq 'rule'); + die "duplicate addon rule for $target\n" + if (exists $addons{$target}); # If we had some preceding code lines, attach them to all # as-yet-unfinished records. if (@code) @@ -724,10 +733,10 @@ sub preload_addons @needsRules = (); } my $record = {}; - $record->{type} = $2; + $record->{type} = $type; $record->{lines} = []; $record->{used} = 0; - $addons{$1} = $record; + $addons{$target} = $record; push(@needsRules, $record); } elsif (/^ECPG:/)