From e18247c1d7d435bed9cf8815830f1f9468529a2e Mon Sep 17 00:00:00 2001
From: "James K. Lowden" <jklowden@symas.com>
Date: Wed, 10 Apr 2024 14:26:52 -0400
Subject: [PATCH] accept CONSTANT for date function argument

---
 gcc/cobol/parse.y    |  4 ++++
 gcc/cobol/scan.l     | 26 +++++++++++++++++++--
 gcc/cobol/symbols.cc | 30 +-----------------------
 gcc/cobol/symbols.h  |  3 ++-
 gcc/cobol/util.cc    | 54 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 85 insertions(+), 32 deletions(-)

diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y
index 783bddc9c497..783f01343fc8 100644
--- a/gcc/cobol/parse.y
+++ b/gcc/cobol/parse.y
@@ -3879,6 +3879,10 @@ type_clause: TYPE to typename
 typedef_clause: is TYPEDEF strong 
 		{
                   cbl_field_t *field = current_field();
+		  if( field->level != 1 ) {
+		    yyerrorv("error: %02d %s IS TYPEDEF must be level 01",
+			     field->level, field->name);
+		  }
 		  field->attr |= typedef_e;
 		  if( $strong ) field->attr |= strongdef_e;
 		  yywarn("warning: TYPEDEF is provisional");
diff --git a/gcc/cobol/scan.l b/gcc/cobol/scan.l
index 040d79c208a6..a84b366a7f5e 100644
--- a/gcc/cobol/scan.l
+++ b/gcc/cobol/scan.l
@@ -1569,8 +1569,30 @@ USE({SPC}FOR)?		{ return USE; }
 				  pop_return TIME_FMT; }
 
   {SPC}				// ignore
-  {NAME}			{ yyerror("error: format must be literal"); 
-				  pop_return NO_CONDITION; }
+  {NAME}			{
+				  int token = NO_CONDITION;
+				  char type = 0;
+				  auto elem = symbol_field(PROGRAM, 0, yytext);
+
+				  if( elem->type == SymField ) {
+				    auto f = cbl_field_of(elem);
+				    if( f->type == FldLiteralA && f->has_attr(constant_e) ) {
+				      type = date_time_fmt(f->data.initial);
+				      yylval.string = strdup(f->data.initial);
+				    }
+				  }
+				  switch(type) {
+				  case 'D': token = DATETIME_FMT; break;
+				  case 'd': token = DATE_FMT; break;
+				  case 't': token = TIME_FMT; break;
+				  default:
+				    yyerror("error: format must be literal"); 
+				    pop_return token;
+				    break;
+				  }
+				  pop_return token;
+				}
+
   . 				{ yyless(0); pop_return NO_CONDITION; }
 }
 
diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc
index 03205d82fc55..81798fc25315 100644
--- a/gcc/cobol/symbols.cc
+++ b/gcc/cobol/symbols.cc
@@ -1795,40 +1795,11 @@ symbols_update( size_t first, bool parsed_ok ) {
     if( field->level == 0 && field->is_key_name() ) continue;
     if( is_literal(field) && field->var_decl_node != NULL ) continue;
 
-#if 1
     if( field->is_typedef() ) {
       auto isym = end_of_group( symbol_index(p) );
       p = symbol_at(--isym);
       continue;
     }
-#else
-    if( 1 < field->level && field->is_typedef() ) {
-      if( field->parent ) {
-        auto e = symbol_at(field->parent);
-        if( e->type == SymField ) {
-          auto parent = cbl_field_of(e);
-          p = end_of_group( parent, field );
-          p--;
-          continue;
-        }
-        warnx("%s:%s: odd: %s is TYPEDEF and has non-field parent %s",
-              __func__, __LINE__, field->name, symbol_type_str(e->type));
-      }
-      p = std::find(++p, symbols_end(),
-                    []( auto& e ) {
-                      if( e->type == SymField ) {
-                        auto f = cbl_field_of(e);
-                        switch( f->level ) {
-                        case 1: case 66: case 77: case 88:
-                          return true;
-                        }
-                      }
-                      return false;
-                    } );
-      p--;
-      continue;
-    }
-#endif
     
     // Verify REDEFINing field has no ODO components
     auto parent = symbol_redefines(field);
@@ -1845,6 +1816,7 @@ symbols_update( size_t first, bool parsed_ok ) {
       continue;
     }
 
+    assert( ! field->is_typedef() );
     if( parsed_ok ) parser_symbol_add(field);
   }
 
diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h
index 0f0c8495e3d2..bd7f26b0cfc3 100644
--- a/gcc/cobol/symbols.h
+++ b/gcc/cobol/symbols.h
@@ -2082,11 +2082,12 @@ const char * cobol_lineno_save();
 char *cobol_name_mangler(const char *cobol_name);
 
 bool is_elementary( enum cbl_field_type_t type );
-
 bool is_numeric_edited( const char picture[] );
 
 const char * intrinsic_function_name( int token );
 
+char date_time_fmt( const char input[] );
+
 size_t current_program_index();
 const char * current_declarative_section_name();
 
diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc
index f3fea330f316..dfbbc7a89020 100644
--- a/gcc/cobol/util.cc
+++ b/gcc/cobol/util.cc
@@ -1840,6 +1840,60 @@ name_space_kw( const char input[], int *token ) {
   return keyword_of(candidate)? len1 : 0; // 1st is NAME, 2nd is keyword
 }
 
+char
+date_time_fmt( const char input[] ) {
+  if( ! input ) return 0;
+
+#define DATE_FMT_B  "(YYYYMMDD|YYYYDDD|YYYYWwwD)"
+#define DATE_FMT_E  "(YYYY-MM-DD|YYYY-DDD|YYYY-Www-D)"
+#define TIME_FMT1   "hhmmss([.,]s+)?"
+#define TIME_FMT3   "hhmmss([.,]s+)?Z"
+#define TIME_FMT5   "hhmmss([.,]s+)?[+]hhmm"
+#define TIME_FMT2   "hh:mm:ss([.,]s+)?"
+#define TIME_FMT4   "hh:mm:ss([.,]s+)?Z"
+#define TIME_FMT6   "hh:mm:ss([.,]s+)?[+]hh:mm"
+
+#define TIME_FMT_B  "(" TIME_FMT1 "|" TIME_FMT3 "|"  TIME_FMT5 ")"
+#define TIME_FMT_E  "(" TIME_FMT2 "|" TIME_FMT4 "|"  TIME_FMT6 ")"
+
+  static bool compiled = false;
+  static struct fmts_t {
+    regex_t reg; char type; char pattern[256];
+  } fmts[] = {
+    { regex_t(), 'D', "^((" DATE_FMT_B "T" TIME_FMT_B ")|("
+                            DATE_FMT_E "T" TIME_FMT_E "))$" }, 
+    { regex_t(), 'd', "^(" DATE_FMT_B "|" DATE_FMT_E ")$" },
+    { regex_t(), 't', "^(" TIME_FMT_B "|" TIME_FMT_E ")$" },
+  };
+  int erc, cflags = REG_EXTENDED | REG_ICASE, eflags=0;
+  regmatch_t m[5];
+  char result = 0;
+
+  if( ! compiled ) {
+    for( auto& fmt : fmts ) {
+      ////warnx( "%s: %c, %s", __func__, fmt.type, fmt.pattern );
+      if( (erc = regcomp(&fmt.reg, fmt.pattern, cflags)) != 0 ) {
+        char msg[80];
+        regerror(erc, &fmt.reg, msg, sizeof(msg));
+        errx( EXIT_FAILURE, "%s: regcomp: %s", __func__, msg );
+      }
+    }
+    compiled = true;
+  }
+
+  ////warnx("%s: input '%s'", __func__, input);
+  for( auto& fmt : fmts ) {
+    if( 0 == regexec(&fmt.reg, input, COUNT_OF(m), m, eflags) ) {
+      result = fmt.type;
+      break;
+    }
+  }
+
+  return result;
+}
+
+
+
 /*
  * Development suppport
  */
-- 
GitLab