From 354953245b0c82c985642afa611fe25b80d5c69f Mon Sep 17 00:00:00 2001
From: "James K. Lowden" <jklowden@symas.com>
Date: Fri, 10 May 2024 17:49:46 -0400
Subject: [PATCH] CDF IF nesting seems to work

---
 gcc/cobol/cbldiag.h    |  4 +-
 gcc/cobol/cdf.y        | 39 ++++++++++++-----
 gcc/cobol/cdfval.h     | 32 +++++++++++---
 gcc/cobol/copybook.h   |  6 +--
 gcc/cobol/parse_ante.h |  8 ++--
 gcc/cobol/scan.l       | 18 ++++----
 gcc/cobol/scan_ante.h  | 99 +++++++++++++++++++++++++++++-------------
 gcc/cobol/scan_post.h  | 27 +++---------
 gcc/cobol/symbols.cc   | 18 +++++++-
 gcc/cobol/symbols.h    |  2 +-
 10 files changed, 164 insertions(+), 89 deletions(-)

diff --git a/gcc/cobol/cbldiag.h b/gcc/cobol/cbldiag.h
index 1eba92d856f7..bba6dc98ad8c 100644
--- a/gcc/cobol/cbldiag.h
+++ b/gcc/cobol/cbldiag.h
@@ -30,9 +30,11 @@
 
 #include <syslog.h>
 
+const char * cobol_filename();
+
 void yyerror( char const *s, int error_level = LOG_ERR);
 void yyerrorv( const char fmt[], ... );
-void yyerrorvl( int line, const char fmt[], ... );
+void yyerrorvl( int line, const char *filename, const char fmt[], ... );
 
 static inline void yywarn( char const *msg ) { yyerror( msg, LOG_WARNING ); }
 
diff --git a/gcc/cobol/cdf.y b/gcc/cobol/cdf.y
index e85053b90766..5e59a3cf4ea3 100644
--- a/gcc/cobol/cdf.y
+++ b/gcc/cobol/cdf.y
@@ -95,7 +95,7 @@ void ydferror( char const *s, int );
   cdfval_add( const char name[],
 	       const cdfval_t& value, bool override = false )
   {
-    if( scanner_lexing() ) {
+    if( scanner_parsing() ) {
       if( ! override ) {
 	if( dictionary.find(name) != dictionary.end() ) return false;
       }
@@ -105,7 +105,7 @@ void ydferror( char const *s, int );
   }
   static void
   cdfval_off( const char name[] ) {
-    if( scanner_lexing() ) {
+    if( scanner_parsing() ) {
       auto p = dictionary.find(name);
       if( p == dictionary.end() ) {
         dictionary[name] = cdfval_t();
@@ -335,19 +335,19 @@ strings:	LITERAL { (void)asprintf(&display_msg, "%s", $1); }
 
 partials:	partial
 		{
-		  if( ! scanner_lexing() ) YYACCEPT; 
+		  if( ! scanner_parsing() ) YYACCEPT; 
 		}
 	|	partials partial
 		{
-		  if( ! scanner_lexing() ) YYACCEPT; 
+		  if( ! scanner_parsing() ) YYACCEPT; 
 		}
 		;
 partial:	cdf_if            /* text */
-	|	CDF_ELSE          { scanner_lexing_toggle(); }
-	|	CDF_END_IF        { scanner_lexing_pop(); }
+	|	CDF_ELSE          { scanner_parsing_toggle(); }
+	|	CDF_END_IF        { scanner_parsing_pop(); }
 	|	cdf_evaluate      /* text */
 	|	cdf_eval_when     /* text */
-	|	CDF_END_EVALUATE  { scanner_lexing_pop(); }
+	|	CDF_END_EVALUATE  { scanner_parsing_pop(); }
 	;
 
 cdf_define:	CDF_DEFINE cdf_constant NAME as cdf_expr[value] override
@@ -357,10 +357,20 @@ cdf_define:	CDF_DEFINE cdf_constant NAME as cdf_expr[value] override
 		    YYERROR;
 		  }
 		  if( !cdfval_add( $NAME, cdfval_t($value), $override) ) {
-		    yyerrorv("error: name already dictionary: %s", $NAME);
+		    yyerrorv("error: name already in dictionary: %s", $NAME);
+		    const cdfval_t& entry = dictionary[$NAME];
+		    assert(entry.filename);
+		    yyerrorv("error: %s previously defined in %s:%d",
+		             $NAME, entry.filename, entry.lineno);
 		    YYERROR;
 		  }
 		}
+	|	CDF_DEFINE cdf_constant NAME '=' cdf_expr[value] override
+		{  /* accept, but as error */
+		  if( scanner_parsing() ) {
+		    yyerrorv("CDF syntax error: %s = value invalid", $NAME);
+		  }
+		}
 	|	CDF_DEFINE cdf_constant NAME as OFF
 		{
 		  cdfval_off( $NAME);
@@ -446,7 +456,12 @@ filename:       NAME
                 }
                 ;
 
-cdf_if:		CDF_IF cdf_cond_expr { scanner_lexing(YDF_CDF_IF, $2); }
+cdf_if:		CDF_IF cdf_cond_expr { scanner_parsing(YDF_CDF_IF, $2); }
+	|	CDF_IF error {
+		  ////if( scanner_parsing() ) yyerrok;
+		} CDF_END_IF { // not pushed, don't pop
+		  if( ! scanner_parsing() ) YYACCEPT;
+		} 
 		;
 
 cdf_evaluate:   CDF_EVALUATE cdf_expr
@@ -526,7 +541,11 @@ cdf_factor:     NAME {
 		  if( that != dictionary.end() ) {
 		    $$ = that->second;
 		  } else {
-		    yyerrorv("CDF syntax error: no such variable '%s'", $1);
+		    if( ! scanner_parsing() ) {
+		      yywarnv("CDF skipping: no such variable '%s'", $1);
+		    } else {
+		      yyerrorv("CDF syntax error: no such variable '%s'", $1);
+		    }
 		    $$ = cdfval_t();
 		  }
 		}
diff --git a/gcc/cobol/cdfval.h b/gcc/cobol/cdfval.h
index aa98e1bd53b4..27661ac60fd0 100644
--- a/gcc/cobol/cdfval.h
+++ b/gcc/cobol/cdfval.h
@@ -36,7 +36,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 
-bool scanner_lexing();
+bool scanner_parsing();
 
 struct cdfval_base_t {
   bool off;
@@ -49,33 +49,51 @@ struct cdf_arg_t {
   const char *string;
 };
 
+extern int yylineno;
+const char * cobol_filename();
+
 struct cdfval_t : public cdfval_base_t {
-  cdfval_t() {
+  int lineno;
+  const char *filename;
+  
+  cdfval_t()
+    : lineno(yylineno), filename(cobol_filename())
+  {
     cdfval_base_t::off  = false;
     cdfval_base_t::string = NULL;
     cdfval_base_t::number = 0;
   }
-  cdfval_t( const char value[] ) {
+  cdfval_t( const char value[] )
+    : lineno(yylineno), filename(cobol_filename())
+  {
     cdfval_base_t::off  = false;
     cdfval_base_t::string = value;
     cdfval_base_t::number = 0;
   }
-  cdfval_t( long long value ) {
+  cdfval_t( long long value )
+    : lineno(yylineno), filename(cobol_filename())
+  {
     cdfval_base_t::off  = false;
     cdfval_base_t::string = NULL;
     cdfval_base_t::number = value;
   }
-  cdfval_t( int64_t value ) {
+  cdfval_t( int64_t value )
+    : lineno(yylineno), filename(cobol_filename())
+  {
     cdfval_base_t::off  = false;
     cdfval_base_t::string = NULL;
     cdfval_base_t::number = value;
   }
-  cdfval_t( int value ) {
+  cdfval_t( int value )
+    : lineno(yylineno), filename(cobol_filename())
+  {
     cdfval_base_t::off  = false;
     cdfval_base_t::string = NULL;
     cdfval_base_t::number = value;
   }
-  cdfval_t( const cdfval_base_t& value ) {
+  cdfval_t( const cdfval_base_t& value )
+    : lineno(yylineno), filename(cobol_filename())
+  {
     cdfval_base_t *self(this);
     *self = value;
   }
diff --git a/gcc/cobol/copybook.h b/gcc/cobol/copybook.h
index 7e3d071f1582..a7d2e701a482 100644
--- a/gcc/cobol/copybook.h
+++ b/gcc/cobol/copybook.h
@@ -49,9 +49,9 @@ FILE * copy_mode_start();
 const char * cobol_filename();
 bool cobol_filename( const char *name, ino_t inode );
 
-void scanner_lexing( int token, bool tf );
-void scanner_lexing_toggle();
-void scanner_lexing_pop();
+void scanner_parsing( int token, bool tf );
+void scanner_parsing_toggle();
+void scanner_parsing_pop();
 
 /*
  * COPY support On encountering a COPY statement, the parser continues
diff --git a/gcc/cobol/parse_ante.h b/gcc/cobol/parse_ante.h
index 84788c6907ab..8467fa718960 100644
--- a/gcc/cobol/parse_ante.h
+++ b/gcc/cobol/parse_ante.h
@@ -140,9 +140,8 @@ yywarnv( const char fmt[], ... ) {
 }
 
 void
-yyerrorvl( int line, const char fmt[], ... ) {
+yyerrorvl( int line, const char *filename, const char fmt[], ... ) {
   ++nparse_error;
-  assert(line < yylineno);
 
   char *msg;
   va_list ap;
@@ -151,8 +150,9 @@ yyerrorvl( int line, const char fmt[], ... ) {
   (void)! vasprintf(&msg, fmt, ap);
   assert(msg);
 
-  fprintf( stderr, "%s:%d: %s\n",
-           basename(strdup(cobol_filename())), line, msg);
+  if( !filename ) filename = cobol_filename();
+
+  fprintf( stderr, "%s:%d: %s\n", filename, line, msg);
 
   free(msg);
   va_end(ap);
diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l
index aadaa889ab33..e30abfd7d35b 100644
--- a/gcc/cobol/scan.l
+++ b/gcc/cobol/scan.l
@@ -868,8 +868,8 @@ USE({SPC}FOR)?		{ return USE; }
 
 <field_level>{
   66/{SPC}(\f#)?{NAME} { yy_pop_state();
-                          if( !lexing.on() ) orig_picture[0] = '\0';
-                          if( need_level ) {
+                          if( !parsing.on() ) orig_picture[0] = '\0';
+                          if( level_needed() ) {
                             level_found();
                             yylval.number = level_of(yytext); return LEVEL66;
                           } else {
@@ -877,8 +877,8 @@ USE({SPC}FOR)?		{ return USE; }
                           }
                         }
   78/{SPC}(\f#)?{NAME} { yy_pop_state();
-                          if( !lexing.on() ) orig_picture[0] = '\0';
-                          if( need_level ) {
+                          if( !parsing.on() ) orig_picture[0] = '\0';
+                          if( level_needed() ) {
                             level_found();
                             yylval.number = level_of(yytext); return LEVEL78;
                           } else {
@@ -886,8 +886,8 @@ USE({SPC}FOR)?		{ return USE; }
                           }
                         }
   88/{SPC}(\f#)?{NAME} { yy_pop_state();
-                          if( !lexing.on() ) orig_picture[0] = '\0';
-                          if( need_level ) {
+                          if( !parsing.on() ) orig_picture[0] = '\0';
+                          if( level_needed() ) {
                             level_found();
                             yylval.number = level_of(yytext); return LEVEL88;
                           } else {
@@ -895,8 +895,8 @@ USE({SPC}FOR)?		{ return USE; }
                           }
                         }
   [[:digit:]]{1,2}/[[:space:]] { yy_pop_state();
-                          if( !lexing.on() ) orig_picture[0] = '\0';
-                          if( need_level ) {
+                          if( !parsing.on() ) orig_picture[0] = '\0';
+                          if( level_needed() ) {
                             level_found();
                             yylval.number = level_of(yytext); return LEVEL;
                           } else {
@@ -909,7 +909,7 @@ USE({SPC}FOR)?		{ return USE; }
 
 <field_state>{
   ^[[:blank:]]*[[:digit:]]{1,2}{OSPC}/[.] {
-                              if( !lexing.on() ) orig_picture[0] = '\0';
+                              if( !parsing.on() ) orig_picture[0] = '\0';
                               level_found();
                               yylval.number = level_of(yytext);
                               return LEVEL;
diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h
index ac15ec9a2e1b..3c9c583608dc 100644
--- a/gcc/cobol/scan_ante.h
+++ b/gcc/cobol/scan_ante.h
@@ -134,8 +134,8 @@ original_number( char input[] = NULL ) {
 }
 
 static bool need_level = true;
-static void level_found() { need_level = false; }
 
+// Used only by parser, so scanner_normal() obviously true. 
 void field_done() { orig_picture[0] = '\0'; need_level = true; }
 
 /*
@@ -231,17 +231,30 @@ null_trim( char name[] ) {
 static int final_token, penultimate_token;
 static int separator = '\0';
 
+static inline const char *
+boolalpha( bool tf ) { return tf? "True" : "False"; }
+
 struct cdf_status_t {
+  int lineno;
+  const char *filename;
   int  token;
-  bool lexing;
-  cdf_status_t( int token = 0, bool lexing = true )
-    : token(token), lexing(lexing)
+  bool parsing;
+  cdf_status_t( int token = 0, bool parsing = true )
+    : lineno(yylineno), filename(cobol_filename())
+    , token(token), parsing(parsing)
   {}
-  bool toggle() { return lexing = ! lexing; }
-};
+  bool toggle() { return parsing = ! parsing; }
 
-static inline const char *
-boolalpha( bool tf ) { return tf? "True" : "False"; }
+  const char * str() const {
+    static char line[132];
+    snprintf(line, sizeof(line), "%s:%d: %s, parsing %s",
+	     filename, lineno, keyword_str(token), boolalpha(parsing));
+    return line;
+  }
+  static const char * as_string( const cdf_status_t& status ) {
+    return status.str();
+  }
+};
 
 /*
  * Scanning status is true if tokens are being parsed and false if not (because
@@ -250,57 +263,81 @@ boolalpha( bool tf ) { return tf? "True" : "False"; }
  * false, then all of B is skipped, regardless of >>IF and >>ELSE for B.
 */
 
-static class lexing_status_t : public std::stack<cdf_status_t> {
+static class parsing_status_t : public std::stack<cdf_status_t> {
  public:
   int (*parser)(void) = yyparse;
 
   bool on() const { // true only if all true
-    bool lexing = std::all_of( c.begin(), c.end(),
-			       []( const auto& status ) { return status.lexing; } );
-    return lexing;
+    bool parsing = std::all_of( c.begin(), c.end(),
+			       []( const auto& status ) { return status.parsing; } );
+    return parsing;
   }
 
   bool feed_the_parser() const {
     return on() || parser == ydfparse;
   }
 
-} lexing;
+  void splat() const {
+    int i=0;
+    for( const auto& status : c ) {
+      warnx( "%4d\t%s", ++i, status.str() );
+    }
+  }
+
+} parsing;
 
 static int scanner_token() {
-  if( lexing.empty() ) {
+  if( parsing.empty() ) {
     yyerror("error: >>ELSE or >>END-IF without >>IF");
     return NO_CONDITION;
   }
-  return lexing.top().token;
+  return parsing.top().token;
 }
 
-bool scanner_lexing() { return lexing.on(); }
-void scanner_lexing( int token, bool tf ) {
-  lexing.push( cdf_status_t(token, tf) );
-  if( yydebug )
-    yywarnv("%s @ %d: %s: scanning now %s, depth %zu", __func__, yylineno,
-	    keyword_str(token), boolalpha(lexing.on()), lexing.size());
+bool scanner_parsing() { return parsing.on(); }
+bool scanner_normal()  { return parsing.on() && parsing.parser == yyparse; }
+
+void scanner_parsing( int token, bool tf ) {
+  parsing.push( cdf_status_t(token, tf) );
+  if( yydebug ) {
+    yywarnv("%10s: parsing now %5s, depth %zu", 
+	    keyword_str(token), boolalpha(parsing.on()), parsing.size());
+    parsing.splat();
+  }
 }
-void scanner_lexing_toggle() {
-  if( lexing.empty() ) {
+void scanner_parsing_toggle() {
+  if( parsing.empty() ) {
     yyerror("error: >>ELSE without >>IF");
     return;
   }
-  lexing.top().toggle();
-  if( yydebug ) yywarnv("%s @ %d: scanning now %s", __func__, yylineno,
-			boolalpha(lexing.on()));
+  parsing.top().toggle();
+  if( yydebug ) {
+    yywarnv("%10s: parsing now %5s",
+	    keyword_str(CDF_ELSE), boolalpha(parsing.on()));
+  }
 }
-void scanner_lexing_pop() {
-  if( lexing.empty() ) {
+void scanner_parsing_pop() {
+  if( parsing.empty() ) {
     yyerror("error: >>END-IF without >>IF");
     return;
   }
-  lexing.pop();
-  if( yydebug ) yywarnv("%s @ %d: scanning now %s, depth %zu", __func__, yylineno,
-			boolalpha(lexing.on()), lexing.size());
+  parsing.pop();
+  if( yydebug ) {
+    yywarnv("%10s: parsing now %5s, depth %zu", 
+	    keyword_str(CDF_END_IF), boolalpha(parsing.on()), parsing.size());
+    parsing.splat();
+  }
 }
 
 
+static bool level_needed() {
+  return scanner_normal() && need_level;
+}
+
+static void level_found() {
+  if( scanner_normal() ) need_level = false;
+}
+
 /*
  * The penultimate token is used to identify paragraph names in the presence of
  * directives, which are logical whitespace.  It is the next-to-last token
diff --git a/gcc/cobol/scan_post.h b/gcc/cobol/scan_post.h
index 47e221daf1ee..fed531939072 100644
--- a/gcc/cobol/scan_post.h
+++ b/gcc/cobol/scan_post.h
@@ -155,33 +155,17 @@ run_cdf( int token ) {
   // Parse the CDF directive.
 
   int (*parser)(void) = ydfparse;
-  std::swap(parser, lexing.parser);
+  std::swap(parser, parsing.parser);
 
   int erc = ydfparse();
 
-  std::swap(parser, lexing.parser);
+  std::swap(parser, parsing.parser);
 
   return  0 == erc;
 }
 
 int next_token() {
   int token = lexer();
-
-  switch(token) {
-  case CDF_ELSE:
-    if( valid_conditional_context(token) ) {
-      scanner_lexing_toggle();
-      token = next_token();
-    }
-    break;
-  case CDF_END_IF:
-  case CDF_END_EVALUATE:
-    if( valid_conditional_context(token) ) {
-      scanner_lexing_pop();
-      token = next_token();
-    }
-    break;
-  }
   return token;
 }
 
@@ -213,7 +197,6 @@ prelex() {
     }
     token = ydfchar > 0? final_token : next_token();
     // re-enter cdf parser if next token is a CDF token
-    if( ! lexing.on() ) break;
   }
   
   if( yydebug ) warnx( ">>CDF parser done, returning "
@@ -275,16 +258,16 @@ yylex(void) {
   do {
     token = prelex();
     if( yy_flex_debug ) {
-      if( lexing.parser == ydfparse ) {
+      if( parsing.parser == ydfparse ) {
 	warnx( "%s:%d: routing %s to CDF parser", __func__, __LINE__,
 	       keyword_str(token) );
-      } else if( !lexing.on() ) {
+      } else if( !parsing.on() ) {
 	yywarnv( "eating %s because conditional compilatiion is FALSE",
 		 keyword_str(token) );
       }
     }
 
-  } while( token && ! lexing.feed_the_parser() );
+  } while( token && ! parsing.feed_the_parser() );
 
   if( next_sentence && token == '.' ) {
     produce_next_sentence_target = true;
diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc
index 886e502696f5..89bad6b69126 100644
--- a/gcc/cobol/symbols.cc
+++ b/gcc/cobol/symbols.cc
@@ -1987,6 +1987,19 @@ had_picture( const cbl_field_t *field ) {
 /*
  * When adding a symbol, set the parent as an offset into the symbol table.
  */
+static symbol_elem_t *
+symbol_in_file( symbol_elem_t *e ) {
+
+  auto beg = std::reverse_iterator<symbol_elem_t *>(e);
+  auto end = std::reverse_iterator<symbol_elem_t *>(symbols_begin());
+  auto p = std::find_if( beg, end,
+                         []( const symbol_elem_t& elem ) {
+                           return elem.type == SymFilename; 
+                         } );
+
+  return p != end? &*p : NULL;
+}
+
 static struct cbl_field_t *
 symbol_field_parent_set( struct cbl_field_t *field )
 {
@@ -2019,7 +2032,10 @@ symbol_field_parent_set( struct cbl_field_t *field )
       field->parent = e - symbols.elems;
       if( 1 < field->level && field->level < 50 ) {
         if( had_picture(prior) ) {
-          yyerrorvl(prior->line, "error: group %s cannot have PICTURE clause", prior->name);
+          auto efile = symbol_in_file(e);
+          const char *filename = efile? efile->elem.filename : NULL;
+          yyerrorvl(prior->line, filename, 
+                    "error: group %s cannot have PICTURE clause", prior->name);
           return NULL;
         }
 	prior->type = FldGroup;
diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h
index d20f18598486..0705efac08c2 100644
--- a/gcc/cobol/symbols.h
+++ b/gcc/cobol/symbols.h
@@ -393,7 +393,7 @@ const char * day4_is_now(void);
 const char * time_is_now(void);
 
 void yyerrorv( const char fmt[], ... );
-void yyerrorvl( int line, const char fmt[], ... );
+void yyerrorvl( int line, const char *filename, const char fmt[], ... );
 void yywarnv( const char fmt[], ... );
 
 struct cbl_upsi_mask_t {
-- 
GitLab