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

Reply via email to