/* * Copyright (c) 2021-2025 Symas Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of the Symas Corporation nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SHOW_PARSE_H_ #define SHOW_PARSE_H_ // These macros provide information about what the compiler is doing, // and about what the compiled code is doing. // SHOW_PARSE gives information when parser_xxx functions are entered, and // then attempts to give as much information as it can at compile time about // variables and their characteristics, the contents of literals, and such. It // doesn't affect the executable at all. // TRACE1 lays down code for run-time tracing. // SHOW_PARSE must be followed by a bracketed set of instructions, no semicolon // This construction isn't really necessary; getenv() apparently runs pretty // fast. But using makes compiling a large number of programs just perceptably // quicker. So, I am using it; it's cheap. extern bool bSHOW_PARSE; extern bool show_parse_sol; extern int show_parse_indent; extern char const *bTRACE1; extern tree trace_handle; extern tree trace_indent; extern bool cursor_at_sol; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #define RETURN_IF_PARSE_ONLY \ do { if( mode_syntax_only() ) return; } while(0) #define SHOW_PARSE1 if(bSHOW_PARSE) #define SHOW_PARSE RETURN_IF_PARSE_ONLY; if(bSHOW_PARSE) // _HEADER and _END are generally the first and last things inside the // SHOW_PARSE statement. They don't have to be; SHOW_PARSE can be used // anywhere #define SHOW_PARSE_HEADER do \ { \ if(!show_parse_sol){fprintf(stderr, "\n");} \ show_parse_indent=fprintf(stderr, \ "( %d ) %s():" , \ (CURRENT_LINE_NUMBER), __func__); \ show_parse_sol=false; \ }while(0); #define SHOW_PARSE_END do{fprintf(stderr, "\n");show_parse_sol=true;}while(0); // This does one simple text string #define SHOW_PARSE_TEXT(a) do \ { \ fprintf(stderr, "%s", a); \ show_parse_sol=false; \ }while(0); #define SHOW_PARSE_INDENT do{ \ if(!show_parse_sol){fprintf(stderr, "\n");} \ for(int i=0; i<show_parse_indent-1; i++) \ {fprintf(stderr, " ");} \ fprintf(stderr, ": "); \ show_parse_sol=false; \ }while(0); // This does three simple text strings. #define SHOW_PARSE_TEXT_AB(pre, a, post) do \ { \ SHOW_PARSE_TEXT(pre);SHOW_PARSE_TEXT(a);SHOW_PARSE_TEXT(post) \ }while(0); // #define SHOW_PARSE_FIELD(pre, b) \ do \ { \ fprintf(stderr, "%s", pre); \ if( !(b) ) \ { \ fprintf(stderr, "parameter " #b " is NULL"); \ } \ else \ { \ fprintf(stderr, "%s", (b)->name); \ if( (b)->type == FldLiteralA || (b)->type == FldLiteralN ) \ { \ fprintf(stderr, " \"%s\"", (b)->data.initial); \ } \ else \ { \ fprintf(stderr, "<%s>", cbl_field_type_str((b)->type)); \ } \ } \ show_parse_sol = false; \ } while(0); #define SHOW_PARSE_REF(pre, b) \ do \ { \ fprintf(stderr, "%s", pre); \ if( !(b).field ) \ { \ fprintf(stderr, "parameter " #b".field is NULL"); \ } \ else \ { \ fprintf(stderr, "%s", (b).field->name); \ if( (b).field->type == FldLiteralA || (b).field->type == FldLiteralN ) \ { \ fprintf(stderr, " \"%s\"", (b).field->data.initial); \ } \ else \ { \ fprintf(stderr, "<%s>", cbl_field_type_str((b).field->type)); \ } \ } \ if( (b).nsubscript) \ { \ fprintf(stderr,"("); \ for(size_t jjj=0; jjj<(b).nsubscript; jjj++) \ { \ if(jjj) \ { \ SHOW_PARSE_FIELD(" ", (b).subscripts[jjj].field) \ } \ else \ { \ SHOW_PARSE_FIELD("", (b).subscripts[jjj].field) \ } \ } \ fprintf(stderr,")"); \ } \ show_parse_sol = false; \ } while(0); #define SHOW_PARSE_LABEL(a, b) \ do \ { \ fprintf(stderr, "%s", a); \ if( !b ) \ { \ fprintf(stderr, "label " #b " is NULL"); \ } \ else \ { \ fprintf(stderr, " %p:%s (%s)", (void*)b, b->name, b->type_str()); \ } \ show_parse_sol = false; \ } while(0); #define TRACE1 if(bTRACE1) #define TRACE1_HEADER do \ { \ if(!cursor_at_sol){gg_fprintf(trace_handle , 0, "\n");} \ gg_assign(trace_indent, \ gg_fprintf( trace_handle , \ 2, \ ">>>>>>( %d )(%s) ", \ build_int_cst_type(INT, CURRENT_LINE_NUMBER), \ gg_string_literal(__func__))); \ }while(0); #define TRACE1_INDENT do{ \ if(!cursor_at_sol){gg_fprintf(trace_handle , 0, "\n");} \ tree counter = gg_define_int(); \ gg_assign(counter, integer_zero_node); \ WHILE(counter, lt_op, trace_indent) \ gg_fprintf(trace_handle , 0, " "); \ gg_increment(counter); \ WEND \ }while(0); #define TRACE1_END do{gg_fprintf(trace_handle, 0, "\n");cursor_at_sol=true;}while(0); #define TRACE1_TEXT(a) do{cursor_at_sol=false;gg_fprintf(trace_handle, 1, "%s", gg_string_literal(a));}while(0); #define TRACE1_TEXT_ABC(a,b,c) do{TRACE1_TEXT(a);TRACE1_TEXT(b);TRACE1_TEXT(c)}while(0); #define TRACE1_FIELD_VALUE(a, field, b) \ do \ { \ cursor_at_sol=false; \ if ( field->type == FldConditional ) \ { \ gg_fprintf(trace_handle, 1, "%s \"", gg_string_literal(a)); \ parser_display_internal_field(trace_handle, field, false); \ gg_fprintf(trace_handle, 1, "\" %s", gg_string_literal(b)); \ } \ else \ { \ IF( member(field->var_decl_node, "data"), eq_op, gg_cast(UCHAR_P, null_pointer_node) ) \ { \ gg_fprintf(trace_handle, 1, "%s ", gg_string_literal(a)); \ gg_fprintf(trace_handle, 0, "NULL"); \ gg_fprintf(trace_handle, 1, " %s", gg_string_literal(b)); \ } \ ELSE \ { \ if( field->type == FldGroup \ || field->type == FldAlphanumeric \ || field->type == FldAlphaEdited \ || field->type == FldLiteralA ) \ { \ gg_fprintf(trace_handle, 1, "%s \"", gg_string_literal(a)); \ parser_display_internal_field(trace_handle, field, false); \ gg_fprintf(trace_handle, 1, "\" %s", gg_string_literal(b)); \ } \ else \ { \ gg_fprintf(trace_handle, 1, "%s [", gg_string_literal(a)); \ parser_display_internal_field(trace_handle, field, false); \ gg_fprintf(trace_handle, 1, "] %s", gg_string_literal(b)); \ } \ } \ ENDIF \ } \ }while(0); #define TRACE1_REFER_VALUE(a, refer, b) \ do \ { \ if( refer.field ) \ { \ cursor_at_sol=false; \ IF( member(refer.field->var_decl_node, "data"), eq_op, gg_cast(UCHAR_P, null_pointer_node) ) \ { \ gg_fprintf(trace_handle, 1, "%s ", gg_string_literal(a)); \ gg_fprintf(trace_handle, 0, "NULL"); \ gg_fprintf(trace_handle, 1, " %s", gg_string_literal(b)); \ } \ ELSE \ { \ if( refer.field->type == FldGroup \ || refer.field->type == FldAlphanumeric \ || refer.field->type == FldAlphaEdited \ || refer.field->type == FldLiteralA ) \ { \ gg_fprintf(trace_handle, 1, "%s \"", gg_string_literal(a)); \ parser_display_internal(trace_handle, refer, false); \ gg_fprintf(trace_handle, 1, "\" %s", gg_string_literal(b)); \ } \ else \ { \ gg_fprintf(trace_handle, 1, "%s [", gg_string_literal(a)); \ parser_display_internal(trace_handle, refer, false); \ gg_fprintf(trace_handle, 1, "] %s", gg_string_literal(b)); \ } \ } \ ENDIF \ } \ else \ { \ gg_fprintf(trace_handle, 0, "refer.field is NULL"); \ } \ }while(0); #define TRACE1_FIELD_INFO(pre, b) \ do{ \ cursor_at_sol=false; \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal(pre)); \ if( !b ) \ { \ gg_fprintf(trace_handle, 0, "field " #b " is NULL"); \ } \ else \ { \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal(b->name)); \ gg_fprintf(trace_handle, 1, " (%s", gg_string_literal(cbl_field_type_str((b)->type))); \ if( b->type != FldLiteralN && b->type != FldConditional ) \ { \ cbl_field_t* B(b); \ if( !b->var_decl_node ) \ { \ gg_fprintf(trace_handle, 0, #b "->var_decl_node is NULL", NULL_TREE); \ } \ else \ { \ gg_fprintf(trace_handle, 1, " attr 0x%lx", member(B, "attr" )); \ gg_fprintf(trace_handle, 1, " c:o:d:r %ld", member(B, "capacity")); \ gg_fprintf(trace_handle, 1, ":%ld", member(B, "offset" )); \ gg_fprintf(trace_handle, 1, ":%d", gg_cast(INT, (member(B, "digits" )))); \ gg_fprintf(trace_handle, 1, ":%d", gg_cast(INT, (member(B, "rdigits" )))); \ } \ } \ else if( b->type == FldLiteralN ) \ { \ gg_fprintf(trace_handle, 1, " attr 0x%lx", build_int_cst_type(SIZE_T, b->attr)); \ gg_fprintf(trace_handle, 1, " c:o:d:r %ld", build_int_cst_type(SIZE_T, b->data.capacity)); \ gg_fprintf(trace_handle, 1, ":%ld", build_int_cst_type(SIZE_T, b->offset)); \ gg_fprintf(trace_handle, 1, ":%d", build_int_cst_type(INT, b->data.digits)); \ gg_fprintf(trace_handle, 1, ":%d", build_int_cst_type(INT, b->data.rdigits)); \ } \ gg_fprintf(trace_handle, 0, ")"); \ } \ }while(0); #define TRACE1_REFER_INFO(pre, b) \ do{ \ cursor_at_sol=false; \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal(pre)); \ if( !(b).field ) \ { \ gg_fprintf(trace_handle, 0, #b ".field is NULL"); \ } \ else \ { \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal( (b).field->name ? (b).field->name:"")); \ if( b.nsubscript ) \ { \ gg_fprintf(trace_handle, 0, "("); \ for(unsigned int i=0; i<b.nsubscript; i++) \ { \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal( b.subscripts[i].field->name ? b.subscripts[i].field->name : "" )); \ if( i<b.nsubscript-1 ) \ { \ gg_fprintf(trace_handle, 0, " "); \ } \ } \ if( b.refmod.from || b.refmod.len ) \ { \ gg_fprintf(trace_handle, 0, "("); \ if( b.refmod.from ) \ { \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal( b.refmod.from->name() ? b.refmod.from->name() : "" )); \ } \ gg_fprintf(trace_handle, 0, ":"); \ if( b.refmod.len ) \ { \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal( b.refmod.len->name() ? b.refmod.len->name() : "" )); \ } \ gg_fprintf(trace_handle, 0, "("); \ } \ gg_fprintf(trace_handle, 0, ")"); \ } \ gg_fprintf(trace_handle, 1, " (%s", gg_string_literal(cbl_field_type_str((b).field->type))); \ if( (b).field->type != FldLiteralN && (b).field->type != FldConditional ) \ { \ if( !(b).field->var_decl_node ) \ { \ gg_fprintf(trace_handle, 0, #b ".field->var_decl_node is NULL", NULL_TREE); \ } \ else \ { \ gg_fprintf(trace_handle, 1, " attr 0x%lx", member(b.field, "attr" )); \ gg_fprintf(trace_handle, 1, " c:o:d:r %ld", member(b.field, "capacity")); \ gg_fprintf(trace_handle, 1, ":%ld", member(b.field, "offset" )); \ gg_fprintf(trace_handle, 1, ":%d", gg_cast(INT, (member(b.field, "digits" )))); \ gg_fprintf(trace_handle, 1, ":%d)", gg_cast(INT, (member(b.field, "rdigits" )))); \ } \ } \ else if( (b).field->type == FldLiteralN ) \ { \ gg_fprintf(trace_handle, 1, " attr 0x%lx", build_int_cst_type(SIZE_T, (b).field->attr)); \ gg_fprintf(trace_handle, 1, " c:o:d:r %ld", build_int_cst_type(SIZE_T, (b).field->data.capacity)); \ gg_fprintf(trace_handle, 1, ":%ld", build_int_cst_type(SIZE_T, (b).field->offset)); \ gg_fprintf(trace_handle, 1, ":%d", build_int_cst_type(INT, (b).field->data.digits)); \ gg_fprintf(trace_handle, 1, ":%d)", build_int_cst_type(INT, (b).field->data.rdigits)); \ } \ } \ }while(0); #define TRACE1_FIELD(a, b, c) \ do{ \ TRACE1_FIELD_INFO(a, b) \ TRACE1_FIELD_VALUE("", b, c) \ }while(0); #define TRACE1_REFER(a, b, c) \ do{ \ TRACE1_REFER_INFO(a, b) \ TRACE1_REFER_VALUE("", b, c) \ }while(0); #define TRACE1_LABEL(a, b, c) \ do{ \ cursor_at_sol=false; \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal(a)); \ if( !b ) \ { \ gg_fprintf(trace_handle, 0, "label " #b " is NULL"); \ } \ else \ { \ gg_fprintf(trace_handle, 2, \ "%s (%s)", \ gg_string_literal(b->name), \ gg_string_literal(b->type_str()), \ NULL_TREE); \ } \ gg_fprintf(trace_handle, 1, "%s", gg_string_literal(c)); \ } while(0); // Use CHECK_FIELD when a should be non-null, and a->var_decl_node also should // by non-null: #define CHECK_FIELD(a) \ do{ \ if(!a) \ { \ yywarn("%s(): parameter " #a " is NULL", __func__); \ gcc_unreachable(); \ } \ if( !a->var_decl_node && a->type != FldConditional && a->type != FldLiteralA) \ { \ yywarn("%s() parameter " #a " is variable %s<%s> with NULL var_decl_node", \ __func__, \ a->name, \ cbl_field_type_str(a->type) ); \ gcc_unreachable(); \ } \ }while(0); #define CHECK_LABEL(a) \ do{ \ if(!a) \ { \ yywarn("%s(): parameter " #a " is NULL", __func__); \ gcc_unreachable(); \ } \ }while(0); #ifdef INCORPORATE_ANALYZER // The analyzer requires a C++17 compiler because of the inline static variable class ANALYZE { private: const char *func; int level; inline static int analyze_level=1; public: ANALYZE(const char *func_) : func(func_) { level = 0; if( getenv("Analyze") ) { level = analyze_level++; char ach[128]; snprintf(ach, sizeof(ach), "# %s analyze_enter %d", func, level); if( !mode_syntax_only() ) { gg_insert_into_assembler(ach); } } } ~ANALYZE() { ExitMessage(); } void ExitMessage() { if( getenv("Analyze") ) { char ach[128]; snprintf(ach, sizeof(ach), "# %s analyze_exit %d", func, level); if( !mode_syntax_only() ) { gg_insert_into_assembler(ach); } } } void Message(const char *msg) { if( getenv("Analyze") ) { char ach[128]; snprintf(ach, sizeof(ach), "# %s %s %d", func, msg, level); if( !mode_syntax_only() ) { gg_insert_into_assembler(ach); } } } }; #else class ANALYZE { public: ANALYZE(const char *) { } ~ANALYZE() { ExitMessage(); } void ExitMessage() { } void Message(const char *) { } }; #endif #define Analyze() ANALYZE Analyzer(__func__); #pragma GCC diagnostic pop #endif