[gcc r17-2793] cobol: Accept newline in refmod pattern.

"James K. Lowden via Gcc-cvs" <[email protected]> Wed, 29 Jul 2026 19:56:05 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:4487236ddfbd387287ff217a40ae675511dcdb00

commit r17-2793-g4487236ddfbd387287ff217a40ae675511dcdb00
Author: James K. Lowden <[email protected]>
Date:   Wed Jul 29 14:00:41 2026 -0400

    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:
---
 gcc/cobol/parse.y     |   2 +-
 gcc/cobol/scan.l      |   2 +-
 gcc/cobol/scan_ante.h | 103 ++++++++++++++++++++++++++++++--------------------
 gcc/cobol/util.cc     |   2 +-
 4 files changed, 64 insertions(+), 45 deletions(-)

diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y
index b95a5e2e8fcd..d6cd656085bb 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 ee85883ebf88..67bdefa91dc8 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 fa55b9b17984..402600edabc7 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 <typename T>
+T * rsearch( T* a, T* z, T sarg ) {
+  std::reverse_iterator<T*> 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 9e0fb368d042..656ecfe09494 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