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