From: James K. Lowden Date: Wed, 29 Jul 2026 18:00:41 +0000 (-0400) Subject: cobol: Accept newline in refmod pattern. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4487236ddfbd387287ff217a40ae675511dcdb00;p=thirdparty%2Fgcc.git cobol: Accept newline in refmod pattern. Modify lexer recognition of refmods and correct errors in is_refmod() function. gcc/cobol/ChangeLog: * parse.y: Report LPAREN token as '(', not ')'. * scan.l: Remove newline exclusion from LPAREN pattern. * scan_ante.h (rsearch): Helper function to ensure c++11 compatibility. (trim_location): Use rsearch function. (is_quote): New inline function to test quotiness. (skip_string): New function to find end of string literal. (is_refmod): Stay in bounds. * util.cc (gcc_location_set): Decrease debug message verbosity. --- diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y index b95a5e2e8fc..d6cd656085b 100644 --- a/gcc/cobol/parse.y +++ b/gcc/cobol/parse.y @@ -579,7 +579,7 @@ class locale_tgt_t { LOWER_CASE "LOWER-CASE" LOW_VALUES "LOW-VALUES" LOWEST_ALGEBRAIC "LOWEST-ALGEBRAIC" - LPAREN " )" + LPAREN " (" MANUAL MAXX "Max" MEAN MEDIAN MIDRANGE MINN "Min" MULTIPLE MOD MODE diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l index ee85883ebf8..67bdefa91dc 100644 --- a/gcc/cobol/scan.l +++ b/gcc/cobol/scan.l @@ -1915,7 +1915,7 @@ USE({SPC}FOR)? { return USE; } [(:)] { return *yytext; } [(]/[^(:)""'']*[:][^)]*[)] { return LPAREN; /* parentheses around a colon */ } - [(][^:""''\n]*[:][^)]*[)] { // does not match foo(bar)\n: :-( + [(][^:""'']*[:][^)]*[)] { // does not match foo(bar)\n: :-( int tok = is_refmod(yytext, yytext + yyleng)? int(LPAREN) : '('; myless(1); diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h index fa55b9b1798..402600edabc 100644 --- a/gcc/cobol/scan_ante.h +++ b/gcc/cobol/scan_ante.h @@ -501,6 +501,13 @@ reset_location() { #define YY_USER_ACTION update_location(); +template +T * rsearch( T* a, T* z, T sarg ) { + std::reverse_iterator beg(z), end(a); + auto p = std::find(beg, end, sarg); + return p != end? p.base() : nullptr; +} + /* * Before calling yyless to tell the generated scanner to rescan nkeep * characters, set the scanner's location to reflect the cbl_loc_t of what @@ -524,12 +531,9 @@ trim_location( int nkeep) { yylloc.last_column = yylloc.first_column + nkeep; } else { auto eokeep = yytext + nkeep; - std::reverse_iterator beg(eokeep); - std::reverse_iterator end(yytext); - auto nl = std::find(beg, end, '\n'); - gcc_assert( nl != end ); - gcc_assert( nl.base() != yytext ); - yylloc.last_column = 1 + (eokeep - nl.base()); + auto nl = rsearch(yytext, eokeep, '\n'); + gcc_assert( nl != nullptr ); + yylloc.last_column = 1 + (eokeep - nl); } gcc_assert( yylloc.first_line <= yylloc.last_line ); @@ -1313,6 +1317,27 @@ integer_of( const char input[], bool is_hex = false) { return output; } +static inline bool is_quote( const char ch ) { + return ch == '\'' || ch == '"'; +} + +static const char* +skip_string(const char* p, const char* pend, char delimiter) { + p++; // Skip opening delimiter + while (p < pend) { + if (p[0] == delimiter) { + if (p[1] == delimiter) { + p += 2; // doubled delimiter is escaped + } else { + return ++p; // Found valid closing delimiter + } + } else { + p++; + } + } + return pend; +} + /* * Loosely parse what might be a refmod expression. This is used to decide * whether to indicate a refmod to the parser with an LPAREN token, or not, @@ -1325,40 +1350,34 @@ integer_of( const char input[], bool is_hex = false) { */ static bool is_refmod( const char input[], const char enput[] ) { - if( input == enput || *input != '(' ) return false; - int depth = 0; - bool colon_at_depth1 = false; - const char *p = input; - - while( p < enput ) { - char ch = *p++; - if( ch == '"' || ch == '\'' ) { - /* Skip quoted region; doubled quote is escape. */ - const char quote = ch; - while( p < enput ) { - ch = *p++; - if( ch == quote ) { - if( p < enput && *p == quote ) { p++; continue; } - break; - } - } - continue; - } - if( ch == '(' ) { - depth++; - continue; - } - if( ch == ')' ) { - depth--; - if( depth < 0 ) return false; - if( depth == 0 ) return colon_at_depth1; - continue; - } - if( ch == ':' && depth == 1 ) { - if( colon_at_depth1 ) return false; - colon_at_depth1 = true; - continue; - } - } - return false; + if( input == enput ) return false; + gcc_assert( *input == '(' ); + int depth = 1; + bool colon_at_depth1 = false; + + for( const char *p = input + 1; p < enput; p++ ) { + char ch = *p; + if( is_quote(ch) ) { + p = skip_string(p, enput, ch) - 1; + continue; + } + if( ch == '(' ) { + depth++; + continue; + } + if( ch == ')' ) { + depth--; + if( depth < 0 ) return false; + if( depth == 0 ) return colon_at_depth1; + continue; + } + if( ch == ':' && depth == 1 ) { + if( colon_at_depth1 ) return false; + colon_at_depth1 = true; + continue; + } + } + dbgmsg("%s:%d: '%.*s' is %sa refmod", __func__, __LINE__, + int(enput - input), input, colon_at_depth1? "" : "not "); + return colon_at_depth1; } diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc index 9e0fb368d04..656ecfe0949 100644 --- a/gcc/cobol/util.cc +++ b/gcc/cobol/util.cc @@ -3522,7 +3522,7 @@ gcc_location_set( const cbl_loc_t& loc ) { loc_m_1 = token_location; } - location_dump(__func__, __LINE__, "parser", loc, true); + location_dump(__func__, __LINE__, "parser", loc); } #ifdef NDEBUG